PageRenderTime 60ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/libraries/fof/table/table.php

https://github.com/J2MTecnologia/joomla-3.x
PHP | 3651 lines | 2104 code | 570 blank | 977 comment | 300 complexity | 591d0bc6cd4c20e88f9389ecaf3daa05 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, BSD-3-Clause
  1. <?php
  2. /**
  3. * @package FrameworkOnFramework
  4. * @subpackage table
  5. * @copyright Copyright (C) 2010 - 2014 Akeeba Ltd. All rights reserved.
  6. * @license GNU General Public License version 2 or later; see LICENSE.txt
  7. */
  8. // Protect from unauthorized access
  9. defined('FOF_INCLUDED') or die;
  10. /**
  11. * Normally this shouldn't be required. Some PHP versions, however, seem to
  12. * require this. Why? No idea whatsoever. If I remove it, FOF crashes on some
  13. * hosts. Same PHP version on another host and no problem occurs. Any takers?
  14. */
  15. if (class_exists('FOFTable', false))
  16. {
  17. return;
  18. }
  19. if (!interface_exists('JTableInterface', true))
  20. {
  21. interface JTableInterface {}
  22. }
  23. /**
  24. * FrameworkOnFramework Table class. The Table is one part controller, one part
  25. * model and one part data adapter. It's supposed to handle operations for single
  26. * records.
  27. *
  28. * @package FrameworkOnFramework
  29. * @since 1.0
  30. */
  31. class FOFTable extends FOFUtilsObject implements JTableInterface
  32. {
  33. /**
  34. * Cache array for instances
  35. *
  36. * @var array
  37. */
  38. protected static $instances = array();
  39. /**
  40. * Include paths for searching for FOFTable classes.
  41. *
  42. * @var array
  43. */
  44. protected static $_includePaths = array();
  45. /**
  46. * The configuration parameters array
  47. *
  48. * @var array
  49. */
  50. protected $config = array();
  51. /**
  52. * Name of the database table to model.
  53. *
  54. * @var string
  55. */
  56. protected $_tbl = '';
  57. /**
  58. * Name of the primary key field in the table.
  59. *
  60. * @var string
  61. */
  62. protected $_tbl_key = '';
  63. /**
  64. * JDatabaseDriver object.
  65. *
  66. * @var JDatabaseDriver
  67. */
  68. protected $_db;
  69. /**
  70. * Should rows be tracked as ACL assets?
  71. *
  72. * @var boolean
  73. */
  74. protected $_trackAssets = false;
  75. /**
  76. * Does the resource support joomla tags?
  77. *
  78. * @var boolean
  79. */
  80. protected $_has_tags = false;
  81. /**
  82. * The rules associated with this record.
  83. *
  84. * @var JAccessRules A JAccessRules object.
  85. */
  86. protected $_rules;
  87. /**
  88. * Indicator that the tables have been locked.
  89. *
  90. * @var boolean
  91. */
  92. protected $_locked = false;
  93. /**
  94. * If this is set to true, it triggers automatically plugin events for
  95. * table actions
  96. *
  97. * @var boolean
  98. */
  99. protected $_trigger_events = false;
  100. /**
  101. * Table alias used in queries
  102. *
  103. * @var string
  104. */
  105. protected $_tableAlias = false;
  106. /**
  107. * Array with alias for "special" columns such as ordering, hits etc etc
  108. *
  109. * @var array
  110. */
  111. protected $_columnAlias = array();
  112. /**
  113. * If set to true, it enabled automatic checks on fields based on columns properties
  114. *
  115. * @var boolean
  116. */
  117. protected $_autoChecks = false;
  118. /**
  119. * Array with fields that should be skipped by automatic checks
  120. *
  121. * @var array
  122. */
  123. protected $_skipChecks = array();
  124. /**
  125. * Does the table actually exist? We need that to avoid PHP notices on
  126. * table-less views.
  127. *
  128. * @var boolean
  129. */
  130. protected $_tableExists = true;
  131. /**
  132. * The asset key for items in this table. It's usually something in the
  133. * com_example.viewname format. They asset name will be this key appended
  134. * with the item's ID, e.g. com_example.viewname.123
  135. *
  136. * @var string
  137. */
  138. protected $_assetKey = '';
  139. /**
  140. * The input data
  141. *
  142. * @var FOFInput
  143. */
  144. protected $input = null;
  145. /**
  146. * Extended query including joins with other tables
  147. *
  148. * @var JDatabaseQuery
  149. */
  150. protected $_queryJoin = null;
  151. /**
  152. * The prefix for the table class
  153. *
  154. * @var string
  155. */
  156. protected $_tablePrefix = '';
  157. /**
  158. * The known fields for this table
  159. *
  160. * @var array
  161. */
  162. protected $knownFields = array();
  163. /**
  164. * A list of table fields, keyed per table
  165. *
  166. * @var array
  167. */
  168. protected static $tableFieldCache = array();
  169. /**
  170. * A list of tables in the database
  171. *
  172. * @var array
  173. */
  174. protected static $tableCache = array();
  175. /**
  176. * An instance of FOFConfigProvider to provision configuration overrides
  177. *
  178. * @var FOFConfigProvider
  179. */
  180. protected $configProvider = null;
  181. /**
  182. * FOFTableDispatcherBehavior for dealing with extra behaviors
  183. *
  184. * @var FOFTableDispatcherBehavior
  185. */
  186. protected $tableDispatcher = null;
  187. /**
  188. * List of default behaviors to apply to the table
  189. *
  190. * @var array
  191. */
  192. protected $default_behaviors = array('tags', 'assets');
  193. /**
  194. * The relations object of the table. It's lazy-loaded by getRelations().
  195. *
  196. * @var FOFTableRelations
  197. */
  198. protected $_relations = null;
  199. /**
  200. * The configuration provider's key for this table, e.g. foobar.tables.bar for the #__foobar_bars table. This is set
  201. * automatically by the constructor
  202. *
  203. * @var string
  204. */
  205. protected $_configProviderKey = '';
  206. /**
  207. * Returns a static object instance of a particular table type
  208. *
  209. * @param string $type The table name
  210. * @param string $prefix The prefix of the table class
  211. * @param array $config Optional configuration variables
  212. *
  213. * @return FOFTable
  214. */
  215. public static function getInstance($type, $prefix = 'JTable', $config = array())
  216. {
  217. return self::getAnInstance($type, $prefix, $config);
  218. }
  219. /**
  220. * Returns a static object instance of a particular table type
  221. *
  222. * @param string $type The table name
  223. * @param string $prefix The prefix of the table class
  224. * @param array $config Optional configuration variables
  225. *
  226. * @return FOFTable
  227. */
  228. public static function &getAnInstance($type = null, $prefix = 'JTable', $config = array())
  229. {
  230. // Make sure $config is an array
  231. if (is_object($config))
  232. {
  233. $config = (array) $config;
  234. }
  235. elseif (!is_array($config))
  236. {
  237. $config = array();
  238. }
  239. // Guess the component name
  240. if (!array_key_exists('input', $config))
  241. {
  242. $config['input'] = new FOFInput;
  243. }
  244. if ($config['input'] instanceof FOFInput)
  245. {
  246. $tmpInput = $config['input'];
  247. }
  248. else
  249. {
  250. $tmpInput = new FOFInput($config['input']);
  251. }
  252. $option = $tmpInput->getCmd('option', '');
  253. $tmpInput->set('option', $option);
  254. $config['input'] = $tmpInput;
  255. if (!in_array($prefix, array('Table', 'JTable')))
  256. {
  257. preg_match('/(.*)Table$/', $prefix, $m);
  258. $option = 'com_' . strtolower($m[1]);
  259. }
  260. if (array_key_exists('option', $config))
  261. {
  262. $option = $config['option'];
  263. }
  264. $config['option'] = $option;
  265. if (!array_key_exists('view', $config))
  266. {
  267. $config['view'] = $config['input']->getCmd('view', 'cpanel');
  268. }
  269. if (is_null($type))
  270. {
  271. if ($prefix == 'JTable')
  272. {
  273. $prefix = 'Table';
  274. }
  275. $type = $config['view'];
  276. }
  277. $type = preg_replace('/[^A-Z0-9_\.-]/i', '', $type);
  278. $tableClass = $prefix . ucfirst($type);
  279. $config['_table_type'] = $type;
  280. $config['_table_class'] = $tableClass;
  281. $configProvider = new FOFConfigProvider;
  282. $configProviderKey = $option . '.views.' . FOFInflector::singularize($type) . '.config.';
  283. if (!array_key_exists($tableClass, self::$instances))
  284. {
  285. if (!class_exists($tableClass))
  286. {
  287. $componentPaths = FOFPlatform::getInstance()->getComponentBaseDirs($config['option']);
  288. $searchPaths = array(
  289. $componentPaths['main'] . '/tables',
  290. $componentPaths['admin'] . '/tables'
  291. );
  292. if (array_key_exists('tablepath', $config))
  293. {
  294. array_unshift($searchPaths, $config['tablepath']);
  295. }
  296. $altPath = $configProvider->get($configProviderKey . 'table_path', null);
  297. if ($altPath)
  298. {
  299. array_unshift($searchPaths, $componentPaths['admin'] . '/' . $altPath);
  300. }
  301. $filesystem = FOFPlatform::getInstance()->getIntegrationObject('filesystem');
  302. $path = $filesystem->pathFind(
  303. $searchPaths, strtolower($type) . '.php'
  304. );
  305. if ($path)
  306. {
  307. require_once $path;
  308. }
  309. }
  310. if (!class_exists($tableClass))
  311. {
  312. $tableClass = 'FOFTable';
  313. }
  314. $component = str_replace('com_', '', $config['option']);
  315. $tbl_common = $component . '_';
  316. if (!array_key_exists('tbl', $config))
  317. {
  318. $config['tbl'] = strtolower('#__' . $tbl_common . strtolower(FOFInflector::pluralize($type)));
  319. }
  320. $altTbl = $configProvider->get($configProviderKey . 'tbl', null);
  321. if ($altTbl)
  322. {
  323. $config['tbl'] = $altTbl;
  324. }
  325. if (!array_key_exists('tbl_key', $config))
  326. {
  327. $keyName = FOFInflector::singularize($type);
  328. $config['tbl_key'] = strtolower($tbl_common . $keyName . '_id');
  329. }
  330. $altTblKey = $configProvider->get($configProviderKey . 'tbl_key', null);
  331. if ($altTblKey)
  332. {
  333. $config['tbl_key'] = $altTblKey;
  334. }
  335. if (!array_key_exists('db', $config))
  336. {
  337. $config['db'] = FOFPlatform::getInstance()->getDbo();
  338. }
  339. // Assign the correct table alias
  340. if (array_key_exists('table_alias', $config))
  341. {
  342. $table_alias = $config['table_alias'];
  343. }
  344. else
  345. {
  346. $configProviderTableAliasKey = $option . '.tables.' . FOFInflector::singularize($type) . '.tablealias';
  347. $table_alias = $configProvider->get($configProviderTableAliasKey, false );
  348. }
  349. // Can we use the FOF cache?
  350. if (!array_key_exists('use_table_cache', $config))
  351. {
  352. $config['use_table_cache'] = FOFPlatform::getInstance()->isGlobalFOFCacheEnabled();
  353. }
  354. $alt_use_table_cache = $configProvider->get($configProviderKey . 'use_table_cache', null);
  355. if (!is_null($alt_use_table_cache))
  356. {
  357. $config['use_table_cache'] = $alt_use_table_cache;
  358. }
  359. // Create a new table instance
  360. $instance = new $tableClass($config['tbl'], $config['tbl_key'], $config['db'], $config);
  361. $instance->setInput($tmpInput);
  362. $instance->setTablePrefix($prefix);
  363. $instance->setTableAlias($table_alias);
  364. // Determine and set the asset key for this table
  365. $assetKey = 'com_' . $component . '.' . strtolower(FOFInflector::singularize($type));
  366. $assetKey = $configProvider->get($configProviderKey . 'asset_key', $assetKey);
  367. $instance->setAssetKey($assetKey);
  368. if (array_key_exists('trigger_events', $config))
  369. {
  370. $instance->setTriggerEvents($config['trigger_events']);
  371. }
  372. if (version_compare(JVERSION, '3.1', 'ge'))
  373. {
  374. if (array_key_exists('has_tags', $config))
  375. {
  376. $instance->setHasTags($config['has_tags']);
  377. }
  378. $altHasTags = $configProvider->get($configProviderKey . 'has_tags', null);
  379. if ($altHasTags)
  380. {
  381. $instance->setHasTags($altHasTags);
  382. }
  383. }
  384. else
  385. {
  386. $instance->setHasTags(false);
  387. }
  388. $configProviderFieldmapKey = $option . '.tables.' . FOFInflector::singularize($type) . '.field';
  389. $aliases = $configProvider->get($configProviderFieldmapKey, $instance->_columnAlias);
  390. $instance->_columnAlias = array_merge($instance->_columnAlias, $aliases);
  391. self::$instances[$tableClass] = $instance;
  392. }
  393. return self::$instances[$tableClass];
  394. }
  395. /**
  396. * Force an instance inside class cache. Setting arguments to null nukes all or part of the cache
  397. *
  398. * @param string|null $key TableClass to replace. Set it to null to nuke the entire cache
  399. * @param FOFTable|null $instance Instance to replace. Set it to null to nuke $key instances
  400. *
  401. * @return bool Did I correctly switch the instance?
  402. */
  403. public static function forceInstance($key = null, $instance = null)
  404. {
  405. if(is_null($key))
  406. {
  407. self::$instances = array();
  408. return true;
  409. }
  410. elseif($key && isset(self::$instances[$key]))
  411. {
  412. // I'm forcing an instance, but it's not a FOFTable, abort! abort!
  413. if(!$instance || ($instance && $instance instanceof FOFTable))
  414. {
  415. self::$instances[$key] = $instance;
  416. return true;
  417. }
  418. }
  419. return false;
  420. }
  421. /**
  422. * Class Constructor.
  423. *
  424. * @param string $table Name of the database table to model.
  425. * @param string $key Name of the primary key field in the table.
  426. * @param JDatabaseDriver &$db Database driver
  427. * @param array $config The configuration parameters array
  428. */
  429. public function __construct($table, $key, &$db, $config = array())
  430. {
  431. $this->_tbl = $table;
  432. $this->_tbl_key = $key;
  433. $this->_db = $db;
  434. // Make sure the use FOF cache information is in the config
  435. if (!array_key_exists('use_table_cache', $config))
  436. {
  437. $config['use_table_cache'] = FOFPlatform::getInstance()->isGlobalFOFCacheEnabled();
  438. }
  439. $this->config = $config;
  440. // Load the configuration provider
  441. $this->configProvider = new FOFConfigProvider;
  442. // Load the behavior dispatcher
  443. $this->tableDispatcher = new FOFTableDispatcherBehavior;
  444. // Initialise the table properties.
  445. if ($fields = $this->getTableFields())
  446. {
  447. // Do I have anything joined?
  448. $j_fields = $this->getQueryJoinFields();
  449. if ($j_fields)
  450. {
  451. $fields = array_merge($fields, $j_fields);
  452. }
  453. $this->setKnownFields(array_keys($fields), true);
  454. $this->reset();
  455. }
  456. else
  457. {
  458. $this->_tableExists = false;
  459. }
  460. // Get the input
  461. if (array_key_exists('input', $config))
  462. {
  463. if ($config['input'] instanceof FOFInput)
  464. {
  465. $this->input = $config['input'];
  466. }
  467. else
  468. {
  469. $this->input = new FOFInput($config['input']);
  470. }
  471. }
  472. else
  473. {
  474. $this->input = new FOFInput;
  475. }
  476. // Set the $name/$_name variable
  477. $component = $this->input->getCmd('option', 'com_foobar');
  478. if (array_key_exists('option', $config))
  479. {
  480. $component = $config['option'];
  481. }
  482. $this->input->set('option', $component);
  483. // Apply table behaviors
  484. $type = explode("_", $this->_tbl);
  485. $type = $type[count($type) - 1];
  486. $this->_configProviderKey = $component . '.tables.' . FOFInflector::singularize($type);
  487. $configKey = $this->_configProviderKey . '.behaviors';
  488. if (isset($config['behaviors']))
  489. {
  490. $behaviors = (array) $config['behaviors'];
  491. }
  492. elseif ($behaviors = $this->configProvider->get($configKey, null))
  493. {
  494. $behaviors = explode(',', $behaviors);
  495. }
  496. else
  497. {
  498. $behaviors = $this->default_behaviors;
  499. }
  500. if (is_array($behaviors) && count($behaviors))
  501. {
  502. foreach ($behaviors as $behavior)
  503. {
  504. $this->addBehavior($behavior);
  505. }
  506. }
  507. // If we are tracking assets, make sure an access field exists and initially set the default.
  508. $asset_id_field = $this->getColumnAlias('asset_id');
  509. $access_field = $this->getColumnAlias('access');
  510. if (in_array($asset_id_field, $this->getKnownFields()))
  511. {
  512. JLoader::import('joomla.access.rules');
  513. $this->_trackAssets = true;
  514. }
  515. // If the access property exists, set the default.
  516. if (in_array($access_field, $this->getKnownFields()))
  517. {
  518. $this->$access_field = (int) FOFPlatform::getInstance()->getConfig()->get('access');
  519. }
  520. $this->config = $config;
  521. }
  522. /**
  523. * Replace the entire known fields array
  524. *
  525. * @param array $fields A simple array of known field names
  526. * @param boolean $initialise Should we initialise variables to null?
  527. *
  528. * @return void
  529. */
  530. public function setKnownFields($fields, $initialise = false)
  531. {
  532. $this->knownFields = $fields;
  533. if ($initialise)
  534. {
  535. foreach ($this->knownFields as $field)
  536. {
  537. $this->$field = null;
  538. }
  539. }
  540. }
  541. /**
  542. * Get the known fields array
  543. *
  544. * @return array
  545. */
  546. public function getKnownFields()
  547. {
  548. return $this->knownFields;
  549. }
  550. /**
  551. * Add a field to the known fields array
  552. *
  553. * @param string $field The name of the field to add
  554. * @param boolean $initialise Should we initialise the variable to null?
  555. *
  556. * @return void
  557. */
  558. public function addKnownField($field, $initialise = false)
  559. {
  560. if (!in_array($field, $this->knownFields))
  561. {
  562. $this->knownFields[] = $field;
  563. if ($initialise)
  564. {
  565. $this->$field = null;
  566. }
  567. }
  568. }
  569. /**
  570. * Remove a field from the known fields array
  571. *
  572. * @param string $field The name of the field to remove
  573. *
  574. * @return void
  575. */
  576. public function removeKnownField($field)
  577. {
  578. if (in_array($field, $this->knownFields))
  579. {
  580. $pos = array_search($field, $this->knownFields);
  581. unset($this->knownFields[$pos]);
  582. }
  583. }
  584. /**
  585. * Adds a behavior to the table
  586. *
  587. * @param string $name The name of the behavior
  588. * @param array $config Optional Behavior configuration
  589. *
  590. * @return boolean
  591. */
  592. public function addBehavior($name, $config = array())
  593. {
  594. // First look for ComponentnameTableViewnameBehaviorName (e.g. FoobarTableItemsBehaviorTags)
  595. if (isset($this->config['option']))
  596. {
  597. $option_name = str_replace('com_', '', $this->config['option']);
  598. $behaviorClass = $this->config['_table_class'] . 'Behavior' . ucfirst(strtolower($name));
  599. if (class_exists($behaviorClass))
  600. {
  601. $behavior = new $behaviorClass($this->tableDispatcher, $config);
  602. return true;
  603. }
  604. // Then look for ComponentnameTableBehaviorName (e.g. FoobarTableBehaviorTags)
  605. $option_name = str_replace('com_', '', $this->config['option']);
  606. $behaviorClass = ucfirst($option_name) . 'TableBehavior' . ucfirst(strtolower($name));
  607. if (class_exists($behaviorClass))
  608. {
  609. $behavior = new $behaviorClass($this->tableDispatcher, $config);
  610. return true;
  611. }
  612. }
  613. // Nothing found? Return false.
  614. $behaviorClass = 'FOFTableBehavior' . ucfirst(strtolower($name));
  615. if (class_exists($behaviorClass) && $this->tableDispatcher)
  616. {
  617. $behavior = new $behaviorClass($this->tableDispatcher, $config);
  618. return true;
  619. }
  620. return false;
  621. }
  622. /**
  623. * Sets the events trigger switch state
  624. *
  625. * @param boolean $newState The new state of the switch (what else could it be?)
  626. *
  627. * @return void
  628. */
  629. public function setTriggerEvents($newState = false)
  630. {
  631. $this->_trigger_events = $newState ? true : false;
  632. }
  633. /**
  634. * Gets the events trigger switch state
  635. *
  636. * @return boolean
  637. */
  638. public function getTriggerEvents()
  639. {
  640. return $this->_trigger_events;
  641. }
  642. /**
  643. * Gets the has tags switch state
  644. *
  645. * @return bool
  646. */
  647. public function hasTags()
  648. {
  649. return $this->_has_tags;
  650. }
  651. /**
  652. * Sets the has tags switch state
  653. *
  654. * @param bool $newState
  655. */
  656. public function setHasTags($newState = false)
  657. {
  658. $this->_has_tags = false;
  659. // Tags are available only in 3.1+
  660. if (version_compare(JVERSION, '3.1', 'ge'))
  661. {
  662. $this->_has_tags = $newState ? true : false;
  663. }
  664. }
  665. /**
  666. * Set the class prefix
  667. *
  668. * @param string $prefix The prefix
  669. */
  670. public function setTablePrefix($prefix)
  671. {
  672. $this->_tablePrefix = $prefix;
  673. }
  674. /**
  675. * Sets fields to be skipped from automatic checks.
  676. *
  677. * @param array/string $skip Fields to be skipped by automatic checks
  678. *
  679. * @return void
  680. */
  681. public function setSkipChecks($skip)
  682. {
  683. $this->_skipChecks = (array) $skip;
  684. }
  685. /**
  686. * Method to load a row from the database by primary key and bind the fields
  687. * to the FOFTable instance properties.
  688. *
  689. * @param mixed $keys An optional primary key value to load the row by, or an array of fields to match. If not
  690. * set the instance property value is used.
  691. * @param boolean $reset True to reset the default values before loading the new row.
  692. *
  693. * @return boolean True if successful. False if row not found.
  694. *
  695. * @throws RuntimeException
  696. * @throws UnexpectedValueException
  697. */
  698. public function load($keys = null, $reset = true)
  699. {
  700. if (!$this->_tableExists)
  701. {
  702. $result = false;
  703. return $this->onAfterLoad($result);
  704. }
  705. if (empty($keys))
  706. {
  707. // If empty, use the value of the current key
  708. $keyName = $this->_tbl_key;
  709. if (isset($this->$keyName))
  710. {
  711. $keyValue = $this->$keyName;
  712. }
  713. else
  714. {
  715. $keyValue = null;
  716. }
  717. // If empty primary key there's is no need to load anything
  718. if (empty($keyValue))
  719. {
  720. $result = true;
  721. return $this->onAfterLoad($result);
  722. }
  723. $keys = array($keyName => $keyValue);
  724. }
  725. elseif (!is_array($keys))
  726. {
  727. // Load by primary key.
  728. $keys = array($this->_tbl_key => $keys);
  729. }
  730. if ($reset)
  731. {
  732. $this->reset();
  733. }
  734. // Initialise the query.
  735. $query = $this->_db->getQuery(true);
  736. $query->select($this->_tbl . '.*');
  737. $query->from($this->_tbl);
  738. // Joined fields are ok, since I initialized them in the constructor
  739. $fields = $this->getKnownFields();
  740. foreach ($keys as $field => $value)
  741. {
  742. // Check that $field is in the table.
  743. if (!in_array($field, $fields))
  744. {
  745. throw new UnexpectedValueException(sprintf('Missing field in table %s : %s.', $this->_tbl, $field));
  746. }
  747. // Add the search tuple to the query.
  748. $query->where($this->_db->qn($this->_tbl . '.' . $field) . ' = ' . $this->_db->q($value));
  749. }
  750. // Do I have any joined table?
  751. $j_query = $this->getQueryJoin();
  752. if ($j_query)
  753. {
  754. if ($j_query->select && $j_query->select->getElements())
  755. {
  756. //$query->select($this->normalizeSelectFields($j_query->select->getElements(), true));
  757. $query->select($j_query->select->getElements());
  758. }
  759. if ($j_query->join)
  760. {
  761. foreach ($j_query->join as $join)
  762. {
  763. $t = (string) $join;
  764. // Joomla doesn't provide any access to the "name" variable, so I have to work with strings...
  765. if (stripos($t, 'inner') !== false)
  766. {
  767. $query->innerJoin($join->getElements());
  768. }
  769. elseif (stripos($t, 'left') !== false)
  770. {
  771. $query->leftJoin($join->getElements());
  772. }
  773. elseif (stripos($t, 'right') !== false)
  774. {
  775. $query->rightJoin($join->getElements());
  776. }
  777. elseif (stripos($t, 'outer') !== false)
  778. {
  779. $query->outerJoin($join->getElements());
  780. }
  781. }
  782. }
  783. }
  784. $this->_db->setQuery($query);
  785. $row = $this->_db->loadAssoc();
  786. // Check that we have a result.
  787. if (empty($row))
  788. {
  789. $result = false;
  790. return $this->onAfterLoad($result);
  791. }
  792. // Bind the object with the row and return.
  793. $result = $this->bind($row);
  794. $this->onAfterLoad($result);
  795. return $result;
  796. }
  797. /**
  798. * Based on fields properties (nullable column), checks if the field is required or not
  799. *
  800. * @return boolean
  801. */
  802. public function check()
  803. {
  804. if (!$this->_autoChecks)
  805. {
  806. return true;
  807. }
  808. $fields = $this->getTableFields();
  809. // No fields? Why in the hell am I here?
  810. if(!$fields)
  811. {
  812. return false;
  813. }
  814. $result = true;
  815. $known = $this->getKnownFields();
  816. $skipFields[] = $this->_tbl_key;
  817. if(in_array($this->getColumnAlias('hits'), $known)) $skipFields[] = $this->getColumnAlias('hits');
  818. if(in_array($this->getColumnAlias('created_on'), $known)) $skipFields[] = $this->getColumnAlias('created_on');
  819. if(in_array($this->getColumnAlias('created_by'), $known)) $skipFields[] = $this->getColumnAlias('created_by');
  820. if(in_array($this->getColumnAlias('modified_on'), $known)) $skipFields[] = $this->getColumnAlias('modified_on');
  821. if(in_array($this->getColumnAlias('modified_by'), $known)) $skipFields[] = $this->getColumnAlias('modified_by');
  822. if(in_array($this->getColumnAlias('locked_by'), $known)) $skipFields[] = $this->getColumnAlias('locked_by');
  823. if(in_array($this->getColumnAlias('locked_on'), $known)) $skipFields[] = $this->getColumnAlias('locked_on');
  824. // Let's merge it with custom skips
  825. $skipFields = array_merge($skipFields, $this->_skipChecks);
  826. foreach ($fields as $field)
  827. {
  828. $fieldName = $field->Field;
  829. if (empty($fieldName))
  830. {
  831. $fieldName = $field->column_name;
  832. }
  833. // Field is not nullable but it's null, set error
  834. if ($field->Null == 'NO' && $this->$fieldName == '' && !in_array($fieldName, $skipFields))
  835. {
  836. $text = str_replace('#__', 'COM_', $this->getTableName()) . '_ERR_' . $fieldName;
  837. $this->setError(JText::_(strtoupper($text)));
  838. $result = false;
  839. }
  840. }
  841. return $result;
  842. }
  843. /**
  844. * Method to reset class properties to the defaults set in the class
  845. * definition. It will ignore the primary key as well as any private class
  846. * properties.
  847. *
  848. * @return void
  849. */
  850. public function reset()
  851. {
  852. if (!$this->onBeforeReset())
  853. {
  854. return false;
  855. }
  856. // Get the default values for the class from the table.
  857. $fields = $this->getTableFields();
  858. $j_fields = $this->getQueryJoinFields();
  859. if ($j_fields)
  860. {
  861. $fields = array_merge($fields, $j_fields);
  862. }
  863. foreach ($fields as $k => $v)
  864. {
  865. // If the property is not the primary key or private, reset it.
  866. if ($k != $this->_tbl_key && (strpos($k, '_') !== 0))
  867. {
  868. $this->$k = $v->Default;
  869. }
  870. }
  871. if (!$this->onAfterReset())
  872. {
  873. return false;
  874. }
  875. }
  876. /**
  877. * Clones the current object, after resetting it
  878. *
  879. * @return FOFTable
  880. */
  881. public function getClone()
  882. {
  883. $clone = clone $this;
  884. $clone->reset();
  885. $key = $this->getKeyName();
  886. $clone->$key = null;
  887. return $clone;
  888. }
  889. /**
  890. * Generic check for whether dependencies exist for this object in the db schema
  891. *
  892. * @param integer $oid The primary key of the record to delete
  893. * @param array $joins Any joins to foreign table, used to determine if dependent records exist
  894. *
  895. * @return boolean True if the record can be deleted
  896. */
  897. public function canDelete($oid = null, $joins = null)
  898. {
  899. $k = $this->_tbl_key;
  900. if ($oid)
  901. {
  902. $this->$k = intval($oid);
  903. }
  904. if (is_array($joins))
  905. {
  906. $db = $this->_db;
  907. $query = $db->getQuery(true)
  908. ->select($db->qn('master') . '.' . $db->qn($k))
  909. ->from($db->qn($this->_tbl) . ' AS ' . $db->qn('master'));
  910. $tableNo = 0;
  911. foreach ($joins as $table)
  912. {
  913. $tableNo++;
  914. $query->select(
  915. array(
  916. 'COUNT(DISTINCT ' . $db->qn('t' . $tableNo) . '.' . $db->qn($table['idfield']) . ') AS ' . $db->qn($table['idalias'])
  917. )
  918. );
  919. $query->join('LEFT', $db->qn($table['name']) .
  920. ' AS ' . $db->qn('t' . $tableNo) .
  921. ' ON ' . $db->qn('t' . $tableNo) . '.' . $db->qn($table['joinfield']) .
  922. ' = ' . $db->qn('master') . '.' . $db->qn($k)
  923. );
  924. }
  925. $query->where($db->qn('master') . '.' . $db->qn($k) . ' = ' . $db->q($this->$k));
  926. $query->group($db->qn('master') . '.' . $db->qn($k));
  927. $this->_db->setQuery((string) $query);
  928. if (version_compare(JVERSION, '3.0', 'ge'))
  929. {
  930. try
  931. {
  932. $obj = $this->_db->loadObject();
  933. }
  934. catch (JDatabaseException $e)
  935. {
  936. $this->setError($e->getMessage());
  937. }
  938. }
  939. else
  940. {
  941. if (!$obj = $this->_db->loadObject())
  942. {
  943. $this->setError($this->_db->getErrorMsg());
  944. return false;
  945. }
  946. }
  947. $msg = array();
  948. $i = 0;
  949. foreach ($joins as $table)
  950. {
  951. $k = $table['idalias'];
  952. if ($obj->$k > 0)
  953. {
  954. $msg[] = JText::_($table['label']);
  955. }
  956. $i++;
  957. }
  958. if (count($msg))
  959. {
  960. $option = $this->input->getCmd('option', 'com_foobar');
  961. $comName = str_replace('com_', '', $option);
  962. $tview = str_replace('#__' . $comName . '_', '', $this->_tbl);
  963. $prefix = $option . '_' . $tview . '_NODELETE_';
  964. foreach ($msg as $key)
  965. {
  966. $this->setError(JText::_($prefix . $key));
  967. }
  968. return false;
  969. }
  970. else
  971. {
  972. return true;
  973. }
  974. }
  975. return true;
  976. }
  977. /**
  978. * Method to bind an associative array or object to the FOFTable instance.This
  979. * method only binds properties that are publicly accessible and optionally
  980. * takes an array of properties to ignore when binding.
  981. *
  982. * @param mixed $src An associative array or object to bind to the FOFTable instance.
  983. * @param mixed $ignore An optional array or space separated list of properties to ignore while binding.
  984. *
  985. * @return boolean True on success.
  986. *
  987. * @throws InvalidArgumentException
  988. */
  989. public function bind($src, $ignore = array())
  990. {
  991. if (!$this->onBeforeBind($src))
  992. {
  993. return false;
  994. }
  995. // If the source value is not an array or object return false.
  996. if (!is_object($src) && !is_array($src))
  997. {
  998. throw new InvalidArgumentException(sprintf('%s::bind(*%s*)', get_class($this), gettype($src)));
  999. }
  1000. // If the source value is an object, get its accessible properties.
  1001. if (is_object($src))
  1002. {
  1003. $src = get_object_vars($src);
  1004. }
  1005. // If the ignore value is a string, explode it over spaces.
  1006. if (!is_array($ignore))
  1007. {
  1008. $ignore = explode(' ', $ignore);
  1009. }
  1010. // Bind the source value, excluding the ignored fields.
  1011. foreach ($this->getKnownFields() as $k)
  1012. {
  1013. // Only process fields not in the ignore array.
  1014. if (!in_array($k, $ignore))
  1015. {
  1016. if (isset($src[$k]))
  1017. {
  1018. $this->$k = $src[$k];
  1019. }
  1020. }
  1021. }
  1022. $result = $this->onAfterBind($src);
  1023. return $result;
  1024. }
  1025. /**
  1026. * Method to store a row in the database from the FOFTable instance properties.
  1027. * If a primary key value is set the row with that primary key value will be
  1028. * updated with the instance property values. If no primary key value is set
  1029. * a new row will be inserted into the database with the properties from the
  1030. * FOFTable instance.
  1031. *
  1032. * @param boolean $updateNulls True to update fields even if they are null.
  1033. *
  1034. * @return boolean True on success.
  1035. */
  1036. public function store($updateNulls = false)
  1037. {
  1038. if (!$this->onBeforeStore($updateNulls))
  1039. {
  1040. return false;
  1041. }
  1042. $k = $this->_tbl_key;
  1043. if ($this->$k == 0)
  1044. {
  1045. $this->$k = null;
  1046. }
  1047. // Create the object used for inserting/updating data to the database
  1048. $fields = $this->getTableFields();
  1049. $properties = $this->getKnownFields();
  1050. $keys = array();
  1051. foreach ($properties as $property)
  1052. {
  1053. // 'input' property is a reserved name
  1054. if (isset($fields[$property]))
  1055. {
  1056. $keys[] = $property;
  1057. }
  1058. }
  1059. $updateObject = array();
  1060. foreach ($keys as $key)
  1061. {
  1062. $updateObject[$key] = $this->$key;
  1063. }
  1064. $updateObject = (object)$updateObject;
  1065. // If a primary key exists update the object, otherwise insert it.
  1066. if ($this->$k)
  1067. {
  1068. $result = $this->_db->updateObject($this->_tbl, $updateObject, $this->_tbl_key, $updateNulls);
  1069. }
  1070. else
  1071. {
  1072. $result = $this->_db->insertObject($this->_tbl, $updateObject, $this->_tbl_key);
  1073. }
  1074. if ($result !== true)
  1075. {
  1076. $this->setError($this->_db->getErrorMsg());
  1077. return false;
  1078. }
  1079. $this->bind($updateObject);
  1080. if ($this->_locked)
  1081. {
  1082. $this->_unlock();
  1083. }
  1084. $result = $this->onAfterStore();
  1085. return $result;
  1086. }
  1087. /**
  1088. * Method to move a row in the ordering sequence of a group of rows defined by an SQL WHERE clause.
  1089. * Negative numbers move the row up in the sequence and positive numbers move it down.
  1090. *
  1091. * @param integer $delta The direction and magnitude to move the row in the ordering sequence.
  1092. * @param string $where WHERE clause to use for limiting the selection of rows to compact the
  1093. * ordering values.
  1094. *
  1095. * @return mixed Boolean True on success.
  1096. *
  1097. * @throws UnexpectedValueException
  1098. */
  1099. public function move($delta, $where = '')
  1100. {
  1101. if (!$this->onBeforeMove($delta, $where))
  1102. {
  1103. return false;
  1104. }
  1105. // If there is no ordering field set an error and return false.
  1106. $ordering_field = $this->getColumnAlias('ordering');
  1107. if (!in_array($ordering_field, $this->getKnownFields()))
  1108. {
  1109. throw new UnexpectedValueException(sprintf('%s does not support ordering.', $this->_tbl));
  1110. }
  1111. // If the change is none, do nothing.
  1112. if (empty($delta))
  1113. {
  1114. $result = $this->onAfterMove();
  1115. return $result;
  1116. }
  1117. $k = $this->_tbl_key;
  1118. $row = null;
  1119. $query = $this->_db->getQuery(true);
  1120. // If the table is not loaded, return false
  1121. if (empty($this->$k))
  1122. {
  1123. return false;
  1124. }
  1125. // Select the primary key and ordering values from the table.
  1126. $query->select(array($this->_db->qn($this->_tbl_key), $this->_db->qn($ordering_field)));
  1127. $query->from($this->_tbl);
  1128. // If the movement delta is negative move the row up.
  1129. if ($delta < 0)
  1130. {
  1131. $query->where($this->_db->qn($ordering_field) . ' < ' . $this->_db->q((int) $this->$ordering_field));
  1132. $query->order($this->_db->qn($ordering_field) . ' DESC');
  1133. }
  1134. // If the movement delta is positive move the row down.
  1135. elseif ($delta > 0)
  1136. {
  1137. $query->where($this->_db->qn($ordering_field) . ' > ' . $this->_db->q((int) $this->$ordering_field));
  1138. $query->order($this->_db->qn($ordering_field) . ' ASC');
  1139. }
  1140. // Add the custom WHERE clause if set.
  1141. if ($where)
  1142. {
  1143. $query->where($where);
  1144. }
  1145. // Select the first row with the criteria.
  1146. $this->_db->setQuery($query, 0, 1);
  1147. $row = $this->_db->loadObject();
  1148. // If a row is found, move the item.
  1149. if (!empty($row))
  1150. {
  1151. // Update the ordering field for this instance to the row's ordering value.
  1152. $query = $this->_db->getQuery(true);
  1153. $query->update($this->_tbl);
  1154. $query->set($this->_db->qn($ordering_field) . ' = ' . $this->_db->q((int) $row->$ordering_field));
  1155. $query->where($this->_tbl_key . ' = ' . $this->_db->q($this->$k));
  1156. $this->_db->setQuery($query);
  1157. $this->_db->execute();
  1158. // Update the ordering field for the row to this instance's ordering value.
  1159. $query = $this->_db->getQuery(true);
  1160. $query->update($this->_tbl);
  1161. $query->set($this->_db->qn($ordering_field) . ' = ' . $this->_db->q((int) $this->$ordering_field));
  1162. $query->where($this->_tbl_key . ' = ' . $this->_db->q($row->$k));
  1163. $this->_db->setQuery($query);
  1164. $this->_db->execute();
  1165. // Update the instance value.
  1166. $this->$ordering_field = $row->$ordering_field;
  1167. }
  1168. else
  1169. {
  1170. // Update the ordering field for this instance.
  1171. $query = $this->_db->getQuery(true);
  1172. $query->update($this->_tbl);
  1173. $query->set($this->_db->qn($ordering_field) . ' = ' . $this->_db->q((int) $this->$ordering_field));
  1174. $query->where($this->_tbl_key . ' = ' . $this->_db->q($this->$k));
  1175. $this->_db->setQuery($query);
  1176. $this->_db->execute();
  1177. }
  1178. $result = $this->onAfterMove();
  1179. return $result;
  1180. }
  1181. /**
  1182. * Change the ordering of the records of the table
  1183. *
  1184. * @param string $where The WHERE clause of the SQL used to fetch the order
  1185. *
  1186. * @return boolean True is successful
  1187. *
  1188. * @throws UnexpectedValueException
  1189. */
  1190. public function reorder($where = '')
  1191. {
  1192. if (!$this->onBeforeReorder($where))
  1193. {
  1194. return false;
  1195. }
  1196. // If there is no ordering field set an error and return false.
  1197. $order_field = $this->getColumnAlias('ordering');
  1198. if (!in_array($order_field, $this->getKnownFields()))
  1199. {
  1200. throw new UnexpectedValueException(sprintf('%s does not support ordering.', $this->_tbl_key));
  1201. }
  1202. $k = $this->_tbl_key;
  1203. // Get the primary keys and ordering values for the selection.
  1204. $query = $this->_db->getQuery(true);
  1205. $query->select($this->_tbl_key . ', ' . $this->_db->qn($order_field));
  1206. $query->from($this->_tbl);
  1207. $query->where($this->_db->qn($order_field) . ' >= ' . $this->_db->q(0));
  1208. $query->order($this->_db->qn($order_field));
  1209. // Setup the extra where and ordering clause data.
  1210. if ($where)
  1211. {
  1212. $query->where($where);
  1213. }
  1214. $this->_db->setQuery($query);
  1215. $rows = $this->_db->loadObjectList();
  1216. // Compact the ordering values.
  1217. foreach ($rows as $i => $row)
  1218. {
  1219. // Make sure the ordering is a positive integer.
  1220. if ($row->$order_field >= 0)
  1221. {
  1222. // Only update rows that are necessary.
  1223. if ($row->$order_field != $i + 1)
  1224. {
  1225. // Update the row ordering field.
  1226. $query = $this->_db->getQuery(true);
  1227. $query->update($this->_tbl);
  1228. $query->set($this->_db->qn($order_field) . ' = ' . $this->_db->q($i + 1));
  1229. $query->where($this->_tbl_key . ' = ' . $this->_db->q($row->$k));
  1230. $this->_db->setQuery($query);
  1231. $this->_db->execute();
  1232. }
  1233. }
  1234. }
  1235. $result = $this->onAfterReorder();
  1236. return $result;
  1237. }
  1238. /**
  1239. * Check out (lock) a record
  1240. *
  1241. * @param integer $userId The locking user's ID
  1242. * @param integer $oid The primary key value of the record to lock
  1243. *
  1244. * @return boolean True on success
  1245. */
  1246. public function checkout($userId, $oid = null)
  1247. {
  1248. $fldLockedBy = $this->getColumnAlias('locked_by');
  1249. $fldLockedOn = $this->getColumnAlias('locked_on');
  1250. if (!(in_array($fldLockedBy, $this->getKnownFields())
  1251. || in_array($fldLockedOn, $this->getKnownFields())))
  1252. {
  1253. return true;
  1254. }
  1255. $k = $this->_tbl_key;
  1256. if ($oid !== null)
  1257. {
  1258. $this->$k = $oid;
  1259. }
  1260. // No primary key defined, stop here
  1261. if (!$this->$k)
  1262. {
  1263. return false;
  1264. }
  1265. $date = FOFPlatform::getInstance()->getDate();
  1266. $time = $date->toSql();
  1267. $query = $this->_db->getQuery(true)
  1268. ->update($this->_db->qn($this->_tbl))
  1269. ->set(
  1270. array(
  1271. $this->_db->qn($fldLockedBy) . ' = ' . $this->_db->q((int) $userId),
  1272. $this->_db->qn($fldLockedOn) . ' = ' . $this->_db->q($time)
  1273. )
  1274. )
  1275. ->where($this->_db->qn($this->_tbl_key) . ' = ' . $this->_db->q($this->$k));
  1276. $this->_db->setQuery((string) $query);
  1277. $this->$fldLockedBy = $userId;
  1278. $this->$fldLockedOn = $time;
  1279. return $this->_db->execute();
  1280. }
  1281. /**
  1282. * Check in (unlock) a record
  1283. *
  1284. * @param integer $oid The primary key value of the record to unlock
  1285. *
  1286. * @return boolean True on success
  1287. */
  1288. public function checkin($oid = null)
  1289. {
  1290. $fldLockedBy = $this->getColumnAlias('locked_by');
  1291. $fldLockedOn = $this->getColumnAlias('locked_on');
  1292. if (!(in_array($fldLockedBy, $this->getKnownFields())
  1293. || in_array($fldLockedOn, $this->getKnownFields())))
  1294. {
  1295. return true;
  1296. }
  1297. $k = $this->_tbl_key;
  1298. if ($oid !== null)
  1299. {
  1300. $this->$k = $oid;
  1301. }
  1302. if ($this->$k == null)
  1303. {
  1304. return false;
  1305. }
  1306. $query = $this->_db->getQuery(true)
  1307. ->update($this->_db->qn($this->_tbl))
  1308. ->set(
  1309. array(
  1310. $this->_db->qn($fldLockedBy) . ' = 0',
  1311. $this->_db->qn($fldLockedOn) . ' = ' . $this->_db->q($this->_db->getNullDate())
  1312. )
  1313. )
  1314. ->where($this->_db->qn($this->_tbl_key) . ' = ' . $this->_db->q($this->$k));
  1315. $this->_db->setQuery((string) $query);
  1316. $this->$fldLockedBy = 0;
  1317. $this->$fldLockedOn = '';
  1318. return $this->_db->execute();
  1319. }
  1320. /**
  1321. * Is a record locked?
  1322. *
  1323. * @param integer $with The userid to preform the match with. If an item is checked
  1324. * out by this user the function will return false.
  1325. * @param integer $unused_against Junk inherited from JTable; ignore
  1326. *
  1327. * @throws UnexpectedValueException
  1328. *
  1329. * @return boolean True if the record is locked by another user
  1330. */
  1331. public function isCheckedOut($with = 0, $unused_against = null)
  1332. {
  1333. $against = null;
  1334. $fldLockedBy = $this->getColumnAlias('locked_by');
  1335. $k = $this->_tbl_key;
  1336. // If no primary key is given, return false.
  1337. if ($this->$k === null)
  1338. {
  1339. throw new UnexpectedValueException('Null primary key not allowed.');
  1340. }
  1341. if (isset($this) && is_a($this, 'FOFTable') && !$against)
  1342. {
  1343. $against = $this->get($fldLockedBy);
  1344. }
  1345. // Item is not checked out, or being checked out by the same user
  1346. if (!$against || $against == $with)
  1347. {
  1348. return false;
  1349. }
  1350. $session = JTable::getInstance('session');
  1351. return $session->exists($against);
  1352. }
  1353. /**
  1354. * Copy (duplicate) one or more records
  1355. *
  1356. * @param integer|array $cid The primary key value (or values) or the record(s) to copy
  1357. *
  1358. * @return boolean True on success
  1359. */
  1360. public function copy($cid = null)
  1361. {
  1362. //We have to cast the id as array, or the helper function will return an empty set
  1363. if($cid)
  1364. {
  1365. $cid = (array) $cid;
  1366. }
  1367. FOFUtilsArray::toInteger($cid);
  1368. $k = $this->_tbl_key;
  1369. if (count($cid) < 1)
  1370. {
  1371. if ($this->$k)
  1372. {
  1373. $cid = array($this->$k);
  1374. }
  1375. else
  1376. {
  1377. $this->setError("No items selected.");
  1378. return false;
  1379. }
  1380. }
  1381. $created_by = $this->getColumnAlias('created_by');
  1382. $created_on = $this->getColumnAlias('created_on');
  1383. $modified_by = $this->getColumnAlias('modified_by');
  1384. $modified_on = $this->getColumnAlias('modified_on');
  1385. $locked_byName = $this->getColumnAlias('locked_by');
  1386. $checkin = in_array($locked_byName, $this->getKnownFields());
  1387. foreach ($cid as $item)
  1388. {
  1389. // Prevent load with id = 0
  1390. if (!$item)
  1391. {
  1392. continue;
  1393. }
  1394. $this->load($item);
  1395. if ($checkin)
  1396. {
  1397. // We're using the checkin and the record is used by someone else
  1398. if ($this->isCheckedOut($item))
  1399. {
  1400. continue;
  1401. }
  1402. }
  1403. // TODO Should we notify the user that we had a problem with this record?
  1404. if (!$this->onBeforeCopy($item))
  1405. {
  1406. continue;
  1407. }
  1408. $this->$k = null;
  1409. $this->$created_by = null;
  1410. $this->$created_on = null;
  1411. $this->$modified_on = null;
  1412. $this->$modified_by = null;
  1413. // Let's fire the event only if everything is ok
  1414. // TODO Should we notify the user that we had a problem with this record?
  1415. if ($this->store())
  1416. {
  1417. // TODO Should we notify the user that we had a problem with this record?
  1418. $this->onAfterCopy($item);
  1419. }
  1420. $this->reset();
  1421. }
  1422. return true;
  1423. }
  1424. /**
  1425. * Publish or unpublish records
  1426. *
  1427. * @param integer|array $cid The primary key value(s) of the item(s) to publish/unpublish
  1428. * @param integer $publish 1 to publish an item, 0 to unpublish
  1429. * @param integer $user_id The user ID of the user (un)publishing the item.
  1430. *
  1431. * @return boolean True on success, false on failure (e.g. record is locked)
  1432. */
  1433. public function publish($cid = null, $publish = 1, $user_id = 0)
  1434. {
  1435. $enabledName = $this->getColumnAlias('enabled');
  1436. $locked_byName = $this->getColumnAlias('locked_by');
  1437. // Mhm... you called the publish method on a table without publish support...
  1438. if(!in_array($enabledName, $this->getKnownFields()))
  1439. {
  1440. return false;
  1441. }
  1442. //We have to cast the id as array, or the helper function will return an empty set
  1443. if($cid)
  1444. {
  1445. $cid = (array) $cid;
  1446. }
  1447. FOFUtilsArray::toInteger($cid);
  1448. $user_id = (int) $user_id;
  1449. $publish = (int) $publish;
  1450. $k = $this->_tbl_key;
  1451. if (count($cid) < 1)
  1452. {
  1453. if ($this->$k)
  1454. {
  1455. $cid = array($this->$k);
  1456. }
  1457. else
  1458. {
  1459. $this->setError("No items selected.");
  1460. return false;
  1461. }
  1462. }
  1463. if (!$this->onBeforePublish($cid, $publish))
  1464. {
  1465. return false;
  1466. }
  1467. $query = $this->_db->getQuery(true)
  1468. ->update($this->_db->qn($this->_tbl))
  1469. ->set($this->_db->qn($enabledName) . ' = ' . (int) $publish);
  1470. $checkin = in_array($locked_byName, $this->getKnownFields());
  1471. if ($checkin)
  1472. {
  1473. $query->where(
  1474. ' (' . $this->_db->qn($locked_byName) .
  1475. ' = 0 OR ' . $this->_db->qn($locked_byName) . ' = ' . (int) $user_id . ')', 'AND'
  1476. );
  1477. }
  1478. //Why this crazy statement?
  1479. // TODO Rewrite this statment using IN. Check if it work in SQLServer and PostgreSQL
  1480. $cids = $this->_db->qn($k) . ' = ' . implode(' OR ' . $this->_db->qn($k) . ' = ', $cid);
  1481. $query->where('(' . $cids . ')');
  1482. $this->_db->setQuery((string) $query);
  1483. if (version_compare(JVERSION, '3.0', 'ge'))
  1484. {
  1485. try
  1486. {
  1487. $this->_db->execute();
  1488. }
  1489. catch (JDatabaseException $e)
  1490. {
  1491. $this->setError($e->getMessage());
  1492. }
  1493. }
  1494. else
  1495. {
  1496. if (!$this->_db->execute())
  1497. {
  1498. $this->setError($this->_db->getErrorMsg());
  1499. return false;
  1500. }
  1501. }
  1502. if (count($cid) == 1 && $checkin)
  1503. {
  1504. if ($this->_db->getAffectedRows() == 1)
  1505. {
  1506. // TODO should we check for its return value?
  1507. $this->checkin($cid[0]);
  1508. if ($this->$k == $cid[0])
  1509. {
  1510. $this->$enabledName = $publish;
  1511. }
  1512. }
  1513. }
  1514. $this->setError('');
  1515. return true;
  1516. }
  1517. /**
  1518. * Delete a record
  1519. *
  1520. * @param integer $oid The primary key value of the item to delete
  1521. *
  1522. * @throws UnexpectedValueException
  1523. *
  1524. * @return boolean True on success
  1525. */
  1526. public function delete($oid = null)
  1527. {
  1528. if ($oid)
  1529. {
  1530. $this->load($oid);
  1531. }
  1532. $k = $this->_tbl_key;
  1533. $pk = (!$oid) ? $this->$k : $oid;
  1534. // If no primary key is given, return false.
  1535. if (!$pk)
  1536. {
  1537. throw new UnexpectedValueException('Null primary key not allowed.');
  1538. }
  1539. // Execute the logic only if I have a primary key, otherwise I could have weird results
  1540. if (!$this->onBeforeDelete($oid))
  1541. {
  1542. return false;
  1543. }
  1544. // Delete the row by primary key.
  1545. $query = $this->_db->getQuery(true);
  1546. $query->delete();
  1547. $query->from($this->_tbl);
  1548. $query->where($this->_tbl_key . ' = ' . $this->_db->q($pk));
  1549. $this->_db->setQuery($query);
  1550. // @TODO Check for a database error.
  1551. $this->_db->execute();
  1552. $result = $this->onAfterDelete($oid);
  1553. return $result;
  1554. }
  1555. /**
  1556. * Register a hit on a record
  1557. *
  1558. * @param integer $oid The primary key value of the record
  1559. * @param boolean $log Should I log the hit?
  1560. *
  1561. * @return boolean True on success
  1562. */
  1563. public function hit($oid = null, $log = false)
  1564. {
  1565. if (!$this->onBeforeHit($oid, $log))
  1566. {
  1567. return false;
  1568. }
  1569. // If there is no hits field, just return true.
  1570. $hits_field = $this->getColumnAlias('hits');
  1571. if (!in_array($hits_field, $this->getKnownFields()))
  1572. {
  1573. return true;
  1574. }
  1575. $k = $this->_tbl_key;
  1576. $pk = ($oid) ? $oid : $this->$k;
  1577. // If no primary key is given, return false.
  1578. if (!$pk)
  1579. {
  1580. $result = false;
  1581. }
  1582. else
  1583. {
  1584. // Check the row in by primary key.
  1585. $query = $this->_db->getQuery(true)
  1586. ->update($this->_tbl)
  1587. ->set($this->_db->qn($hits_field) . ' = (' . $this->_db->qn($hits_field) . ' + 1)')
  1588. ->where($this->_tbl_key . ' = ' . $this->_db->q($pk));
  1589. $this->_db->setQuery($query)->execute();
  1590. // In order to update the table object, I have to load the table
  1591. if(!$this->$k)
  1592. {
  1593. $query = $this->_db->getQuery(true)
  1594. ->select($this->_db->qn($hits_field))
  1595. ->from($this->_db->qn($this->_tbl))
  1596. ->where($this->_db->qn($this->_tbl_key) . ' = ' . $this->_db->q($pk));
  1597. $this->$hits_field = $this->_db->setQuery($query)->loadResult();
  1598. }
  1599. else
  1600. {
  1601. // Set table values in the object.
  1602. $this->$hits_field++;
  1603. }
  1604. $result = true;
  1605. }
  1606. if ($result)
  1607. {
  1608. $result = $this->onAfterHit($oid);
  1609. }
  1610. return $result;
  1611. }
  1612. /**
  1613. * Export the item as a CSV line
  1614. *
  1615. * @param string $separator CSV separator. Tip: use "\t" to get a TSV file instead.
  1616. *
  1617. * @return string The CSV line
  1618. */
  1619. public function toCSV($separator = ',')
  1620. {
  1621. $csv = array();
  1622. foreach (get_object_vars($this) as $k => $v)
  1623. {
  1624. if (!in_array($k, $this->getKnownFields()))
  1625. {
  1626. continue;
  1627. }
  1628. $csv[] = '"' . str_replace('"', '""', $v) . '"';
  1629. }
  1630. $csv = implode($separator, $csv);
  1631. return $csv;
  1632. }
  1633. /**
  1634. * Exports the table in array format
  1635. *
  1636. * @return array
  1637. */
  1638. public function getData()
  1639. {
  1640. $ret = array();
  1641. foreach (get_object_vars($this) as $k => $v)
  1642. {
  1643. if (!in_array($k, $this->getKnownFields()))
  1644. {
  1645. continue;
  1646. }
  1647. $ret[$k] = $v;
  1648. }
  1649. return $ret;
  1650. }
  1651. /**
  1652. * Get the header for exporting item list to CSV
  1653. *
  1654. * @param string $separator CSV separator. Tip: use "\t" to get a TSV file instead.
  1655. *
  1656. * @return string The CSV file's header
  1657. */
  1658. public function getCSVHeader($separator = ',')
  1659. {
  1660. $csv = array();
  1661. foreach (get_object_vars($this) as $k => $v)
  1662. {
  1663. if (!in_array($k, $this->getKnownFields()))
  1664. {
  1665. continue;
  1666. }
  1667. $csv[] = '"' . str_replace('"', '\"', $k) . '"';
  1668. }
  1669. $csv = implode($separator, $csv);
  1670. return $csv;
  1671. }
  1672. /**
  1673. * Get the columns from a database table.
  1674. *
  1675. * @param string $tableName Table name. If null current table is used
  1676. *
  1677. * @return mixed An array of the field names, or false if an error occurs.
  1678. */
  1679. public function getTableFields($tableName = null)
  1680. {
  1681. // Should I load the cached data?
  1682. $useCache = array_key_exists('use_table_cache', $this->config) ? $this->config['use_table_cache'] : false;
  1683. // Make sure we have a list of tables in this db
  1684. if (empty(self::$tableCache))
  1685. {
  1686. if ($useCache)
  1687. {
  1688. // Try to load table cache from a cache file
  1689. $cacheData = FOFPlatform::getInstance()->getCache('tables', null);
  1690. // Unserialise the cached data, or set the table cache to empty
  1691. // if the cache data wasn't loaded.
  1692. if (!is_null($cacheData))
  1693. {
  1694. self::$tableCache = json_decode($cacheData, true);
  1695. }
  1696. else
  1697. {
  1698. self::$tableCache = array();
  1699. }
  1700. }
  1701. // This check is true if the cache data doesn't exist / is not loaded
  1702. if (empty(self::$tableCache))
  1703. {
  1704. self::$tableCache = $this->_db->getTableList();
  1705. if ($useCache)
  1706. {
  1707. FOFPlatform::getInstance()->setCache('tables', json_encode(self::$tableCache));
  1708. }
  1709. }
  1710. }
  1711. // Make sure the cached table fields cache is loaded
  1712. if (empty(self::$tableFieldCache))
  1713. {
  1714. if ($useCache)
  1715. {
  1716. // Try to load table cache from a cache file
  1717. $cacheData = FOFPlatform::getInstance()->getCache('tablefields', null);
  1718. // Unserialise the cached data, or set to empty if the cache
  1719. // data wasn't loaded.
  1720. if (!is_null($cacheData))
  1721. {
  1722. $decoded = json_decode($cacheData, true);
  1723. $tableCache = array();
  1724. if (count($decoded))
  1725. {
  1726. foreach ($decoded as $myTableName => $tableFields)
  1727. {
  1728. $temp = array();
  1729. if (is_array($tableFields))
  1730. {
  1731. foreach($tableFields as $field => $def)
  1732. {
  1733. $temp[$field] = (object)$def;
  1734. }
  1735. $tableCache[$myTableName] = $temp;
  1736. }
  1737. elseif (is_object($tableFields) || is_bool($tableFields))
  1738. {
  1739. $tableCache[$myTableName] = $tableFields;
  1740. }
  1741. }
  1742. }
  1743. self::$tableFieldCache = $tableCache;
  1744. }
  1745. else
  1746. {
  1747. self::$tableFieldCache = array();
  1748. }
  1749. }
  1750. }
  1751. if (!$tableName)
  1752. {
  1753. $tableName = $this->_tbl;
  1754. }
  1755. // Try to load again column specifications if the table is not loaded OR if it's loaded and
  1756. // the previous call returned an error
  1757. if (!array_key_exists($tableName, self::$tableFieldCache) ||
  1758. (isset(self::$tableFieldCache[$tableName]) && !self::$tableFieldCache[$tableName]))
  1759. {
  1760. // Lookup the fields for this table only once.
  1761. $name = $tableName;
  1762. $prefix = $this->_db->getPrefix();
  1763. if (substr($name, 0, 3) == '#__')
  1764. {
  1765. $checkName = $prefix . substr($name, 3);
  1766. }
  1767. else
  1768. {
  1769. $checkName = $name;
  1770. }
  1771. if (!in_array($checkName, self::$tableCache))
  1772. {
  1773. // The table doesn't exist. Return false.
  1774. self::$tableFieldCache[$tableName] = false;
  1775. }
  1776. elseif (version_compare(JVERSION, '3.0', 'ge'))
  1777. {
  1778. $fields = $this->_db->getTableColumns($name, false);
  1779. if (empty($fields))
  1780. {
  1781. $fields = false;
  1782. }
  1783. self::$tableFieldCache[$tableName] = $fields;
  1784. }
  1785. else
  1786. {
  1787. $fields = $this->_db->getTableFields($name, false);
  1788. if (!isset($fields[$name]))
  1789. {
  1790. $fields = false;
  1791. }
  1792. self::$tableFieldCache[$tableName] = $fields[$name];
  1793. }
  1794. // PostgreSQL date type compatibility
  1795. if (($this->_db->name == 'postgresql') && (self::$tableFieldCache[$tableName] != false))
  1796. {
  1797. foreach (self::$tableFieldCache[$tableName] as $field)
  1798. {
  1799. if (strtolower($field->type) == 'timestamp without time zone')
  1800. {
  1801. if (stristr($field->Default, '\'::timestamp without time zone'))
  1802. {
  1803. list ($date, $junk) = explode('::', $field->Default, 2);
  1804. $field->Default = trim($date, "'");
  1805. }
  1806. }
  1807. }
  1808. }
  1809. // Save the data for this table into the cache
  1810. if ($useCache)
  1811. {
  1812. $cacheData = FOFPlatform::getInstance()->setCache('tablefields', json_encode(self::$tableFieldCache));
  1813. }
  1814. }
  1815. return self::$tableFieldCache[$tableName];
  1816. }
  1817. public function getTableAlias()
  1818. {
  1819. return $this->_tableAlias;
  1820. }
  1821. public function setTableAlias($string)
  1822. {
  1823. $string = preg_replace('#[^A-Z0-9_]#i', '', $string);
  1824. $this->_tableAlias = $string;
  1825. }
  1826. /**
  1827. * Method to return the real name of a "special" column such as ordering, hits, published
  1828. * etc etc. In this way you are free to follow your db naming convention and use the
  1829. * built in Joomla functions.
  1830. *
  1831. * @param string $column Name of the "special" column (ie ordering, hits etc etc)
  1832. *
  1833. * @return string The string that identify the special
  1834. */
  1835. public function getColumnAlias($column)
  1836. {
  1837. if (isset($this->_columnAlias[$column]))
  1838. {
  1839. $return = $this->_columnAlias[$column];
  1840. }
  1841. else
  1842. {
  1843. $return = $column;
  1844. }
  1845. $return = preg_replace('#[^A-Z0-9_]#i', '', $return);
  1846. return $return;
  1847. }
  1848. /**
  1849. * Method to register a column alias for a "special" column.
  1850. *
  1851. * @param string $column The "special" column (ie ordering)
  1852. * @param string $columnAlias The real column name (ie foo_ordering)
  1853. *
  1854. * @return void
  1855. */
  1856. public function setColumnAlias($column, $columnAlias)
  1857. {
  1858. $column = strtolower($column);
  1859. $column = preg_replace('#[^A-Z0-9_]#i', '', $column);
  1860. $this->_columnAlias[$column] = $columnAlias;
  1861. }
  1862. /**
  1863. * Get a JOIN query, used to join other tables
  1864. *
  1865. * @param boolean $asReference Return an object reference instead of a copy
  1866. *
  1867. * @return JDatabaseQuery Query used to join other tables
  1868. */
  1869. public function getQueryJoin($asReference = false)
  1870. {
  1871. if ($asReference)
  1872. {
  1873. return $this->_queryJoin;
  1874. }
  1875. else
  1876. {
  1877. if ($this->_queryJoin)
  1878. {
  1879. return clone $this->_queryJoin;
  1880. }
  1881. else
  1882. {
  1883. return null;
  1884. }
  1885. }
  1886. }
  1887. /**
  1888. * Sets the query with joins to other tables
  1889. *
  1890. * @param JDatabaseQuery $query The JOIN query to use
  1891. *
  1892. * @return void
  1893. */
  1894. public function setQueryJoin(JDatabaseQuery $query)
  1895. {
  1896. $this->_queryJoin = $query;
  1897. }
  1898. /**
  1899. * Extracts the fields from the join query
  1900. *
  1901. * @return array Fields contained in the join query
  1902. */
  1903. protected function getQueryJoinFields()
  1904. {
  1905. $query = $this->getQueryJoin();
  1906. if (!$query)
  1907. {
  1908. return array();
  1909. }
  1910. $tables = array();
  1911. $j_tables = array();
  1912. $j_fields = array();
  1913. // Get joined tables. Ignore FROM clause, since it should not be used (the starting point is the table "table")
  1914. $joins = $query->join;
  1915. foreach ($joins as $join)
  1916. {
  1917. $tables = array_merge($tables, $join->getElements());
  1918. }
  1919. // Clean up table names
  1920. foreach($tables as $table)
  1921. {
  1922. preg_match('#(.*)((\w)*(on|using))(.*)#i', $table, $matches);
  1923. if($matches && isset($matches[1]))
  1924. {
  1925. // I always want the first part, no matter what
  1926. $parts = explode(' ', $matches[1]);
  1927. $t_table = $parts[0];
  1928. if($this->isQuoted($t_table))
  1929. {
  1930. $t_table = substr($t_table, 1, strlen($t_table) - 2);
  1931. }
  1932. if(!in_array($t_table, $j_tables))
  1933. {
  1934. $j_tables[] = $t_table;
  1935. }
  1936. }
  1937. }
  1938. // Do I have the current table inside the query join? Remove it (its fields are already ok)
  1939. $find = array_search($this->getTableName(), $j_tables);
  1940. if($find !== false)
  1941. {
  1942. unset($j_tables[$find]);
  1943. }
  1944. // Get table fields
  1945. $fields = array();
  1946. foreach ($j_tables as $table)
  1947. {
  1948. $t_fields = $this->getTableFields($table);
  1949. if ($t_fields)
  1950. {
  1951. $fields = array_merge($fields, $t_fields);
  1952. }
  1953. }
  1954. // Remove any fields that aren't in the joined select
  1955. $j_select = $query->select;
  1956. if ($j_select && $j_select->getElements())
  1957. {
  1958. $j_fields = $this->normalizeSelectFields($j_select->getElements());
  1959. }
  1960. // I can intesect the keys
  1961. $fields = array_intersect_key($fields, $j_fields);
  1962. // Now I walk again the array to change the key of columns that have an alias
  1963. foreach ($j_fields as $column => $alias)
  1964. {
  1965. if ($column != $alias)
  1966. {
  1967. $fields[$alias] = $fields[$column];
  1968. unset($fields[$column]);
  1969. }
  1970. }
  1971. return $fields;
  1972. }
  1973. /**
  1974. * Normalizes the fields, returning an associative array with all the fields.
  1975. * Ie array('foobar as foo, bar') becomes array('foobar' => 'foo', 'bar' => 'bar')
  1976. *
  1977. * @param array $fields Array with column fields
  1978. *
  1979. * @return array Normalized array
  1980. */
  1981. protected function normalizeSelectFields($fields)
  1982. {
  1983. $db = FOFPlatform::getInstance()->getDbo();
  1984. $return = array();
  1985. foreach ($fields as $field)
  1986. {
  1987. $t_fields = explode(',', $field);
  1988. foreach ($t_fields as $t_field)
  1989. {
  1990. // Is there any alias?
  1991. $parts = preg_split('#\sas\s#i', $t_field);
  1992. // Do I have a table.column situation? Let's get the field name
  1993. $tableField = explode('.', $parts[0]);
  1994. if(isset($tableField[1]))
  1995. {
  1996. $column = trim($tableField[1]);
  1997. }
  1998. else
  1999. {
  2000. $column = trim($tableField[0]);
  2001. }
  2002. // Is this field quoted? If so, remove the quotes
  2003. if($this->isQuoted($column))
  2004. {
  2005. $column = substr($column, 1, strlen($column) - 2);
  2006. }
  2007. if(isset($parts[1]))
  2008. {
  2009. $alias = trim($parts[1]);
  2010. // Is this field quoted? If so, remove the quotes
  2011. if($this->isQuoted($alias))
  2012. {
  2013. $alias = substr($alias, 1, strlen($alias) - 2);
  2014. }
  2015. }
  2016. else
  2017. {
  2018. $alias = $column;
  2019. }
  2020. $return[$column] = $alias;
  2021. }
  2022. }
  2023. return $return;
  2024. }
  2025. /**
  2026. * Is the field quoted?
  2027. *
  2028. * @param string $column Column field
  2029. *
  2030. * @return bool Is the field quoted?
  2031. */
  2032. protected function isQuoted($column)
  2033. {
  2034. // Empty string, un-quoted by definition
  2035. if(!$column)
  2036. {
  2037. return false;
  2038. }
  2039. // I need some "magic". If the first char is not a letter, a number
  2040. // an underscore or # (needed for table), then most likely the field is quoted
  2041. preg_match_all('/^[a-z0-9_#]/i', $column, $matches);
  2042. if(!$matches[0])
  2043. {
  2044. return true;
  2045. }
  2046. return false;
  2047. }
  2048. /**
  2049. * The event which runs before binding data to the table
  2050. *
  2051. * NOTE TO 3RD PARTY DEVELOPERS:
  2052. *
  2053. * When you override the following methods in your child classes,
  2054. * be sure to call parent::method *AFTER* your code, otherwise the
  2055. * plugin events do NOT get triggered
  2056. *
  2057. * Example:
  2058. * protected function onAfterStore(){
  2059. * // Your code here
  2060. * return parent::onAfterStore() && $your_result;
  2061. * }
  2062. *
  2063. * Do not do it the other way around, e.g. return $your_result && parent::onAfterStore()
  2064. * Due to PHP short-circuit boolean evaluation the parent::onAfterStore()
  2065. * will not be called if $your_result is false.
  2066. *
  2067. * @param object|array &$from The data to bind
  2068. *
  2069. * @return boolean True on success
  2070. */
  2071. protected function onBeforeBind(&$from)
  2072. {
  2073. // Call the behaviors
  2074. $result = $this->tableDispatcher->trigger('onBeforeBind', array(&$this, &$from));
  2075. if (in_array(false, $result, true))
  2076. {
  2077. // Behavior failed, return false
  2078. return false;
  2079. }
  2080. if ($this->_trigger_events)
  2081. {
  2082. $name = FOFInflector::pluralize($this->getKeyName());
  2083. $result = FOFPlatform::getInstance()->runPlugins('onBeforeBind' . ucfirst($name), array(&$this, &$from));
  2084. if (in_array(false, $result, true))
  2085. {
  2086. return false;
  2087. }
  2088. else
  2089. {
  2090. return true;
  2091. }
  2092. }
  2093. return true;
  2094. }
  2095. /**
  2096. * The event which runs after loading a record from the database
  2097. *
  2098. * @param boolean &$result Did the load succeeded?
  2099. *
  2100. * @return void
  2101. */
  2102. protected function onAfterLoad(&$result)
  2103. {
  2104. // Call the behaviors
  2105. $eventResult = $this->tableDispatcher->trigger('onAfterLoad', array(&$this, &$result));
  2106. if (in_array(false, $eventResult, true))
  2107. {
  2108. // Behavior failed, return false
  2109. $result = false;
  2110. return false;
  2111. }
  2112. if ($this->_trigger_events)
  2113. {
  2114. $name = FOFInflector::pluralize($this->getKeyName());
  2115. FOFPlatform::getInstance()->runPlugins('onAfterLoad' . ucfirst($name), array(&$this, &$result));
  2116. }
  2117. }
  2118. /**
  2119. * The event which runs before storing (saving) data to the database
  2120. *
  2121. * @param boolean $updateNulls Should nulls be saved as nulls (true) or just skipped over (false)?
  2122. *
  2123. * @return boolean True to allow saving
  2124. */
  2125. protected function onBeforeStore($updateNulls)
  2126. {
  2127. // Do we have a "Created" set of fields?
  2128. $created_on = $this->getColumnAlias('created_on');
  2129. $created_by = $this->getColumnAlias('created_by');
  2130. $modified_on = $this->getColumnAlias('modified_on');
  2131. $modified_by = $this->getColumnAlias('modified_by');
  2132. $locked_on = $this->getColumnAlias('locked_on');
  2133. $locked_by = $this->getColumnAlias('locked_by');
  2134. $title = $this->getColumnAlias('title');
  2135. $slug = $this->getColumnAlias('slug');
  2136. $hasCreatedOn = in_array($created_on, $this->getKnownFields());
  2137. $hasCreatedBy = in_array($created_by, $this->getKnownFields());
  2138. if ($hasCreatedOn && $hasCreatedBy)
  2139. {
  2140. $hasModifiedOn = in_array($modified_on, $this->getKnownFields());
  2141. $hasModifiedBy = in_array($modified_by, $this->getKnownFields());
  2142. $nullDate = $this->_db->getNullDate();
  2143. if (empty($this->$created_by) || ($this->$created_on == $nullDate) || empty($this->$created_on))
  2144. {
  2145. $uid = FOFPlatform::getInstance()->getUser()->id;
  2146. if ($uid)
  2147. {
  2148. $this->$created_by = FOFPlatform::getInstance()->getUser()->id;
  2149. }
  2150. $date = FOFPlatform::getInstance()->getDate('now', null, false);
  2151. $this->$created_on = $date->toSql();
  2152. }
  2153. elseif ($hasModifiedOn && $hasModifiedBy)
  2154. {
  2155. $uid = FOFPlatform::getInstance()->getUser()->id;
  2156. if ($uid)
  2157. {
  2158. $this->$modified_by = FOFPlatform::getInstance()->getUser()->id;
  2159. }
  2160. $date = FOFPlatform::getInstance()->getDate('now', null, false);
  2161. $this->$modified_on = $date->toSql();
  2162. }
  2163. }
  2164. // Do we have a set of title and slug fields?
  2165. $hasTitle = in_array($title, $this->getKnownFields());
  2166. $hasSlug = in_array($slug, $this->getKnownFields());
  2167. if ($hasTitle && $hasSlug)
  2168. {
  2169. if (empty($this->$slug))
  2170. {
  2171. // Create a slug from the title
  2172. $this->$slug = FOFStringUtils::toSlug($this->$title);
  2173. }
  2174. else
  2175. {
  2176. // Filter the slug for invalid characters
  2177. $this->$slug = FOFStringUtils::toSlug($this->$slug);
  2178. }
  2179. // Make sure we don't have a duplicate slug on this table
  2180. $db = $this->getDbo();
  2181. $query = $db->getQuery(true)
  2182. ->select($db->qn($slug))
  2183. ->from($this->_tbl)
  2184. ->where($db->qn($slug) . ' = ' . $db->q($this->$slug))
  2185. ->where('NOT ' . $db->qn($this->_tbl_key) . ' = ' . $db->q($this->{$this->_tbl_key}));
  2186. $db->setQuery($query);
  2187. $existingItems = $db->loadAssocList();
  2188. $count = 0;
  2189. $newSlug = $this->$slug;
  2190. while (!empty($existingItems))
  2191. {
  2192. $count++;
  2193. $newSlug = $this->$slug . '-' . $count;
  2194. $query = $db->getQuery(true)
  2195. ->select($db->qn($slug))
  2196. ->from($this->_tbl)
  2197. ->where($db->qn($slug) . ' = ' . $db->q($newSlug))
  2198. ->where('NOT '. $db->qn($this->_tbl_key) . ' = ' . $db->q($this->{$this->_tbl_key}));
  2199. $db->setQuery($query);
  2200. $existingItems = $db->loadAssocList();
  2201. }
  2202. $this->$slug = $newSlug;
  2203. }
  2204. // Call the behaviors
  2205. $result = $this->tableDispatcher->trigger('onBeforeStore', array(&$this, $updateNulls));
  2206. if (in_array(false, $result, true))
  2207. {
  2208. // Behavior failed, return false
  2209. return false;
  2210. }
  2211. // Execute onBeforeStore<tablename> events in loaded plugins
  2212. if ($this->_trigger_events)
  2213. {
  2214. $name = FOFInflector::pluralize($this->getKeyName());
  2215. $result = FOFPlatform::getInstance()->runPlugins('onBeforeStore' . ucfirst($name), array(&$this, $updateNulls));
  2216. if (in_array(false, $result, true))
  2217. {
  2218. return false;
  2219. }
  2220. else
  2221. {
  2222. return true;
  2223. }
  2224. }
  2225. return true;
  2226. }
  2227. /**
  2228. * The event which runs after binding data to the class
  2229. *
  2230. * @param object|array &$src The data to bind
  2231. *
  2232. * @return boolean True to allow binding without an error
  2233. */
  2234. protected function onAfterBind(&$src)
  2235. {
  2236. // Call the behaviors
  2237. $options = array(
  2238. 'component' => $this->input->get('option'),
  2239. 'view' => $this->input->get('view'),
  2240. 'table_prefix' => $this->_tablePrefix
  2241. );
  2242. $result = $this->tableDispatcher->trigger('onAfterBind', array(&$this, &$src, $options));
  2243. if (in_array(false, $result, true))
  2244. {
  2245. // Behavior failed, return false
  2246. return false;
  2247. }
  2248. if ($this->_trigger_events)
  2249. {
  2250. $name = FOFInflector::pluralize($this->getKeyName());
  2251. $result = FOFPlatform::getInstance()->runPlugins('onAfterBind' . ucfirst($name), array(&$this, &$src));
  2252. if (in_array(false, $result, true))
  2253. {
  2254. return false;
  2255. }
  2256. else
  2257. {
  2258. return true;
  2259. }
  2260. }
  2261. return true;
  2262. }
  2263. /**
  2264. * The event which runs after storing (saving) data to the database
  2265. *
  2266. * @return boolean True to allow saving without an error
  2267. */
  2268. protected function onAfterStore()
  2269. {
  2270. // Call the behaviors
  2271. $result = $this->tableDispatcher->trigger('onAfterStore', array(&$this));
  2272. if (in_array(false, $result, true))
  2273. {
  2274. // Behavior failed, return false
  2275. return false;
  2276. }
  2277. if ($this->_trigger_events)
  2278. {
  2279. $name = FOFInflector::pluralize($this->getKeyName());
  2280. $result = FOFPlatform::getInstance()->runPlugins('onAfterStore' . ucfirst($name), array(&$this));
  2281. if (in_array(false, $result, true))
  2282. {
  2283. return false;
  2284. }
  2285. else
  2286. {
  2287. return true;
  2288. }
  2289. }
  2290. return true;
  2291. }
  2292. /**
  2293. * The event which runs before moving a record
  2294. *
  2295. * @param boolean $updateNulls Should nulls be saved as nulls (true) or just skipped over (false)?
  2296. *
  2297. * @return boolean True to allow moving
  2298. */
  2299. protected function onBeforeMove($updateNulls)
  2300. {
  2301. // Call the behaviors
  2302. $result = $this->tableDispatcher->trigger('onBeforeMove', array(&$this, $updateNulls));
  2303. if (in_array(false, $result, true))
  2304. {
  2305. // Behavior failed, return false
  2306. return false;
  2307. }
  2308. if ($this->_trigger_events)
  2309. {
  2310. $name = FOFInflector::pluralize($this->getKeyName());
  2311. $result = FOFPlatform::getInstance()->runPlugins('onBeforeMove' . ucfirst($name), array(&$this, $updateNulls));
  2312. if (in_array(false, $result, true))
  2313. {
  2314. return false;
  2315. }
  2316. else
  2317. {
  2318. return true;
  2319. }
  2320. }
  2321. return true;
  2322. }
  2323. /**
  2324. * The event which runs after moving a record
  2325. *
  2326. * @return boolean True to allow moving without an error
  2327. */
  2328. protected function onAfterMove()
  2329. {
  2330. // Call the behaviors
  2331. $result = $this->tableDispatcher->trigger('onAfterMove', array(&$this));
  2332. if (in_array(false, $result, true))
  2333. {
  2334. // Behavior failed, return false
  2335. return false;
  2336. }
  2337. if ($this->_trigger_events)
  2338. {
  2339. $name = FOFInflector::pluralize($this->getKeyName());
  2340. $result = FOFPlatform::getInstance()->runPlugins('onAfterMove' . ucfirst($name), array(&$this));
  2341. if (in_array(false, $result, true))
  2342. {
  2343. return false;
  2344. }
  2345. else
  2346. {
  2347. return true;
  2348. }
  2349. }
  2350. return true;
  2351. }
  2352. /**
  2353. * The event which runs before reordering a table
  2354. *
  2355. * @param string $where The WHERE clause of the SQL query to run on reordering (record filter)
  2356. *
  2357. * @return boolean True to allow reordering
  2358. */
  2359. protected function onBeforeReorder($where = '')
  2360. {
  2361. // Call the behaviors
  2362. $result = $this->tableDispatcher->trigger('onBeforeReorder', array(&$this, $where));
  2363. if (in_array(false, $result, true))
  2364. {
  2365. // Behavior failed, return false
  2366. return false;
  2367. }
  2368. if ($this->_trigger_events)
  2369. {
  2370. $name = FOFInflector::pluralize($this->getKeyName());
  2371. $result = FOFPlatform::getInstance()->runPlugins('onBeforeReorder' . ucfirst($name), array(&$this, $where));
  2372. if (in_array(false, $result, true))
  2373. {
  2374. return false;
  2375. }
  2376. else
  2377. {
  2378. return true;
  2379. }
  2380. }
  2381. return true;
  2382. }
  2383. /**
  2384. * The event which runs after reordering a table
  2385. *
  2386. * @return boolean True to allow the reordering to complete without an error
  2387. */
  2388. protected function onAfterReorder()
  2389. {
  2390. // Call the behaviors
  2391. $result = $this->tableDispatcher->trigger('onAfterReorder', array(&$this));
  2392. if (in_array(false, $result, true))
  2393. {
  2394. // Behavior failed, return false
  2395. return false;
  2396. }
  2397. if ($this->_trigger_events)
  2398. {
  2399. $name = FOFInflector::pluralize($this->getKeyName());
  2400. $result = FOFPlatform::getInstance()->runPlugins('onAfterReorder' . ucfirst($name), array(&$this));
  2401. if (in_array(false, $result, true))
  2402. {
  2403. return false;
  2404. }
  2405. else
  2406. {
  2407. return true;
  2408. }
  2409. }
  2410. return true;
  2411. }
  2412. /**
  2413. * The event which runs before deleting a record
  2414. *
  2415. * @param integer $oid The PK value of the record to delete
  2416. *
  2417. * @return boolean True to allow the deletion
  2418. */
  2419. protected function onBeforeDelete($oid)
  2420. {
  2421. // Call the behaviors
  2422. $result = $this->tableDispatcher->trigger('onBeforeDelete', array(&$this, $oid));
  2423. if (in_array(false, $result, true))
  2424. {
  2425. // Behavior failed, return false
  2426. return false;
  2427. }
  2428. if ($this->_trigger_events)
  2429. {
  2430. $name = FOFInflector::pluralize($this->getKeyName());
  2431. $result = FOFPlatform::getInstance()->runPlugins('onBeforeDelete' . ucfirst($name), array(&$this, $oid));
  2432. if (in_array(false, $result, true))
  2433. {
  2434. return false;
  2435. }
  2436. else
  2437. {
  2438. return true;
  2439. }
  2440. }
  2441. return true;
  2442. }
  2443. /**
  2444. * The event which runs after deleting a record
  2445. *
  2446. * @param integer $oid The PK value of the record which was deleted
  2447. *
  2448. * @return boolean True to allow the deletion without errors
  2449. */
  2450. protected function onAfterDelete($oid)
  2451. {
  2452. // Call the behaviors
  2453. $result = $this->tableDispatcher->trigger('onAfterDelete', array(&$this, $oid));
  2454. if (in_array(false, $result, true))
  2455. {
  2456. // Behavior failed, return false
  2457. return false;
  2458. }
  2459. if ($this->_trigger_events)
  2460. {
  2461. $name = FOFInflector::pluralize($this->getKeyName());
  2462. $result = FOFPlatform::getInstance()->runPlugins('onAfterDelete' . ucfirst($name), array(&$this, $oid));
  2463. if (in_array(false, $result, true))
  2464. {
  2465. return false;
  2466. }
  2467. else
  2468. {
  2469. return true;
  2470. }
  2471. }
  2472. return true;
  2473. }
  2474. /**
  2475. * The event which runs before hitting a record
  2476. *
  2477. * @param integer $oid The PK value of the record to hit
  2478. * @param boolean $log Should we log the hit?
  2479. *
  2480. * @return boolean True to allow the hit
  2481. */
  2482. protected function onBeforeHit($oid, $log)
  2483. {
  2484. // Call the behaviors
  2485. $result = $this->tableDispatcher->trigger('onBeforeHit', array(&$this, $oid, $log));
  2486. if (in_array(false, $result, true))
  2487. {
  2488. // Behavior failed, return false
  2489. return false;
  2490. }
  2491. if ($this->_trigger_events)
  2492. {
  2493. $name = FOFInflector::pluralize($this->getKeyName());
  2494. $result = FOFPlatform::getInstance()->runPlugins('onBeforeHit' . ucfirst($name), array(&$this, $oid, $log));
  2495. if (in_array(false, $result, true))
  2496. {
  2497. return false;
  2498. }
  2499. else
  2500. {
  2501. return true;
  2502. }
  2503. }
  2504. return true;
  2505. }
  2506. /**
  2507. * The event which runs after hitting a record
  2508. *
  2509. * @param integer $oid The PK value of the record which was hit
  2510. *
  2511. * @return boolean True to allow the hitting without errors
  2512. */
  2513. protected function onAfterHit($oid)
  2514. {
  2515. // Call the behaviors
  2516. $result = $this->tableDispatcher->trigger('onAfterHit', array(&$this, $oid));
  2517. if (in_array(false, $result, true))
  2518. {
  2519. // Behavior failed, return false
  2520. return false;
  2521. }
  2522. if ($this->_trigger_events)
  2523. {
  2524. $name = FOFInflector::pluralize($this->getKeyName());
  2525. $result = FOFPlatform::getInstance()->runPlugins('onAfterHit' . ucfirst($name), array(&$this, $oid));
  2526. if (in_array(false, $result, true))
  2527. {
  2528. return false;
  2529. }
  2530. else
  2531. {
  2532. return true;
  2533. }
  2534. }
  2535. return true;
  2536. }
  2537. /**
  2538. * The even which runs before copying a record
  2539. *
  2540. * @param integer $oid The PK value of the record being copied
  2541. *
  2542. * @return boolean True to allow the copy to take place
  2543. */
  2544. protected function onBeforeCopy($oid)
  2545. {
  2546. // Call the behaviors
  2547. $result = $this->tableDispatcher->trigger('onBeforeCopy', array(&$this, $oid));
  2548. if (in_array(false, $result, true))
  2549. {
  2550. // Behavior failed, return false
  2551. return false;
  2552. }
  2553. if ($this->_trigger_events)
  2554. {
  2555. $name = FOFInflector::pluralize($this->getKeyName());
  2556. $result = FOFPlatform::getInstance()->runPlugins('onBeforeCopy' . ucfirst($name), array(&$this, $oid));
  2557. if (in_array(false, $result, true))
  2558. {
  2559. return false;
  2560. }
  2561. else
  2562. {
  2563. return true;
  2564. }
  2565. }
  2566. return true;
  2567. }
  2568. /**
  2569. * The even which runs after copying a record
  2570. *
  2571. * @param integer $oid The PK value of the record which was copied (not the new one)
  2572. *
  2573. * @return boolean True to allow the copy without errors
  2574. */
  2575. protected function onAfterCopy($oid)
  2576. {
  2577. // Call the behaviors
  2578. $result = $this->tableDispatcher->trigger('onAfterCopy', array(&$this, $oid));
  2579. if (in_array(false, $result, true))
  2580. {
  2581. // Behavior failed, return false
  2582. return false;
  2583. }
  2584. if ($this->_trigger_events)
  2585. {
  2586. $name = FOFInflector::pluralize($this->getKeyName());
  2587. $result = FOFPlatform::getInstance()->runPlugins('onAfterCopy' . ucfirst($name), array(&$this, $oid));
  2588. if (in_array(false, $result, true))
  2589. {
  2590. return false;
  2591. }
  2592. else
  2593. {
  2594. return true;
  2595. }
  2596. }
  2597. return true;
  2598. }
  2599. /**
  2600. * The event which runs before a record is (un)published
  2601. *
  2602. * @param integer|array &$cid The PK IDs of the records being (un)published
  2603. * @param integer $publish 1 to publish, 0 to unpublish
  2604. *
  2605. * @return boolean True to allow the (un)publish to proceed
  2606. */
  2607. protected function onBeforePublish(&$cid, $publish)
  2608. {
  2609. // Call the behaviors
  2610. $result = $this->tableDispatcher->trigger('onBeforePublish', array(&$this, &$cid, $publish));
  2611. if (in_array(false, $result, true))
  2612. {
  2613. // Behavior failed, return false
  2614. return false;
  2615. }
  2616. if ($this->_trigger_events)
  2617. {
  2618. $name = FOFInflector::pluralize($this->getKeyName());
  2619. $result = FOFPlatform::getInstance()->runPlugins('onBeforePublish' . ucfirst($name), array(&$this, &$cid, $publish));
  2620. if (in_array(false, $result, true))
  2621. {
  2622. return false;
  2623. }
  2624. else
  2625. {
  2626. return true;
  2627. }
  2628. }
  2629. return true;
  2630. }
  2631. /**
  2632. * The event which runs after the object is reset to its default values.
  2633. *
  2634. * @return boolean True to allow the reset to complete without errors
  2635. */
  2636. protected function onAfterReset()
  2637. {
  2638. // Call the behaviors
  2639. $result = $this->tableDispatcher->trigger('onAfterReset', array(&$this));
  2640. if (in_array(false, $result, true))
  2641. {
  2642. // Behavior failed, return false
  2643. return false;
  2644. }
  2645. if ($this->_trigger_events)
  2646. {
  2647. $name = FOFInflector::pluralize($this->getKeyName());
  2648. $result = FOFPlatform::getInstance()->runPlugins('onAfterReset' . ucfirst($name), array(&$this));
  2649. if (in_array(false, $result, true))
  2650. {
  2651. return false;
  2652. }
  2653. else
  2654. {
  2655. return true;
  2656. }
  2657. }
  2658. return true;
  2659. }
  2660. /**
  2661. * The even which runs before the object is reset to its default values.
  2662. *
  2663. * @return boolean True to allow the reset to complete
  2664. */
  2665. protected function onBeforeReset()
  2666. {
  2667. // Call the behaviors
  2668. $result = $this->tableDispatcher->trigger('onBeforeReset', array(&$this));
  2669. if (in_array(false, $result, true))
  2670. {
  2671. // Behavior failed, return false
  2672. return false;
  2673. }
  2674. if ($this->_trigger_events)
  2675. {
  2676. $name = FOFInflector::pluralize($this->getKeyName());
  2677. $result = FOFPlatform::getInstance()->runPlugins('onBeforeReset' . ucfirst($name), array(&$this));
  2678. if (in_array(false, $result, true))
  2679. {
  2680. return false;
  2681. }
  2682. else
  2683. {
  2684. return true;
  2685. }
  2686. }
  2687. return true;
  2688. }
  2689. /**
  2690. * Replace the input object of this table with the provided FOFInput object
  2691. *
  2692. * @param FOFInput $input The new input object
  2693. *
  2694. * @return void
  2695. */
  2696. public function setInput(FOFInput $input)
  2697. {
  2698. $this->input = $input;
  2699. }
  2700. /**
  2701. * Get the columns from database table.
  2702. *
  2703. * @return mixed An array of the field names, or false if an error occurs.
  2704. *
  2705. * @deprecated 2.1
  2706. */
  2707. public function getFields()
  2708. {
  2709. return $this->getTableFields();
  2710. }
  2711. /**
  2712. * Add a filesystem path where FOFTable should search for table class files.
  2713. * You may either pass a string or an array of paths.
  2714. *
  2715. * @param mixed $path A filesystem path or array of filesystem paths to add.
  2716. *
  2717. * @return array An array of filesystem paths to find FOFTable classes in.
  2718. */
  2719. public static function addIncludePath($path = null)
  2720. {
  2721. // If the internal paths have not been initialised, do so with the base table path.
  2722. if (empty(self::$_includePaths))
  2723. {
  2724. self::$_includePaths = array(__DIR__);
  2725. }
  2726. // Convert the passed path(s) to add to an array.
  2727. settype($path, 'array');
  2728. // If we have new paths to add, do so.
  2729. if (!empty($path) && !in_array($path, self::$_includePaths))
  2730. {
  2731. // Check and add each individual new path.
  2732. foreach ($path as $dir)
  2733. {
  2734. // Sanitize path.
  2735. $dir = trim($dir);
  2736. // Add to the front of the list so that custom paths are searched first.
  2737. array_unshift(self::$_includePaths, $dir);
  2738. }
  2739. }
  2740. return self::$_includePaths;
  2741. }
  2742. /**
  2743. * Loads the asset table related to this table.
  2744. * This will help tests, too, since we can mock this function.
  2745. *
  2746. * @return bool|JTableAsset False on failure, otherwise JTableAsset
  2747. */
  2748. protected function getAsset()
  2749. {
  2750. $name = $this->_getAssetName();
  2751. // Do NOT touch JTable here -- we are loading the core asset table which is a JTable, not a FOFTable
  2752. $asset = JTable::getInstance('Asset');
  2753. if (!$asset->loadByName($name))
  2754. {
  2755. return false;
  2756. }
  2757. return $asset;
  2758. }
  2759. /**
  2760. * Method to compute the default name of the asset.
  2761. * The default name is in the form table_name.id
  2762. * where id is the value of the primary key of the table.
  2763. *
  2764. * @throws UnexpectedValueException
  2765. *
  2766. * @return string
  2767. */
  2768. public function getAssetName()
  2769. {
  2770. $k = $this->_tbl_key;
  2771. // If there is no assetKey defined, stop here, or we'll get a wrong name
  2772. if(!$this->_assetKey || !$this->$k)
  2773. {
  2774. throw new UnexpectedValueException('Table must have an asset key defined and a value for the table id in order to track assets');
  2775. }
  2776. return $this->_assetKey . '.' . (int) $this->$k;
  2777. }
  2778. /**
  2779. * Method to compute the default name of the asset.
  2780. * The default name is in the form table_name.id
  2781. * where id is the value of the primary key of the table.
  2782. *
  2783. * @throws UnexpectedValueException
  2784. *
  2785. * @return string
  2786. */
  2787. public function getAssetKey()
  2788. {
  2789. return $this->_assetKey;
  2790. }
  2791. /**
  2792. * Method to return the title to use for the asset table. In
  2793. * tracking the assets a title is kept for each asset so that there is some
  2794. * context available in a unified access manager. Usually this would just
  2795. * return $this->title or $this->name or whatever is being used for the
  2796. * primary name of the row. If this method is not overridden, the asset name is used.
  2797. *
  2798. * @return string The string to use as the title in the asset table.
  2799. */
  2800. public function getAssetTitle()
  2801. {
  2802. return $this->getAssetName();
  2803. }
  2804. /**
  2805. * Method to get the parent asset under which to register this one.
  2806. * By default, all assets are registered to the ROOT node with ID,
  2807. * which will default to 1 if none exists.
  2808. * The extended class can define a table and id to lookup. If the
  2809. * asset does not exist it will be created.
  2810. *
  2811. * @param FOFTable $table A FOFTable object for the asset parent.
  2812. * @param integer $id Id to look up
  2813. *
  2814. * @return integer
  2815. */
  2816. public function getAssetParentId($table = null, $id = null)
  2817. {
  2818. // For simple cases, parent to the asset root.
  2819. $assets = JTable::getInstance('Asset', 'JTable', array('dbo' => $this->getDbo()));
  2820. $rootId = $assets->getRootId();
  2821. if (!empty($rootId))
  2822. {
  2823. return $rootId;
  2824. }
  2825. return 1;
  2826. }
  2827. /**
  2828. * This method sets the asset key for the items of this table. Obviously, it
  2829. * is only meant to be used when you have a table with an asset field.
  2830. *
  2831. * @param string $assetKey The name of the asset key to use
  2832. *
  2833. * @return void
  2834. */
  2835. public function setAssetKey($assetKey)
  2836. {
  2837. $this->_assetKey = $assetKey;
  2838. }
  2839. /**
  2840. * Method to get the database table name for the class.
  2841. *
  2842. * @return string The name of the database table being modeled.
  2843. */
  2844. public function getTableName()
  2845. {
  2846. return $this->_tbl;
  2847. }
  2848. /**
  2849. * Method to get the primary key field name for the table.
  2850. *
  2851. * @return string The name of the primary key for the table.
  2852. */
  2853. public function getKeyName()
  2854. {
  2855. return $this->_tbl_key;
  2856. }
  2857. /**
  2858. * Method to get the JDatabaseDriver object.
  2859. *
  2860. * @return JDatabaseDriver The internal database driver object.
  2861. */
  2862. public function getDbo()
  2863. {
  2864. return $this->_db;
  2865. }
  2866. /**
  2867. * Method to set the JDatabaseDriver object.
  2868. *
  2869. * @param JDatabaseDriver $db A JDatabaseDriver object to be used by the table object.
  2870. *
  2871. * @return boolean True on success.
  2872. */
  2873. public function setDBO(JDatabaseDriver $db)
  2874. {
  2875. $this->_db = $db;
  2876. return true;
  2877. }
  2878. /**
  2879. * Method to set rules for the record.
  2880. *
  2881. * @param mixed $input A JAccessRules object, JSON string, or array.
  2882. *
  2883. * @return void
  2884. */
  2885. public function setRules($input)
  2886. {
  2887. if ($input instanceof JAccessRules)
  2888. {
  2889. $this->_rules = $input;
  2890. }
  2891. else
  2892. {
  2893. $this->_rules = new JAccessRules($input);
  2894. }
  2895. }
  2896. /**
  2897. * Method to get the rules for the record.
  2898. *
  2899. * @return JAccessRules object
  2900. */
  2901. public function getRules()
  2902. {
  2903. return $this->_rules;
  2904. }
  2905. /**
  2906. * Method to check if the record is treated as an ACL asset
  2907. *
  2908. * @return boolean [description]
  2909. */
  2910. public function isAssetsTracked()
  2911. {
  2912. return $this->_trackAssets;
  2913. }
  2914. /**
  2915. * Method to manually set this record as ACL asset or not.
  2916. * We have to do this since the automatic check is made in the constructor, but here we can't set any alias.
  2917. * So, even if you have an alias for `asset_id`, it wouldn't be reconized and assets won't be tracked.
  2918. *
  2919. * @param $state
  2920. */
  2921. public function setAssetsTracked($state)
  2922. {
  2923. $state = (bool) $state;
  2924. if($state)
  2925. {
  2926. JLoader::import('joomla.access.rules');
  2927. }
  2928. $this->_trackAssets = $state;
  2929. }
  2930. /**
  2931. * Method to provide a shortcut to binding, checking and storing a FOFTable
  2932. * instance to the database table. The method will check a row in once the
  2933. * data has been stored and if an ordering filter is present will attempt to
  2934. * reorder the table rows based on the filter. The ordering filter is an instance
  2935. * property name. The rows that will be reordered are those whose value matches
  2936. * the FOFTable instance for the property specified.
  2937. *
  2938. * @param mixed $src An associative array or object to bind to the FOFTable instance.
  2939. * @param string $orderingFilter Filter for the order updating
  2940. * @param mixed $ignore An optional array or space separated list of properties
  2941. * to ignore while binding.
  2942. *
  2943. * @return boolean True on success.
  2944. */
  2945. public function save($src, $orderingFilter = '', $ignore = '')
  2946. {
  2947. // Attempt to bind the source to the instance.
  2948. if (!$this->bind($src, $ignore))
  2949. {
  2950. return false;
  2951. }
  2952. // Run any sanity checks on the instance and verify that it is ready for storage.
  2953. if (!$this->check())
  2954. {
  2955. return false;
  2956. }
  2957. // Attempt to store the properties to the database table.
  2958. if (!$this->store())
  2959. {
  2960. return false;
  2961. }
  2962. // Attempt to check the row in, just in case it was checked out.
  2963. if (!$this->checkin())
  2964. {
  2965. return false;
  2966. }
  2967. // If an ordering filter is set, attempt reorder the rows in the table based on the filter and value.
  2968. if ($orderingFilter)
  2969. {
  2970. $filterValue = $this->$orderingFilter;
  2971. $this->reorder($orderingFilter ? $this->_db->qn($orderingFilter) . ' = ' . $this->_db->q($filterValue) : '');
  2972. }
  2973. // Set the error to empty and return true.
  2974. $this->setError('');
  2975. return true;
  2976. }
  2977. /**
  2978. * Method to get the next ordering value for a group of rows defined by an SQL WHERE clause.
  2979. * This is useful for placing a new item last in a group of items in the table.
  2980. *
  2981. * @param string $where WHERE clause to use for selecting the MAX(ordering) for the table.
  2982. *
  2983. * @return mixed Boolean false an failure or the next ordering value as an integer.
  2984. */
  2985. public function getNextOrder($where = '')
  2986. {
  2987. // If there is no ordering field set an error and return false.
  2988. $ordering = $this->getColumnAlias('ordering');
  2989. if (!in_array($ordering, $this->getKnownFields()))
  2990. {
  2991. throw new UnexpectedValueException(sprintf('%s does not support ordering.', get_class($this)));
  2992. }
  2993. // Get the largest ordering value for a given where clause.
  2994. $query = $this->_db->getQuery(true);
  2995. $query->select('MAX('.$this->_db->qn($ordering).')');
  2996. $query->from($this->_tbl);
  2997. if ($where)
  2998. {
  2999. $query->where($where);
  3000. }
  3001. $this->_db->setQuery($query);
  3002. $max = (int) $this->_db->loadResult();
  3003. // Return the largest ordering value + 1.
  3004. return ($max + 1);
  3005. }
  3006. /**
  3007. * Method to lock the database table for writing.
  3008. *
  3009. * @return boolean True on success.
  3010. *
  3011. * @throws RuntimeException
  3012. */
  3013. protected function _lock()
  3014. {
  3015. $this->_db->lockTable($this->_tbl);
  3016. $this->_locked = true;
  3017. return true;
  3018. }
  3019. /**
  3020. * Method to unlock the database table for writing.
  3021. *
  3022. * @return boolean True on success.
  3023. */
  3024. protected function _unlock()
  3025. {
  3026. $this->_db->unlockTables();
  3027. $this->_locked = false;
  3028. return true;
  3029. }
  3030. public function setConfig(array $config)
  3031. {
  3032. $this->config = $config;
  3033. }
  3034. /**
  3035. * Get the content type for ucm
  3036. *
  3037. * @return string The content type alias
  3038. */
  3039. public function getContentType()
  3040. {
  3041. $component = $this->input->get('option');
  3042. $view = FOFInflector::singularize($this->input->get('view'));
  3043. $alias = $component . '.' . $view;
  3044. return $alias;
  3045. }
  3046. /**
  3047. * Returns the table relations object of the current table, lazy-loading it if necessary
  3048. *
  3049. * @return FOFTableRelations
  3050. */
  3051. public function getRelations()
  3052. {
  3053. if (is_null($this->_relations))
  3054. {
  3055. $this->_relations = new FOFTableRelations($this);
  3056. }
  3057. return $this->_relations;
  3058. }
  3059. /**
  3060. * Gets a reference to the configuration parameters provider for this table
  3061. *
  3062. * @return FOFConfigProvider
  3063. */
  3064. public function getConfigProvider()
  3065. {
  3066. return $this->configProvider;
  3067. }
  3068. /**
  3069. * Returns the configuration parameters provider's key for this table
  3070. *
  3071. * @return string
  3072. */
  3073. public function getConfigProviderKey()
  3074. {
  3075. return $this->_configProviderKey;
  3076. }
  3077. }