PageRenderTime 68ms CodeModel.GetById 27ms 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

Large files files are truncated, but you can click here to view the full file

  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[$table

Large files files are truncated, but you can click here to view the full file