diff --git a/component/admin/views/category/tmpl/edit_information.php b/component/admin/views/category/tmpl/edit_information.php
index 69d6dad355e..7ce89fcb3b3 100644
--- a/component/admin/views/category/tmpl/edit_information.php
+++ b/component/admin/views/category/tmpl/edit_information.php
@@ -16,39 +16,15 @@
-
- form->renderField('name') ?>
-
-
- form->renderField('parent_id') ?>
-
-
- form->renderField('published') ?>
-
-
-
- form->renderField('products_per_page') ?>
-
-
-
- form->renderField('template') ?>
-
-
-
- form->renderField('more_template') ?>
-
-
-
- form->renderField('compare_template_id') ?>
-
-
-
- form->renderField('short_description') ?>
-
-
-
- form->renderField('description') ?>
-
+ form->renderField('name') ?>
+ form->renderField('parent_id') ?>
+ form->renderField('published') ?>
+ form->renderField('products_per_page') ?>
+ form->renderField('template') ?>
+ form->renderField('more_template') ?>
+ form->renderField('compare_template_id') ?>
+ form->renderField('short_description') ?>
+ form->renderField('description') ?>
diff --git a/libraries/redshop/src/Table/AbstractNestedTable.php b/libraries/redshop/src/Table/AbstractNestedTable.php
deleted file mode 100644
index 90bf32e6e06..00000000000
--- a/libraries/redshop/src/Table/AbstractNestedTable.php
+++ /dev/null
@@ -1,480 +0,0 @@
-_tbl) && !empty($this->_tableName))
- {
- // Add the table prefix
- $this->_tbl = '#__' . $this->_tableName;
- }
-
- $key = $this->_tbl_key;
-
- if (empty($key) && !empty($this->_tbl_keys))
- {
- $key = $this->_tbl_keys;
- }
-
- // Keep checking _tbl_key for standard defined tables
- if (empty($key) && !empty($this->_tableKey))
- {
- $this->_tbl_key = $this->_tableKey;
- $key = $this->_tbl_key;
- }
-
- if (empty($this->_tbl) || empty($key))
- {
- throw new \UnexpectedValueException(sprintf('Missing data to initialize %s table | id: %s', $this->_tbl, $key));
- }
-
- if ($this->autoEvents)
- {
- $this->generateEventsConfig();
- }
-
- parent::__construct($this->_tbl, $key, $db);
- }
-
- /**
- * Checks that the object is valid and able to be stored.
- *
- * This method checks that the parent_id is non-zero and exists in the database.
- * Note that the root node (parent_id = 0) cannot be manipulated with this class.
- *
- * @return boolean True if all checks pass.
- */
- protected function doCheck()
- {
- if (!parent::check())
- {
- return false;
- }
-
- return true;
- }
-
- /**
- * Delete one or more registers
- *
- * @param string /array $pk Array of ids or ids comma separated
- *
- * @return boolean Deleted successfuly?
- */
- protected function doDelete($pk = null)
- {
- // Initialise variables.
- $k = $this->_tbl_key;
-
- // Multiple keys
- $multiplePrimaryKeys = count($this->_tbl_keys) > 1;
-
- // We are dealing with multiple primary keys
- if ($multiplePrimaryKeys)
- {
- // Received an array of ids?
- if (is_null($pk))
- {
- $pk = array();
-
- foreach ($this->_tbl_keys AS $key)
- {
- $pk[$key] = $this->$key;
- }
- }
- elseif (is_array($pk))
- {
- foreach ($this->_tbl_keys AS $key)
- {
- $pk[$key] = !empty($pk[$key]) ? $pk[$key] : $this->$key;
- }
- }
- }
- // Standard Joomla delete method
- else
- {
- if (is_array($pk))
- {
- // Sanitize input.
- $pk = ArrayHelper::toInteger($pk);
- $pk = \RedshopHelperUtility::quote($pk);
- $pk = implode(',', $pk);
- }
- // Try the instance property value
- elseif (empty($pk) && $this->{$k})
- {
- $pk = $this->{$k};
- }
- }
-
- // If no primary key is given, return false.
- if ($pk === null)
- {
- return false;
- }
-
- // Delete the row by primary key.
- $query = $this->_db->getQuery(true);
- $query->delete($this->_db->quoteName($this->_tbl));
-
- if ($multiplePrimaryKeys)
- {
- foreach ($this->_tbl_keys AS $k)
- {
- $query->where($this->_db->quoteName($k) . ' = ' . $this->_db->quote($pk[$k]));
- }
- }
- else
- {
- $query->where($this->_db->quoteName($this->_tbl_key) . ' IN (' . $pk . ')');
- }
-
- $this->_db->setQuery($query);
- $this->_db->execute();
-
- // Check for a database error.
- if ($this->_db->getErrorNum())
- {
- $this->setError($this->_db->getErrorMsg());
-
- return false;
- }
-
- return true;
- }
-
- /**
- * Method to set the publishing state for a row or list of rows in the database
- * table. The method respects checked out rows by other users and will attempt
- * to checkin rows that it can after adjustments are made.
- *
- * @param mixed $pks An optional array of primary key values to update.
- * If not set the instance property value is used.
- * @param integer $state The publishing state. eg. [0 = unpublished, 1 = published]
- * @param integer $userId The user id of the user performing the operation.
- *
- * @return boolean True on success; false if $pks is empty.
- *
- * @link https://docs.joomla.org/JTable/publish
- */
- public function doPublish($pks = null, $state = 1, $userId = 0)
- {
- // Sanitize input
- $userId = (int) $userId;
- $state = (int) $state;
-
- if (!is_null($pks))
- {
- if (!is_array($pks))
- {
- $pks = array($pks);
- }
-
- foreach ($pks as $key => $pk)
- {
- if (!is_array($pk))
- {
- $pks[$key] = array($this->_tbl_key => $pk);
- }
- }
- }
-
- // If there are no primary keys set check to see if the instance key is set.
- if (empty($pks))
- {
- $pk = array();
-
- foreach ($this->_tbl_keys AS $key)
- {
- if ($this->$key)
- {
- $pk[$key] = $this->$key;
- }
- // We don't have a full primary key - return false
- else
- {
- $this->setError(\JText::_('JLIB_DATABASE_ERROR_NO_ROWS_SELECTED'));
-
- return false;
- }
- }
-
- $pks = array($pk);
- }
-
- foreach ($pks as $pk)
- {
- // Update the publishing state for rows with the given primary keys.
- $query = $this->_db->getQuery(true)
- ->update($this->_tbl)
- ->set($this->_db->quoteName($this->getColumnAlias('published')) . ' = ' . (int) $state);
-
- // Determine if there is checkin support for the table.
- if (property_exists($this, 'checked_out') || property_exists($this, 'checked_out_time'))
- {
- $query->where('(' . $this->getColumnAlias('checked_out') . ' = 0'
- . ' OR ' . $this->getColumnAlias('checked_out') . ' IS NULL'
- . ' OR ' . $this->getColumnAlias('checked_out') . ' = ' . (int) $userId . ')');
- $checkin = true;
- }
- else
- {
- $checkin = false;
- }
-
- // Build the WHERE clause for the primary keys.
- $this->appendPrimaryKeys($query, $pk);
-
- $this->_db->setQuery($query);
-
- try
- {
- $this->_db->execute();
- }
- catch (\RuntimeException $e)
- {
- $this->setError($e->getMessage());
-
- return false;
- }
-
- // If checkin is supported and all rows were adjusted, check them in.
- if ($checkin && (count($pks) == $this->_db->getAffectedRows()))
- {
- $this->checkIn($pk);
- }
-
- // If the JTable instance value is in the list of primary keys that were set, set the instance.
- $ours = true;
-
- foreach ($this->_tbl_keys AS $key)
- {
- if ($this->{$key} != $pk[$key])
- {
- $ours = false;
- }
- }
-
- if ($ours)
- {
- $publishedField = $this->getColumnAlias('published');
- $this->{$publishedField} = $state;
- }
- }
-
- $this->setError('');
-
- return true;
- }
-
- /**
- * Do the database store.
- *
- * @param boolean $updateNulls True to update null values as well.
- *
- * @return boolean
- */
- protected function doStore($updateNulls = false)
- {
- $k = $this->_tbl_keys;
-
- // Implement JObservableInterface: Pre-processing by observers
- $this->_observers->update('onBeforeStore', array($updateNulls, $k));
-
- $currentAssetId = 0;
-
- if (!empty($this->asset_id))
- {
- $currentAssetId = $this->asset_id;
- }
-
- // The asset id field is managed privately by this class.
- if ($this->_trackAssets)
- {
- unset($this->asset_id);
- }
-
- // If a primary key exists update the object, otherwise insert it.
- if ($this->hasPrimaryKey())
- {
- $result = $this->_db->updateObject($this->_tbl, $this, $this->_tbl_keys, $updateNulls);
- }
- else
- {
- $result = $this->_db->insertObject($this->_tbl, $this, $this->_tbl_keys[0]);
- }
-
- // If the table is not set to track assets return true.
- if ($this->_trackAssets)
- {
- if ($this->_locked)
- {
- $this->_unlock();
- }
-
- /*
- * Asset Tracking
- */
- $parentId = $this->_getAssetParentId();
- $name = $this->_getAssetName();
- $title = $this->_getAssetTitle();
-
- /** @var \JTableAsset $asset */
- $asset = self::getInstance('Asset', 'JTable', array('dbo' => $this->getDbo()));
- $asset->loadByName($name);
-
- // Re-inject the asset id.
- $this->asset_id = $asset->id;
-
- // Check for an error.
- $error = $asset->getError();
-
- if ($error)
- {
- $this->setError($error);
-
- return false;
- }
- else
- {
- // Specify how a new or moved node asset is inserted into the tree.
- if (empty($this->asset_id) || $asset->parent_id != $parentId)
- {
- $asset->setLocation($parentId, 'last-child');
- }
-
- // Prepare the asset to be stored.
- $asset->parent_id = $parentId;
- $asset->name = $name;
- $asset->title = $title;
-
- if ($this->_rules instanceof \JAccessRules)
- {
- $asset->rules = (string) $this->_rules;
- }
-
- if (!$asset->check() || !$asset->store())
- {
- $this->setError($asset->getError());
-
- return false;
- }
- else
- {
- // Create an asset_id or heal one that is corrupted.
- if (empty($this->asset_id) || ($currentAssetId != $this->asset_id && !empty($this->asset_id)))
- {
- // Update the asset_id field in this table.
- $this->asset_id = (int) $asset->id;
-
- $query = $this->_db->getQuery(true)
- ->update($this->_db->quoteName($this->_tbl))
- ->set('asset_id = ' . (int) $this->asset_id);
- $this->appendPrimaryKeys($query);
- $this->_db->setQuery($query)->execute();
- }
- }
- }
- }
-
- // Implement JObservableInterface: Post-processing by observers
- $this->_observers->update('onAfterStore', array(&$result));
-
- return $result;
- }
-
- /**
- * Get a table option value.
- *
- * @param string $key The key
- * @param mixed $default The default value
- *
- * @return mixed The value or the default value
- */
- public function getOption($key, $default = null)
- {
- if (isset($this->options[$key]))
- {
- return $this->options[$key];
- }
-
- return $default;
- }
-
- /**
- * Set a table option value.
- *
- * @param string $key The key
- * @param mixed $val The default value
- *
- * @return self
- */
- public function setOption($key, $val)
- {
- $this->options[$key] = $val;
-
- return $this;
- }
-}
diff --git a/libraries/redshop/table/nested.php b/libraries/redshop/table/nested.php
index b2f1506fefc..d1e0534f6e3 100755
--- a/libraries/redshop/table/nested.php
+++ b/libraries/redshop/table/nested.php
@@ -7,8 +7,6 @@
* @license GNU General Public License version 2 or later, see LICENSE.
*/
-use Redshop\Table\AbstractNestedTable;
-
defined('_JEXEC') or die;
JLoader::import('joomla.database.tablenested');
@@ -20,12 +18,848 @@
* @subpackage Base
* @since 1.0
*/
-class RedshopTableNested extends AbstractNestedTable
+class RedshopTableNested extends JTableNested
{
+ /**
+ * The options.
+ *
+ * @var array
+ */
+ protected $_options = array();
+
/**
* Prefix to add to log files
*
* @var string
*/
- protected $logPrefix = 'redshop';
-}
+ protected $_logPrefix = 'redshop';
+
+ /**
+ * The table name without the prefix. Ex: cursos_courses
+ *
+ * @var string
+ */
+ protected $_tableName = null;
+
+ /**
+ * The table key column. Usually: id
+ *
+ * @var string
+ */
+ protected $_tableKey = 'id';
+
+ /**
+ * Field name to publish/unpublish/trash table registers. Ex: state
+ *
+ * @var string
+ */
+ protected $_tableFieldState = 'published';
+
+ /**
+ * Field name to keep creator user (created_by)
+ *
+ * @var string
+ */
+ protected $_tableFieldCreatedBy = 'created_by';
+
+ /**
+ * Field name to keep latest modifier user (modified_by)
+ *
+ * @var string
+ */
+ protected $_tableFieldModifiedBy = 'modified_by';
+
+ /**
+ * Field name to keep created date (created_date)
+ *
+ * @var string
+ */
+ protected $_tableFieldCreatedDate = 'created_date';
+
+ /**
+ * Field name to keep latest modified user (modified_date)
+ *
+ * @var string
+ */
+ protected $_tableFieldModifiedDate = 'modified_date';
+
+ /**
+ * Format for audit date fields (created_date, modified_date)
+ *
+ * @var string
+ */
+ protected $_auditDateFormat = 'Y-m-d H:i:s';
+
+ /**
+ * An array of plugin types to import.
+ *
+ * @var array
+ */
+ protected $_pluginTypesToImport = array();
+
+ /**
+ * Event name to trigger before load().
+ *
+ * @var string
+ */
+ protected $_eventBeforeLoad;
+
+ /**
+ * Event name to trigger after load().
+ *
+ * @var string
+ */
+ protected $_eventAfterLoad;
+
+ /**
+ * Event name to trigger before delete().
+ *
+ * @var string
+ */
+ protected $_eventBeforeDelete;
+
+ /**
+ * Event name to trigger after delete().
+ *
+ * @var string
+ */
+ protected $_eventAfterDelete;
+
+ /**
+ * Event name to trigger before check().
+ *
+ * @var string
+ */
+ protected $_eventBeforeCheck;
+
+ /**
+ * Event name to trigger after check().
+ *
+ * @var string
+ */
+ protected $_eventAfterCheck;
+
+ /**
+ * Event name to trigger before store().
+ *
+ * @var string
+ */
+ protected $_eventBeforeStore;
+
+ /**
+ * Event name to trigger after store().
+ *
+ * @var string
+ */
+ protected $_eventAfterStore;
+
+ /**
+ * Constructor
+ *
+ * @param JDatabase &$db A database connector object
+ *
+ * @throws UnexpectedValueException
+ */
+ public function __construct(&$db)
+ {
+ // Keep checking _tbl value for standard defined tables
+ if (empty($this->_tbl) && !empty($this->_tableName))
+ {
+ // Add the table prefix
+ $this->_tbl = '#__' . $this->_tableName;
+ }
+
+ $key = $this->_tbl_key;
+
+ if (empty($key) && !empty($this->_tbl_keys))
+ {
+ $key = $this->_tbl_keys;
+ }
+
+ // Keep checking _tbl_key for standard defined tables
+ if (empty($key) && !empty($this->_tableKey))
+ {
+ $this->_tbl_key = $this->_tableKey;
+ $key = $this->_tbl_key;
+ }
+
+ if (empty($this->_tbl) || empty($key))
+ {
+ throw new UnexpectedValueException(sprintf('Missing data to initialize %s table | id: %s', $this->_tbl, $key));
+ }
+
+ parent::__construct($this->_tbl, $key, $db);
+ }
+
+ /**
+ * Method to bind an associative array or object to the JTable instance.This
+ * method only binds properties that are publicly accessible and optionally
+ * takes an array of properties to ignore when binding.
+ *
+ * @param mixed $src An associative array or object to bind to the JTable instance.
+ * @param mixed $ignore An optional array or space separated list of properties to ignore while binding.
+ *
+ * @return boolean True on success.
+ *
+ * @throws InvalidArgumentException
+ */
+ public function bind($src, $ignore = array())
+ {
+ if (isset($src['params']) && is_array($src['params']))
+ {
+ $registry = new JRegistry;
+ $registry->loadArray($src['params']);
+ $src['params'] = (string) $registry;
+ }
+
+ if (isset($src['metadata']) && is_array($src['metadata']))
+ {
+ $registry = new JRegistry;
+ $registry->loadArray($src['metadata']);
+ $src['metadata'] = (string) $registry;
+ }
+
+ if (isset($src['rules']) && is_array($src['rules']))
+ {
+ $rules = new JAccessRules($src['rules']);
+ $this->setRules($rules);
+ }
+
+ return parent::bind($src, $ignore);
+ }
+
+ /**
+ * Import the plugin types.
+ *
+ * @return void
+ */
+ private function importPluginTypes()
+ {
+ foreach ($this->_pluginTypesToImport as $type)
+ {
+ JPluginHelper::importPlugin($type);
+ }
+ }
+
+ /**
+ * Called before load().
+ *
+ * @param mixed $keys An optional primary key value to load the row by, or an array of fields to match. If not
+ * set the instance property value is used.
+ * @param boolean $reset True to reset the default values before loading the new row.
+ *
+ * @return boolean True if successful. False if row not found.
+ */
+ protected function beforeLoad($keys = null, $reset = true)
+ {
+ if ($this->_eventBeforeLoad)
+ {
+ // Import the plugin types
+ $this->importPluginTypes();
+
+ // Trigger the event
+ $results = RedshopHelperUtility::getDispatcher()
+ ->trigger($this->_eventBeforeLoad, array($this, $keys, $reset));
+
+ if (count($results) && in_array(false, $results, true))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Called after load().
+ *
+ * @param mixed $keys An optional primary key value to load the row by, or an array of fields to match. If not
+ * set the instance property value is used.
+ * @param boolean $reset True to reset the default values before loading the new row.
+ *
+ * @return boolean True if successful. False if row not found.
+ */
+ protected function afterLoad($keys = null, $reset = true)
+ {
+ if ($this->_eventAfterLoad)
+ {
+ // Import the plugin types
+ $this->importPluginTypes();
+
+ // Trigger the event
+ $results = JFactory::getDispatcher()
+ ->trigger($this->_eventAfterLoad, array($this, $keys, $reset));
+
+ if (count($results) && in_array(false, $results, true))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Method to load a row from the database by primary key and bind the fields
+ * to the JTable instance properties.
+ *
+ * @param mixed $keys An optional primary key value to load the row by, or an array of fields to match. If not
+ * set the instance property value is used.
+ * @param boolean $reset True to reset the default values before loading the new row.
+ *
+ * @return boolean True if successful. False if row not found.
+ */
+ public function load($keys = null, $reset = true)
+ {
+ // Before load
+ if (!$this->beforeLoad($keys, $reset))
+ {
+ return false;
+ }
+
+ // Load
+ if (!parent::load($keys, $reset))
+ {
+ return false;
+ }
+
+ // After load
+ if (!$this->afterLoad($keys, $reset))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Called before delete().
+ *
+ * @param integer $pk The primary key of the node to delete.
+ * @param boolean $children True to delete child nodes, false to move them up a level.
+ *
+ * @return boolean True on success.
+ */
+ protected function beforeDelete($pk = null, $children = true)
+ {
+ if ($this->_eventBeforeDelete)
+ {
+ // Import the plugin types
+ $this->importPluginTypes();
+
+ // Trigger the event
+ $results = JFactory::getDispatcher()
+ ->trigger($this->_eventBeforeDelete, array($this, $pk, $children));
+
+ if (count($results) && in_array(false, $results, true))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Called after delete().
+ *
+ * @param integer $pk The primary key of the node to delete.
+ * @param boolean $children True to delete child nodes, false to move them up a level.
+ *
+ * @return boolean True on success.
+ */
+ protected function afterDelete($pk = null, $children = true)
+ {
+ // Trigger after delete
+ if ($this->_eventAfterDelete)
+ {
+ // Import the plugin types
+ $this->importPluginTypes();
+
+ // Trigger the event
+ $results = JFactory::getDispatcher()
+ ->trigger($this->_eventAfterDelete, array($this, $pk, $children));
+
+ if (count($results) && in_array(false, $results, true))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Called delete().
+ *
+ * @param integer $pk The primary key of the node to delete.
+ * @param boolean $children True to delete child nodes, false to move them up a level.
+ *
+ * @return boolean True on success.
+ */
+ protected function doDelete($pk = null, $children = true)
+ {
+ return parent::delete($pk, $children);
+ }
+
+ /**
+ * Method to delete a node and, optionally, its child nodes from the table.
+ *
+ * @param integer $pk The primary key of the node to delete.
+ * @param boolean $children True to delete child nodes, false to move them up a level.
+ *
+ * @return boolean True on success.
+ */
+ public function delete($pk = null, $children = true)
+ {
+ // Before delete
+ if (!$this->beforeDelete($pk, $children))
+ {
+ return false;
+ }
+
+ // Delete
+ if (!$this->doDelete($pk, $children))
+ {
+ return false;
+ }
+
+ // After delete
+ if (!$this->afterDelete($pk, $children))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Called before check().
+ *
+ * @return boolean True if all checks pass.
+ */
+ protected function beforeCheck()
+ {
+ if ($this->_eventBeforeCheck)
+ {
+ // Import the plugin types
+ $this->importPluginTypes();
+
+ // Trigger the event
+ $results = JFactory::getDispatcher()
+ ->trigger($this->_eventBeforeCheck, array($this));
+
+ if (count($results) && in_array(false, $results, true))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Called after check().
+ *
+ * @return boolean True if all checks pass.
+ */
+ protected function afterCheck()
+ {
+ // Trigger after check
+ if ($this->_eventAfterCheck)
+ {
+ // Import the plugin types
+ $this->importPluginTypes();
+
+ // Trigger the event
+ $results = JFactory::getDispatcher()
+ ->trigger($this->_eventAfterCheck, array($this));
+
+ if (count($results) && in_array(false, $results, true))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Checks that the object is valid and able to be stored.
+ *
+ * This method checks that the parent_id is non-zero and exists in the database.
+ * Note that the root node (parent_id = 0) cannot be manipulated with this class.
+ *
+ * @return boolean True if all checks pass.
+ */
+ public function check()
+ {
+ // Before check
+ if (!$this->beforeCheck())
+ {
+ return false;
+ }
+
+ // Check
+ if (!parent::check())
+ {
+ return false;
+ }
+
+ // After check
+ if (!$this->afterCheck())
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Called before store().
+ *
+ * @param boolean $updateNulls True to update null values as well.
+ *
+ * @return boolean True on success.
+ */
+ protected function beforeStore($updateNulls = false)
+ {
+ if ($this->_eventBeforeStore)
+ {
+ // Import the plugin types
+ $this->importPluginTypes();
+
+ // Trigger the event
+ $results = JFactory::getDispatcher()
+ ->trigger($this->_eventBeforeStore, array($this, $updateNulls));
+
+ if (count($results) && in_array(false, $results, true))
+ {
+ return false;
+ }
+ }
+
+ // Audit fields optional auto-update (on by default)
+ if ($this->getOption('updateAuditFields', true))
+ {
+ $this->updateAuditFields($this);
+ }
+
+ return true;
+ }
+
+ /**
+ * Called after store().
+ *
+ * @param boolean $updateNulls True to update null values as well.
+ *
+ * @return boolean True on success.
+ */
+ protected function afterStore($updateNulls = false)
+ {
+ if ($this->_eventAfterStore)
+ {
+ // Import the plugin types
+ $this->importPluginTypes();
+
+ // Trigger the event
+ $results = RedshopHelperUtility::getDispatcher()
+ ->trigger($this->_eventAfterStore, array($this, $updateNulls));
+
+ if (count($results) && in_array(false, $results, true))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Method to store a node in the database table.
+ *
+ * @param boolean $updateNulls True to update null values as well.
+ *
+ * @return boolean True on success.
+ */
+ public function store($updateNulls = false)
+ {
+ // Before store
+ if (!$this->beforeStore($updateNulls))
+ {
+ return false;
+ }
+
+ // Store
+ if (!parent::store($updateNulls))
+ {
+ return false;
+ }
+
+ // After store
+ if (!$this->afterStore($updateNulls))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Override the parent checkin method to set checked_out = null instead of 0 so the foreign key doesn't fail.
+ *
+ * @param mixed $pk An optional primary key value to check out. If not set the instance property value is used.
+ *
+ * @return boolean True on success.
+ *
+ * @throws UnexpectedValueException
+ */
+ public function checkIn($pk = null)
+ {
+ // If there is no checked_out or checked_out_time field, just return true.
+ if (!property_exists($this, 'checked_out') || !property_exists($this, 'checked_out_time'))
+ {
+ return true;
+ }
+
+ $k = $this->_tbl_key;
+ $pk = (is_null($pk)) ? $this->$k : $pk;
+
+ // If no primary key is given, return false.
+ if ($pk === null)
+ {
+ throw new UnexpectedValueException('Null primary key not allowed.');
+ }
+
+ // Check the row in by primary key.
+ $query = $this->_db->getQuery(true);
+ $query->update($this->_tbl);
+ $query->set($this->_db->quoteName('checked_out') . ' = NULL');
+ $query->set($this->_db->quoteName('checked_out_time') . ' = ' . $this->_db->quote($this->_db->getNullDate()));
+ $query->where($this->_tbl_key . ' = ' . $this->_db->quote($pk));
+ $this->_db->setQuery($query);
+
+ // Check for a database error.
+ $this->_db->execute();
+
+ // Set table values in the object.
+ $this->checked_out = null;
+ $this->checked_out_time = '';
+
+ return true;
+ }
+
+ /**
+ * Method to set the publishing state for a row or list of rows in the database
+ * table. The method respects checked out rows by other users and will attempt
+ * to checkin rows that it can after adjustments are made.
+ *
+ * @param mixed $pks An optional array of primary key values to update.
+ * If not set the instance property value is used.
+ * @param integer $state The publishing state. eg. [0 = unpublished, 1 = published]
+ * @param integer $userId The user id of the user performing the operation.
+ *
+ * @return boolean True on success; false if $pks is empty.
+ */
+ public function publish($pks = null, $state = 1, $userId = 0)
+ {
+ // Use an easy to handle variable for database
+ $db = $this->_db;
+
+ // Initialise variables.
+ $k = $this->_tbl_key;
+
+ // Sanitize input.
+ JArrayHelper::toInteger($pks);
+ $userId = (int) $userId;
+ $state = (int) $state;
+
+ // If there are no primary keys set check to see if the instance key is set.
+ if (empty($pks))
+ {
+ if ($this->$k)
+ {
+ $pks = array($this->$k);
+ }
+
+ // Nothing to set publishing state on, return false.
+ else
+ {
+ $this->setError(JText::_('JLIB_DATABASE_ERROR_NO_ROWS_SELECTED'));
+
+ return false;
+ }
+ }
+
+ // Build the main update query
+ $query = $db->getQuery(true)
+ ->update($db->quoteName($this->_tbl))
+ ->set($db->quoteName($this->_tableFieldState) . ' = ' . (int) $state)
+ ->where($db->quoteName($k) . '=' . implode(' OR ' . $db->quoteName($k) . '=', $pks));
+
+ // Determine if there is checkin support for the table.
+ if (!property_exists($this, 'checked_out') || !property_exists($this, 'checked_out_time'))
+ {
+ $checkin = false;
+ }
+ else
+ {
+ $query->where('(checked_out = 0 OR checked_out IS NULL OR checked_out = ' . (int) $userId . ')');
+ $checkin = true;
+ }
+
+ // Update the publishing state for rows with the given primary keys.
+ $db->setQuery($query);
+
+ try
+ {
+ $db->query();
+ }
+ catch (RuntimeException $e)
+ {
+ JError::raiseWarning(500, $e->getMessage());
+
+ return false;
+ }
+
+ // If checkin is supported and all rows were adjusted, check them in.
+ if ($checkin && (count($pks) == $this->_db->getAffectedRows()))
+ {
+ // Checkin the rows.
+ foreach ($pks as $pk)
+ {
+ $this->checkin($pk);
+ }
+ }
+
+ // If the JTable instance value is in the list of primary keys that were set, set the instance.
+ if (in_array($this->$k, $pks))
+ {
+ $this->{$this->_tableFieldState} = $state;
+ }
+
+ $this->setError('');
+
+ return true;
+ }
+
+ /**
+ * Set a table option value.
+ *
+ * @param string $key The key
+ * @param mixed $val The default value
+ *
+ * @return JTable
+ */
+ public function setOption($key, $val)
+ {
+ $this->_options[$key] = $val;
+
+ return $this;
+ }
+
+ /**
+ * Get a table option value.
+ *
+ * @param string $key The key
+ * @param mixed $default The default value
+ *
+ * @return mixed The value or the default value
+ */
+ public function getOption($key, $default = null)
+ {
+ if (isset($this->_options[$key]))
+ {
+ return $this->_options[$key];
+ }
+
+ return $default;
+ }
+
+ /**
+ * Validate that the primary key has been set.
+ *
+ * @return boolean True if the primary key(s) have been set.
+ *
+ * @since 1.5.2
+ */
+ public function hasPrimaryKey()
+ {
+ // For Joomla 3.2+ a native method has been provided
+ if (method_exists(get_parent_class(), 'hasPrimaryKey'))
+ {
+ return parent::hasPrimaryKey();
+ }
+
+ // Otherwise, it checks if the only key field compatible for older Joomla versions is set or not
+ if (isset($this->_tbl_key) && !empty($this->_tbl_key) && empty($this->{$this->_tbl_key}))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Method to update audit fields using a static function, to reuse in non-children classes like RNestedTable
+ *
+ * @param self $tableInstance Table instance
+ *
+ * @return void
+ *
+ * @since 2.0.6
+ */
+ public function updateAuditFields(&$tableInstance)
+ {
+ $tableFieldCreatedBy = $tableInstance->get('_tableFieldCreatedBy');
+ $tableFieldCreatedDate = $tableInstance->get('_tableFieldCreatedDate');
+ $tableFieldModifiedBy = $tableInstance->get('_tableFieldModifiedBy');
+ $tableFieldModifiedDate = $tableInstance->get('_tableFieldModifiedDate');
+ $auditDateFormat = $tableInstance->get('_auditDateFormat');
+
+ // Optional created_by field updated when present
+ if (!$tableInstance->hasPrimaryKey() && property_exists($tableInstance, $tableFieldCreatedBy))
+ {
+ $user = JFactory::getUser();
+
+ if ($user->id)
+ {
+ $tableInstance->{$tableFieldCreatedBy} = $user->id;
+ }
+ else
+ {
+ $tableInstance->{$tableFieldCreatedBy} = null;
+ }
+ }
+
+ // Optional created_date field updated when present
+ if (!$tableInstance->hasPrimaryKey() && property_exists($tableInstance, $tableFieldCreatedDate))
+ {
+ $tableInstance->{$tableFieldCreatedDate} = JFactory::getDate()->format($auditDateFormat);
+ }
+
+ // Optional modified_by field updated when present
+ if (property_exists($tableInstance, $tableFieldModifiedBy))
+ {
+ if (!isset($user))
+ {
+ $user = JFactory::getUser();
+ }
+
+ if ($user->id)
+ {
+ $tableInstance->{$tableFieldModifiedBy} = $user->id;
+ }
+ else
+ {
+ $tableInstance->{$tableFieldModifiedBy} = null;
+ }
+ }
+
+ // Optional modified_date field updated when present
+ if (property_exists($tableInstance, $tableFieldModifiedDate))
+ {
+ $tableInstance->{$tableFieldModifiedDate} = JFactory::getDate()->format($auditDateFormat);
+ }
+ }
+}
\ No newline at end of file