Файловый менеджер - Редактировать - /home/lmsyaran/public_html/pusher/Table.zip
Назад
PK �V�[��~a a Asset.phpnu �[��� <?php /** * Joomla! Content Management System * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Table; defined('JPATH_PLATFORM') or die; /** * Table class supporting modified pre-order tree traversal behavior. * * @since 1.7.0 */ class Asset extends Nested { /** * The primary key of the asset. * * @var integer * @since 1.7.0 */ public $id = null; /** * The unique name of the asset. * * @var string * @since 1.7.0 */ public $name = null; /** * The human readable title of the asset. * * @var string * @since 1.7.0 */ public $title = null; /** * The rules for the asset stored in a JSON string * * @var string * @since 1.7.0 */ public $rules = null; /** * Constructor * * @param \JDatabaseDriver $db Database driver object. * * @since 1.7.0 */ public function __construct($db) { parent::__construct('#__assets', 'id', $db); } /** * Method to load an asset by its name. * * @param string $name The name of the asset. * * @return integer * * @since 1.7.0 */ public function loadByName($name) { return $this->load(array('name' => $name)); } /** * Assert that the nested set data is valid. * * @return boolean True if the instance is sane and able to be stored in the database. * * @since 1.7.0 */ public function check() { $this->parent_id = (int) $this->parent_id; if (empty($this->rules)) { $this->rules = '{}'; } // Nested does not allow parent_id = 0, override this. if ($this->parent_id > 0) { // Get the \JDatabaseQuery object $query = $this->_db->getQuery(true) ->select('1') ->from($this->_db->quoteName($this->_tbl)) ->where($this->_db->quoteName('id') . ' = ' . $this->parent_id); if ($this->_db->setQuery($query, 0, 1)->loadResult()) { return true; } $this->setError(\JText::_('JLIB_DATABASE_ERROR_INVALID_PARENT_ID')); return false; } return true; } /** * Method to recursively rebuild the whole nested set tree. * * @param integer $parentId The root of the tree to rebuild. * @param integer $leftId The left id to start with in building the tree. * @param integer $level The level to assign to the current nodes. * @param string $path The path to the current nodes. * * @return integer 1 + value of root rgt on success, false on failure * * @since 3.5 * @throws \RuntimeException on database error. */ public function rebuild($parentId = null, $leftId = 0, $level = 0, $path = null) { // If no parent is provided, try to find it. if ($parentId === null) { // Get the root item. $parentId = $this->getRootId(); if ($parentId === false) { return false; } } $query = $this->_db->getQuery(true); // Build the structure of the recursive query. if (!isset($this->_cache['rebuild.sql'])) { $query->clear() ->select($this->_tbl_key) ->from($this->_tbl) ->where('parent_id = %d'); // If the table has an ordering field, use that for ordering. if (property_exists($this, 'ordering')) { $query->order('parent_id, ordering, lft'); } else { $query->order('parent_id, lft'); } $this->_cache['rebuild.sql'] = (string) $query; } // Make a shortcut to database object. // Assemble the query to find all children of this node. $this->_db->setQuery(sprintf($this->_cache['rebuild.sql'], (int) $parentId)); $children = $this->_db->loadObjectList(); // The right value of this node is the left value + 1 $rightId = $leftId + 1; // Execute this function recursively over all children foreach ($children as $node) { /* * $rightId is the current right value, which is incremented on recursion return. * Increment the level for the children. * Add this item's alias to the path (but avoid a leading /) */ $rightId = $this->rebuild($node->{$this->_tbl_key}, $rightId, $level + 1); // If there is an update failure, return false to break out of the recursion. if ($rightId === false) { return false; } } // We've got the left value, and now that we've processed // the children of this node we also know the right value. $query->clear() ->update($this->_tbl) ->set('lft = ' . (int) $leftId) ->set('rgt = ' . (int) $rightId) ->set('level = ' . (int) $level) ->where($this->_tbl_key . ' = ' . (int) $parentId); $this->_db->setQuery($query)->execute(); // Return the right value of this node + 1. return $rightId + 1; } } PK �V�[L�fK Category.phpnu �[��� <?php /** * Joomla! Content Management System * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Table; defined('JPATH_PLATFORM') or die; use Joomla\CMS\Access\Rules; use Joomla\CMS\Application\ApplicationHelper; use Joomla\CMS\Table\Observer\ContentHistory; use Joomla\CMS\Table\Observer\Tags; use Joomla\Registry\Registry; /** * Category table * * @since 1.5 */ class Category extends Nested { /** * Constructor * * @param \JDatabaseDriver $db Database driver object. * * @since 1.5 */ public function __construct(\JDatabaseDriver $db) { parent::__construct('#__categories', 'id', $db); Tags::createObserver($this, array('typeAlias' => '{extension}.category')); ContentHistory::createObserver($this, array('typeAlias' => '{extension}.category')); $this->access = (int) \JFactory::getConfig()->get('access'); } /** * Method to compute the default name of the asset. * The default name is in the form table_name.id * where id is the value of the primary key of the table. * * @return string * * @since 1.6 */ protected function _getAssetName() { $k = $this->_tbl_key; return $this->extension . '.category.' . (int) $this->$k; } /** * Method to return the title to use for the asset table. * * @return string * * @since 1.6 */ protected function _getAssetTitle() { return $this->title; } /** * Get the parent asset id for the record * * @param Table $table A JTable object for the asset parent. * @param integer $id The id for the asset * * @return integer The id of the asset's parent * * @since 1.6 */ protected function _getAssetParentId(Table $table = null, $id = null) { $assetId = null; // This is a category under a category. if ($this->parent_id > 1) { // Build the query to get the asset id for the parent category. $query = $this->_db->getQuery(true) ->select($this->_db->quoteName('asset_id')) ->from($this->_db->quoteName('#__categories')) ->where($this->_db->quoteName('id') . ' = ' . $this->parent_id); // Get the asset id from the database. $this->_db->setQuery($query); if ($result = $this->_db->loadResult()) { $assetId = (int) $result; } } // This is a category that needs to parent with the extension. elseif ($assetId === null) { // Build the query to get the asset id for the parent category. $query = $this->_db->getQuery(true) ->select($this->_db->quoteName('id')) ->from($this->_db->quoteName('#__assets')) ->where($this->_db->quoteName('name') . ' = ' . $this->_db->quote($this->extension)); // Get the asset id from the database. $this->_db->setQuery($query); if ($result = $this->_db->loadResult()) { $assetId = (int) $result; } } // Return the asset id. if ($assetId) { return $assetId; } else { return parent::_getAssetParentId($table, $id); } } /** * Override check function * * @return boolean * * @see Table::check() * @since 1.5 */ public function check() { // Check for a title. if (trim($this->title) == '') { $this->setError(\JText::_('JLIB_DATABASE_ERROR_MUSTCONTAIN_A_TITLE_CATEGORY')); return false; } $this->alias = trim($this->alias); if (empty($this->alias)) { $this->alias = $this->title; } $this->alias = ApplicationHelper::stringURLSafe($this->alias, $this->language); if (trim(str_replace('-', '', $this->alias)) == '') { $this->alias = \JFactory::getDate()->format('Y-m-d-H-i-s'); } return true; } /** * Overloaded bind function. * * @param array $array named array * @param string $ignore An optional array or space separated list of properties * to ignore while binding. * * @return mixed Null if operation was satisfactory, otherwise returns an error * * @see Table::bind() * @since 1.6 */ public function bind($array, $ignore = '') { if (isset($array['params']) && is_array($array['params'])) { $registry = new Registry($array['params']); $array['params'] = (string) $registry; } if (isset($array['metadata']) && is_array($array['metadata'])) { $registry = new Registry($array['metadata']); $array['metadata'] = (string) $registry; } // Bind the rules. if (isset($array['rules']) && is_array($array['rules'])) { $rules = new Rules($array['rules']); $this->setRules($rules); } return parent::bind($array, $ignore); } /** * Overridden Table::store to set created/modified and user id. * * @param boolean $updateNulls True to update fields even if they are null. * * @return boolean True on success. * * @since 1.6 */ public function store($updateNulls = false) { $date = \JFactory::getDate(); $user = \JFactory::getUser(); $this->modified_time = $date->toSql(); if ($this->id) { // Existing category $this->modified_user_id = $user->get('id'); } else { // New category. A category created_time and created_user_id field can be set by the user, // so we don't touch either of these if they are set. if (!(int) $this->created_time) { $this->created_time = $date->toSql(); } if (empty($this->created_user_id)) { $this->created_user_id = $user->get('id'); } } // Verify that the alias is unique $table = Table::getInstance('Category', 'JTable', array('dbo' => $this->getDbo())); if ($table->load(array('alias' => $this->alias, 'parent_id' => (int) $this->parent_id, 'extension' => $this->extension)) && ($table->id != $this->id || $this->id == 0)) { $this->setError(\JText::_('JLIB_DATABASE_ERROR_CATEGORY_UNIQUE_ALIAS')); return false; } return parent::store($updateNulls); } } PK �V�[;�1�# # Content.phpnu �[��� <?php /** * Joomla! Content Management System * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Table; defined('JPATH_PLATFORM') or die; use Joomla\CMS\Access\Access; use Joomla\CMS\Access\Rules; use Joomla\CMS\Application\ApplicationHelper; use Joomla\CMS\Table\Observer\Tags; use Joomla\CMS\Table\Observer\ContentHistory as ContentHistoryObserver; use Joomla\Registry\Registry; use Joomla\String\StringHelper; /** * Content table * * @since 1.5 * @deprecated 3.1.4 Class will be removed upon completion of transition to UCM */ class Content extends Table { /** * Constructor * * @param \JDatabaseDriver $db A database connector object * * @since 1.5 * @deprecated 3.1.4 Class will be removed upon completion of transition to UCM */ public function __construct(\JDatabaseDriver $db) { parent::__construct('#__content', 'id', $db); Tags::createObserver($this, array('typeAlias' => 'com_content.article')); ContentHistoryObserver::createObserver($this, array('typeAlias' => 'com_content.article')); // Set the alias since the column is called state $this->setColumnAlias('published', 'state'); } /** * Method to compute the default name of the asset. * The default name is in the form table_name.id * where id is the value of the primary key of the table. * * @return string * * @since 1.6 * @deprecated 3.1.4 Class will be removed upon completion of transition to UCM */ protected function _getAssetName() { $k = $this->_tbl_key; return 'com_content.article.' . (int) $this->$k; } /** * Method to return the title to use for the asset table. * * @return string * * @since 1.6 * @deprecated 3.1.4 Class will be removed upon completion of transition to UCM */ protected function _getAssetTitle() { return $this->title; } /** * Method to get the parent asset id for the record * * @param Table $table A Table object (optional) for the asset parent * @param integer $id The id (optional) of the content. * * @return integer * * @since 1.6 * @deprecated 3.1.4 Class will be removed upon completion of transition to UCM */ protected function _getAssetParentId(Table $table = null, $id = null) { $assetId = null; // This is an article under a category. if ($this->catid) { // Build the query to get the asset id for the parent category. $query = $this->_db->getQuery(true) ->select($this->_db->quoteName('asset_id')) ->from($this->_db->quoteName('#__categories')) ->where($this->_db->quoteName('id') . ' = ' . (int) $this->catid); // Get the asset id from the database. $this->_db->setQuery($query); if ($result = $this->_db->loadResult()) { $assetId = (int) $result; } } // Return the asset id. if ($assetId) { return $assetId; } else { return parent::_getAssetParentId($table, $id); } } /** * Overloaded bind function * * @param array $array Named array * @param mixed $ignore An optional array or space separated list of properties * to ignore while binding. * * @return mixed Null if operation was satisfactory, otherwise returns an error string * * @see Table::bind() * @since 1.6 * @deprecated 3.1.4 Class will be removed upon completion of transition to UCM */ public function bind($array, $ignore = '') { // Search for the {readmore} tag and split the text up accordingly. if (isset($array['articletext'])) { $pattern = '#<hr\s+id=("|\')system-readmore("|\')\s*\/*>#i'; $tagPos = preg_match($pattern, $array['articletext']); if ($tagPos == 0) { $this->introtext = $array['articletext']; $this->fulltext = ''; } else { list ($this->introtext, $this->fulltext) = preg_split($pattern, $array['articletext'], 2); } } if (isset($array['attribs']) && is_array($array['attribs'])) { $registry = new Registry($array['attribs']); $array['attribs'] = (string) $registry; } if (isset($array['metadata']) && is_array($array['metadata'])) { $registry = new Registry($array['metadata']); $array['metadata'] = (string) $registry; } // Bind the rules. if (isset($array['rules']) && is_array($array['rules'])) { $rules = new Rules($array['rules']); $this->setRules($rules); } return parent::bind($array, $ignore); } /** * Overloaded check function * * @return boolean True on success, false on failure * * @see Table::check() * @since 1.5 * @deprecated 3.1.4 Class will be removed upon completion of transition to UCM */ public function check() { if (trim($this->title) == '') { $this->setError(\JText::_('COM_CONTENT_WARNING_PROVIDE_VALID_NAME')); return false; } if (trim($this->alias) == '') { $this->alias = $this->title; } $this->alias = ApplicationHelper::stringURLSafe($this->alias, $this->language); if (trim(str_replace('-', '', $this->alias)) == '') { $this->alias = \JFactory::getDate()->format('Y-m-d-H-i-s'); } if (trim(str_replace(' ', '', $this->fulltext)) == '') { $this->fulltext = ''; } /** * Ensure any new items have compulsory fields set. This is needed for things like * frontend editing where we don't show all the fields or using some kind of API */ if (!$this->id) { // Images can be an empty json string if (!isset($this->images)) { $this->images = '{}'; } // URLs can be an empty json string if (!isset($this->urls)) { $this->urls = '{}'; } // Attributes (article params) can be an empty json string if (!isset($this->attribs)) { $this->attribs = '{}'; } // Metadata can be an empty json string if (!isset($this->metadata)) { $this->metadata = '{}'; } } // Check the publish down date is not earlier than publish up. if ($this->publish_down < $this->publish_up && $this->publish_down > $this->_db->getNullDate()) { // Swap the dates. $temp = $this->publish_up; $this->publish_up = $this->publish_down; $this->publish_down = $temp; } // Clean up keywords -- eliminate extra spaces between phrases // and cr (\r) and lf (\n) characters from string if (!empty($this->metakey)) { // Only process if not empty // Array of characters to remove $bad_characters = array("\n", "\r", "\"", '<', '>'); // Remove bad characters $after_clean = StringHelper::str_ireplace($bad_characters, '', $this->metakey); // Create array using commas as delimiter $keys = explode(',', $after_clean); $clean_keys = array(); foreach ($keys as $key) { if (trim($key)) { // Ignore blank keywords $clean_keys[] = trim($key); } } // Put array back together delimited by ", " $this->metakey = implode(', ', $clean_keys); } return true; } /** * Gets the default asset values for a component. * * @param string $component The component asset name to search for * * @return Rules The Rules object for the asset * * @since 3.4 * @deprecated 3.4 Class will be removed upon completion of transition to UCM */ protected function getDefaultAssetValues($component) { // Need to find the asset id by the name of the component. $db = $this->getDbo(); $query = $db->getQuery(true) ->select($db->quoteName('id')) ->from($db->quoteName('#__assets')) ->where($db->quoteName('name') . ' = ' . $db->quote($component)); $db->setQuery($query); $assetId = (int) $db->loadResult(); return Access::getAssetRules($assetId); } /** * Overrides Table::store to set modified data and user id. * * @param boolean $updateNulls True to update fields even if they are null. * * @return boolean True on success. * * @since 1.6 * @deprecated 3.1.4 Class will be removed upon completion of transition to UCM */ public function store($updateNulls = false) { $date = \JFactory::getDate(); $user = \JFactory::getUser(); $this->modified = $date->toSql(); if ($this->id) { // Existing item $this->modified_by = $user->get('id'); } else { // New article. An article created and created_by field can be set by the user, // so we don't touch either of these if they are set. if (!(int) $this->created) { $this->created = $date->toSql(); } if (empty($this->created_by)) { $this->created_by = $user->get('id'); } } // Verify that the alias is unique $table = Table::getInstance('Content', 'JTable', array('dbo' => $this->getDbo())); if ($table->load(array('alias' => $this->alias, 'catid' => $this->catid)) && ($table->id != $this->id || $this->id == 0)) { $this->setError(\JText::_('JLIB_DATABASE_ERROR_ARTICLE_UNIQUE_ALIAS')); return false; } return parent::store($updateNulls); } } PK �V�[��%�_ _ ContentHistory.phpnu �[��� <?php /** * Joomla! Content Management System * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Table; defined('JPATH_PLATFORM') or die; /** * Content History table. * * @since 3.2 */ class ContentHistory extends Table { /** * Array of object fields to unset from the data object before calculating SHA1 hash. This allows us to detect a meaningful change * in the database row using the hash. This can be read from the #__content_types content_history_options column. * * @var array * @since 3.2 */ public $ignoreChanges = array(); /** * Array of object fields to convert to integers before calculating SHA1 hash. Some values are stored differently * when an item is created than when the item is changed and saved. This works around that issue. * This can be read from the #__content_types content_history_options column. * * @var array * @since 3.2 */ public $convertToInt = array(); /** * Constructor * * @param \JDatabaseDriver $db A database connector object * * @since 3.1 */ public function __construct($db) { parent::__construct('#__ucm_history', 'version_id', $db); $this->ignoreChanges = array( 'modified_by', 'modified_user_id', 'modified', 'modified_time', 'checked_out', 'checked_out_time', 'version', 'hits', 'path', ); $this->convertToInt = array('publish_up', 'publish_down', 'ordering', 'featured'); } /** * Overrides Table::store to set modified hash, user id, and save date. * * @param boolean $updateNulls True to update fields even if they are null. * * @return boolean True on success. * * @since 3.2 */ public function store($updateNulls = false) { $this->set('character_count', strlen($this->get('version_data'))); $typeTable = Table::getInstance('ContentType', 'JTable', array('dbo' => $this->getDbo())); $typeTable->load($this->ucm_type_id); if (!isset($this->sha1_hash)) { $this->set('sha1_hash', $this->getSha1($this->get('version_data'), $typeTable)); } // Modify author and date only when not toggling Keep Forever if ($this->get('keep_forever') === null) { $this->set('editor_user_id', \JFactory::getUser()->id); $this->set('save_date', \JFactory::getDate()->toSql()); } return parent::store($updateNulls); } /** * Utility method to get the hash after removing selected values. This lets us detect changes other than * modified date (which will change on every save). * * @param mixed $jsonData Either an object or a string with json-encoded data * @param ContentType $typeTable Table object with data for this content type * * @return string SHA1 hash on success. Empty string on failure. * * @since 3.2 */ public function getSha1($jsonData, ContentType $typeTable) { $object = is_object($jsonData) ? $jsonData : json_decode($jsonData); if (isset($typeTable->content_history_options) && is_object(json_decode($typeTable->content_history_options))) { $options = json_decode($typeTable->content_history_options); $this->ignoreChanges = isset($options->ignoreChanges) ? $options->ignoreChanges : $this->ignoreChanges; $this->convertToInt = isset($options->convertToInt) ? $options->convertToInt : $this->convertToInt; } foreach ($this->ignoreChanges as $remove) { if (property_exists($object, $remove)) { unset($object->$remove); } } // Convert integers, booleans, and nulls to strings to get a consistent hash value foreach ($object as $name => $value) { if (is_object($value)) { // Go one level down for JSON column values foreach ($value as $subName => $subValue) { $object->$subName = is_int($subValue) || is_bool($subValue) || $subValue === null ? (string) $subValue : $subValue; } } else { $object->$name = is_int($value) || is_bool($value) || $value === null ? (string) $value : $value; } } // Work around empty values foreach ($this->convertToInt as $convert) { if (isset($object->$convert)) { $object->$convert = (int) $object->$convert; } } if (isset($object->review_time)) { $object->review_time = (int) $object->review_time; } return sha1(json_encode($object)); } /** * Utility method to get a matching row based on the hash value and id columns. * This lets us check to make sure we don't save duplicate versions. * * @return string SHA1 hash on success. Empty string on failure. * * @since 3.2 */ public function getHashMatch() { $db = $this->_db; $query = $db->getQuery(true); $query->select('*') ->from($db->quoteName('#__ucm_history')) ->where($db->quoteName('ucm_item_id') . ' = ' . (int) $this->get('ucm_item_id')) ->where($db->quoteName('ucm_type_id') . ' = ' . (int) $this->get('ucm_type_id')) ->where($db->quoteName('sha1_hash') . ' = ' . $db->quote($this->get('sha1_hash'))); $db->setQuery($query, 0, 1); return $db->loadObject(); } /** * Utility method to remove the oldest versions of an item, saving only the most recent versions. * * @param integer $maxVersions The maximum number of versions to save. All others will be deleted. * * @return boolean true on success, false on failure. * * @since 3.2 */ public function deleteOldVersions($maxVersions) { $result = true; // Get the list of version_id values we want to save $db = $this->_db; $query = $db->getQuery(true); $query->select($db->quoteName('version_id')) ->from($db->quoteName('#__ucm_history')) ->where($db->quoteName('ucm_item_id') . ' = ' . (int) $this->get('ucm_item_id')) ->where($db->quoteName('ucm_type_id') . ' = ' . (int) $this->get('ucm_type_id')) ->where($db->quoteName('keep_forever') . ' != 1') ->order($db->quoteName('save_date') . ' DESC '); $db->setQuery($query, 0, (int) $maxVersions); $idsToSave = $db->loadColumn(0); // Don't process delete query unless we have at least the maximum allowed versions if (count($idsToSave) === (int) $maxVersions) { // Delete any rows not in our list and and not flagged to keep forever. $query = $db->getQuery(true); $query->delete($db->quoteName('#__ucm_history')) ->where($db->quoteName('ucm_item_id') . ' = ' . (int) $this->get('ucm_item_id')) ->where($db->quoteName('ucm_type_id') . ' = ' . (int) $this->get('ucm_type_id')) ->where($db->quoteName('version_id') . ' NOT IN (' . implode(',', $idsToSave) . ')') ->where($db->quoteName('keep_forever') . ' != 1'); $db->setQuery($query); $result = (boolean) $db->execute(); } return $result; } } PK �V�[�g� ContentType.phpnu �[��� <?php /** * Joomla! Content Management System * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Table; defined('JPATH_PLATFORM') or die; /** * Tags table * * @since 3.1 */ class ContentType extends Table { /** * Constructor * * @param \JDatabaseDriver $db A database connector object * * @since 3.1 */ public function __construct($db) { parent::__construct('#__content_types', 'type_id', $db); } /** * Overloaded check method to ensure data integrity. * * @return boolean True on success. * * @since 3.1 * @throws \UnexpectedValueException */ public function check() { // Check for valid name. if (trim($this->type_title) === '') { throw new \UnexpectedValueException(sprintf('The title is empty')); } $this->type_title = ucfirst($this->type_title); if (empty($this->type_alias)) { throw new \UnexpectedValueException(sprintf('The type_alias is empty')); } return true; } /** * Overridden Table::store. * * @param boolean $updateNulls True to update fields even if they are null. * * @return boolean True on success. * * @since 3.1 */ public function store($updateNulls = false) { // Verify that the alias is unique $table = Table::getInstance('Contenttype', 'JTable', array('dbo' => $this->getDbo())); if ($table->load(array('type_alias' => $this->type_alias)) && ($table->type_id != $this->type_id || $this->type_id == 0)) { $this->setError(\JText::_('COM_TAGS_ERROR_UNIQUE_ALIAS')); return false; } return parent::store($updateNulls); } /** * Method to expand the field mapping * * @param boolean $assoc True to return an associative array. * * @return mixed Array or object with field mappings. Defaults to object. * * @since 3.1 */ public function fieldmapExpand($assoc = true) { return $this->fieldmap = json_decode($this->fieldmappings, $assoc); } /** * Method to get the id given the type alias * * @param string $typeAlias Content type alias (for example, 'com_content.article'). * * @return mixed type_id for this alias if successful, otherwise null. * * @since 3.2 */ public function getTypeId($typeAlias) { $db = $this->_db; $query = $db->getQuery(true); $query->select($db->quoteName('type_id')) ->from($db->quoteName($this->_tbl)) ->where($db->quoteName('type_alias') . ' = ' . $db->quote($typeAlias)); $db->setQuery($query); return $db->loadResult(); } /** * Method to get the Table object for the content type from the table object. * * @return mixed Table object on success, otherwise false. * * @since 3.2 * * @throws \RuntimeException */ public function getContentTable() { $result = false; $tableInfo = json_decode($this->table); if (is_object($tableInfo) && isset($tableInfo->special)) { if (is_object($tableInfo->special) && isset($tableInfo->special->type) && isset($tableInfo->special->prefix)) { $class = isset($tableInfo->special->class) ? $tableInfo->special->class : 'Joomla\\CMS\\Table\\Table'; if (!class_implements($class, 'Joomla\\CMS\\Table\\TableInterface')) { // This isn't an instance of TableInterface. Abort. throw new \RuntimeException('Class must be an instance of Joomla\\CMS\\Table\\TableInterface'); } $result = $class::getInstance($tableInfo->special->type, $tableInfo->special->prefix); } } return $result; } } PK �V�[*��YH* H* CoreContent.phpnu �[��� <?php /** * Joomla! Content Management System * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Table; defined('JPATH_PLATFORM') or die; use Joomla\Registry\Registry; use Joomla\String\StringHelper; use Joomla\Utilities\ArrayHelper; /** * Core content table * * @since 3.1 */ class CoreContent extends Table { /** * Constructor * * @param \JDatabaseDriver $db A database connector object * * @since 3.1 */ public function __construct($db) { parent::__construct('#__ucm_content', 'core_content_id', $db); } /** * Overloaded bind function * * @param array $array Named array * @param mixed $ignore An optional array or space separated list of properties * to ignore while binding. * * @return mixed Null if operation was satisfactory, otherwise returns an error string * * @see Table::bind() * @since 3.1 */ public function bind($array, $ignore = '') { if (isset($array['core_params']) && is_array($array['core_params'])) { $registry = new Registry($array['core_params']); $array['core_params'] = (string) $registry; } if (isset($array['core_metadata']) && is_array($array['core_metadata'])) { $registry = new Registry($array['core_metadata']); $array['core_metadata'] = (string) $registry; } if (isset($array['core_images']) && is_array($array['core_images'])) { $registry = new Registry($array['core_images']); $array['core_images'] = (string) $registry; } if (isset($array['core_urls']) && is_array($array['core_urls'])) { $registry = new Registry($array['core_urls']); $array['core_urls'] = (string) $registry; } if (isset($array['core_body']) && is_array($array['core_body'])) { $registry = new Registry($array['core_body']); $array['core_body'] = (string) $registry; } return parent::bind($array, $ignore); } /** * Overloaded check function * * @return boolean True on success, false on failure * * @see Table::check() * @since 3.1 */ public function check() { if (trim($this->core_title) === '') { $this->setError(\JText::_('JLIB_CMS_WARNING_PROVIDE_VALID_NAME')); return false; } if (trim($this->core_alias) === '') { $this->core_alias = $this->core_title; } $this->core_alias = \JApplicationHelper::stringURLSafe($this->core_alias); if (trim(str_replace('-', '', $this->core_alias)) === '') { $this->core_alias = \JFactory::getDate()->format('Y-m-d-H-i-s'); } // Not Null sanity check if (empty($this->core_images)) { $this->core_images = '{}'; } if (empty($this->core_urls)) { $this->core_urls = '{}'; } // Check the publish down date is not earlier than publish up. if ($this->core_publish_down < $this->core_publish_up && $this->core_publish_down > $this->_db->getNullDate()) { // Swap the dates. $temp = $this->core_publish_up; $this->core_publish_up = $this->core_publish_down; $this->core_publish_down = $temp; } // Clean up keywords -- eliminate extra spaces between phrases // and cr (\r) and lf (\n) characters from string if (!empty($this->core_metakey)) { // Only process if not empty // Array of characters to remove $bad_characters = array("\n", "\r", "\"", '<', '>'); // Remove bad characters $after_clean = StringHelper::str_ireplace($bad_characters, '', $this->core_metakey); // Create array using commas as delimiter $keys = explode(',', $after_clean); $clean_keys = array(); foreach ($keys as $key) { if (trim($key)) { // Ignore blank keywords $clean_keys[] = trim($key); } } // Put array back together delimited by ", " $this->core_metakey = implode(', ', $clean_keys); } return true; } /** * Override JTable delete method to include deleting corresponding row from #__ucm_base. * * @param integer $pk primary key value to delete. Must be set or throws an exception. * * @return boolean True on success. * * @since 3.1 * @throws \UnexpectedValueException */ public function delete($pk = null) { $baseTable = Table::getInstance('Ucm', 'JTable', array('dbo' => $this->getDbo())); return parent::delete($pk) && $baseTable->delete($pk); } /** * Method to delete a row from the #__ucm_content table by content_item_id. * * @param integer $contentItemId value of the core_content_item_id to delete. Corresponds to the primary key of the content table. * @param string $typeAlias Alias for the content type * * @return boolean True on success. * * @since 3.1 * @throws \UnexpectedValueException */ public function deleteByContentId($contentItemId = null, $typeAlias = null) { if ($contentItemId === null || ((int) $contentItemId) === 0) { throw new \UnexpectedValueException('Null content item key not allowed.'); } if ($typeAlias === null) { throw new \UnexpectedValueException('Null type alias not allowed.'); } $db = $this->getDbo(); $query = $db->getQuery(true); $query->select($db->quoteName('core_content_id')) ->from($db->quoteName('#__ucm_content')) ->where($db->quoteName('core_content_item_id') . ' = ' . (int) $contentItemId) ->where($db->quoteName('core_type_alias') . ' = ' . $db->quote($typeAlias)); $db->setQuery($query); if ($ucmId = $db->loadResult()) { return $this->delete($ucmId); } else { return true; } } /** * Overrides Table::store to set modified data and user id. * * @param boolean $updateNulls True to update fields even if they are null. * * @return boolean True on success. * * @since 3.1 */ public function store($updateNulls = false) { $date = \JFactory::getDate(); $user = \JFactory::getUser(); if ($this->core_content_id) { // Existing item $this->core_modified_time = $date->toSql(); $this->core_modified_user_id = $user->get('id'); $isNew = false; } else { // New content item. A content item core_created_time and core_created_user_id field can be set by the user, // so we don't touch either of these if they are set. if (!(int) $this->core_created_time) { $this->core_created_time = $date->toSql(); } if (empty($this->core_created_user_id)) { $this->core_created_user_id = $user->get('id'); } $isNew = true; } $oldRules = $this->getRules(); if (empty($oldRules)) { $this->setRules('{}'); } $result = parent::store($updateNulls); return $result && $this->storeUcmBase($updateNulls, $isNew); } /** * Insert or update row in ucm_base table * * @param boolean $updateNulls True to update fields even if they are null. * @param boolean $isNew if true, need to insert. Otherwise update. * * @return boolean True on success. * * @since 3.1 */ protected function storeUcmBase($updateNulls = false, $isNew = false) { // Store the ucm_base row $db = $this->getDbo(); $query = $db->getQuery(true); $languageId = \JHelperContent::getLanguageId($this->core_language); // Selecting "all languages" doesn't give a language id - we can't store a blank string in non mysql databases, so save 0 (the default value) if (!$languageId) { $languageId = '0'; } if ($isNew) { $query->insert($db->quoteName('#__ucm_base')) ->columns(array($db->quoteName('ucm_id'), $db->quoteName('ucm_item_id'), $db->quoteName('ucm_type_id'), $db->quoteName('ucm_language_id'))) ->values( $db->quote($this->core_content_id) . ', ' . $db->quote($this->core_content_item_id) . ', ' . $db->quote($this->core_type_id) . ', ' . $db->quote($languageId) ); } else { $query->update($db->quoteName('#__ucm_base')) ->set($db->quoteName('ucm_item_id') . ' = ' . $db->quote($this->core_content_item_id)) ->set($db->quoteName('ucm_type_id') . ' = ' . $db->quote($this->core_type_id)) ->set($db->quoteName('ucm_language_id') . ' = ' . $db->quote($languageId)) ->where($db->quoteName('ucm_id') . ' = ' . $db->quote($this->core_content_id)); } $db->setQuery($query); return $db->execute(); } /** * 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. * * @since 3.1 */ public function publish($pks = null, $state = 1, $userId = 0) { $k = $this->_tbl_key; // Sanitize input. $pks = ArrayHelper::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; } } $pksImploded = implode(',', $pks); // Get the JDatabaseQuery object $query = $this->_db->getQuery(true); // Update the publishing state for rows with the given primary keys. $query->update($this->_db->quoteName($this->_tbl)) ->set($this->_db->quoteName('core_state') . ' = ' . (int) $state) ->where($this->_db->quoteName($k) . 'IN (' . $pksImploded . ')'); // Determine if there is checkin support for the table. $checkin = false; if (property_exists($this, 'core_checked_out_user_id') && property_exists($this, 'core_checked_out_time')) { $checkin = true; $query->where( ' (' . $this->_db->quoteName('core_checked_out_user_id') . ' = 0 OR ' . $this->_db->quoteName('core_checked_out_user_id') . ' = ' . (int) $userId . ')' ); } $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()) { // 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->core_state = $state; } $this->setError(''); return true; } } PK �V�["��_b b Extension.phpnu �[��� <?php /** * Joomla! Content Management System * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Table; defined('JPATH_PLATFORM') or die; use Joomla\Registry\Registry; use Joomla\Utilities\ArrayHelper; /** * Extension table * * @since 1.7.0 */ class Extension extends Table { /** * Constructor * * @param \JDatabaseDriver $db Database driver object. * * @since 1.7.0 */ public function __construct($db) { parent::__construct('#__extensions', 'extension_id', $db); // Set the alias since the column is called enabled $this->setColumnAlias('published', 'enabled'); } /** * Overloaded check function * * @return boolean True if the object is ok * * @see Table::check() * @since 1.7.0 */ public function check() { // Check for valid name if (trim($this->name) == '' || trim($this->element) == '') { $this->setError(\JText::_('JLIB_DATABASE_ERROR_MUSTCONTAIN_A_TITLE_EXTENSION')); return false; } if (!$this->extension_id) { if (!$this->custom_data) { $this->custom_data = ''; } if (!$this->system_data) { $this->system_data = ''; } } return true; } /** * Overloaded bind function * * @param array $array Named array * @param mixed $ignore An optional array or space separated list of properties * to ignore while binding. * * @return mixed Null if operation was satisfactory, otherwise returns an error * * @see Table::bind() * @since 1.7.0 */ public function bind($array, $ignore = '') { if (isset($array['params']) && is_array($array['params'])) { $registry = new Registry($array['params']); $array['params'] = (string) $registry; } if (isset($array['control']) && is_array($array['control'])) { $registry = new Registry($array['control']); $array['control'] = (string) $registry; } return parent::bind($array, $ignore); } /** * Method to create and execute a SELECT WHERE query. * * @param array $options Array of options * * @return string The database query result * * @since 1.7.0 */ public function find($options = array()) { // Get the \JDatabaseQuery object $query = $this->_db->getQuery(true); foreach ($options as $col => $val) { $query->where($col . ' = ' . $this->_db->quote($val)); } $query->select($this->_db->quoteName('extension_id')) ->from($this->_db->quoteName('#__extensions')); $this->_db->setQuery($query); return $this->_db->loadResult(); } /** * 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. * * @since 1.7.0 */ public function publish($pks = null, $state = 1, $userId = 0) { $k = $this->_tbl_key; // Sanitize input. $pks = ArrayHelper::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 WHERE clause for the primary keys. $where = $k . '=' . implode(' OR ' . $k . '=', $pks); // Determine if there is checkin support for the table. if (!property_exists($this, 'checked_out') || !property_exists($this, 'checked_out_time')) { $checkin = ''; } else { $checkin = ' AND (checked_out = 0 OR checked_out = ' . (int) $userId . ')'; } // Update the publishing state for rows with the given primary keys. $query = $this->_db->getQuery(true) ->update($this->_db->quoteName($this->_tbl)) ->set($this->_db->quoteName('enabled') . ' = ' . (int) $state) ->where('(' . $where . ')' . $checkin); $this->_db->setQuery($query); $this->_db->execute(); // 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 Table instance value is in the list of primary keys that were set, set the instance. if (in_array($this->$k, $pks)) { $this->enabled = $state; } $this->setError(''); return true; } } PK �V�[���s Language.phpnu �[��� <?php /** * Joomla! Content Management System * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Table; defined('JPATH_PLATFORM') or die; /** * Languages table. * * @since 1.7.0 */ class Language extends Table { /** * Constructor * * @param \JDatabaseDriver $db Database driver object. * * @since 1.7.0 */ public function __construct($db) { parent::__construct('#__languages', 'lang_id', $db); } /** * Overloaded check method to ensure data integrity * * @return boolean True on success * * @since 1.7.0 */ public function check() { if (trim($this->title) == '') { $this->setError(\JText::_('JLIB_DATABASE_ERROR_LANGUAGE_NO_TITLE')); return false; } return true; } /** * Overrides Table::store to check unique fields. * * @param boolean $updateNulls True to update fields even if they are null. * * @return boolean True on success. * * @since 2.5.0 */ public function store($updateNulls = false) { $table = Table::getInstance('Language', 'JTable', array('dbo' => $this->getDbo())); // Verify that the language code is unique if ($table->load(array('lang_code' => $this->lang_code)) && ($table->lang_id != $this->lang_id || $this->lang_id == 0)) { $this->setError(\JText::_('JLIB_DATABASE_ERROR_LANGUAGE_UNIQUE_LANG_CODE')); return false; } // Verify that the sef field is unique if ($table->load(array('sef' => $this->sef)) && ($table->lang_id != $this->lang_id || $this->lang_id == 0)) { $this->setError(\JText::_('JLIB_DATABASE_ERROR_LANGUAGE_UNIQUE_IMAGE')); return false; } // Verify that the image field is unique if ($this->image && $table->load(array('image' => $this->image)) && ($table->lang_id != $this->lang_id || $this->lang_id == 0)) { $this->setError(\JText::_('JLIB_DATABASE_ERROR_LANGUAGE_UNIQUE_IMAGE')); return false; } return parent::store($updateNulls); } /** * Method to compute the default name of the asset. * The default name is in the form table_name.id * where id is the value of the primary key of the table. * * @return string * * @since 3.8.0 */ protected function _getAssetName() { return 'com_languages.language.' . $this->lang_id; } /** * Method to return the title to use for the asset table. * * @return string * * @since 3.8.0 */ protected function _getAssetTitle() { return $this->title; } /** * Method to get the parent asset under which to register this one. * By default, all assets are registered to the ROOT node with ID, * which will default to 1 if none exists. * The extended class can define a table and id to lookup. If the * asset does not exist it will be created. * * @param Table $table A Table object for the asset parent. * @param integer $id Id to look up * * @return integer * * @since 3.8.0 */ protected function _getAssetParentId(Table $table = null, $id = null) { $assetId = null; $asset = Table::getInstance('asset'); if ($asset->loadByName('com_languages')) { $assetId = $asset->id; } return $assetId === null ? parent::_getAssetParentId($table, $id) : $assetId; } } PK �V�[��B� � Menu.phpnu �[��� <?php /** * Joomla! Content Management System * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Table; defined('JPATH_PLATFORM') or die; use Joomla\CMS\Application\ApplicationHelper; use Joomla\CMS\Language\Multilanguage; use Joomla\Registry\Registry; /** * Menu table * * @since 1.5 */ class Menu extends Nested { /** * Constructor * * @param \JDatabaseDriver $db Database driver object. * * @since 1.5 */ public function __construct(\JDatabaseDriver $db) { parent::__construct('#__menu', 'id', $db); // Set the default access level. $this->access = (int) \JFactory::getConfig()->get('access'); } /** * Overloaded bind function * * @param array $array Named array * @param mixed $ignore An optional array or space separated list of properties to ignore while binding. * * @return mixed Null if operation was satisfactory, otherwise returns an error * * @see Table::bind() * @since 1.5 */ public function bind($array, $ignore = '') { // Verify that the default home menu is not unset if ($this->home == '1' && $this->language === '*' && $array['home'] == '0') { $this->setError(\JText::_('JLIB_DATABASE_ERROR_MENU_CANNOT_UNSET_DEFAULT_DEFAULT')); return false; } // Verify that the default home menu set to "all" languages" is not unset if ($this->home == '1' && $this->language === '*' && $array['language'] !== '*') { $this->setError(\JText::_('JLIB_DATABASE_ERROR_MENU_CANNOT_UNSET_DEFAULT')); return false; } // Verify that the default home menu is not unpublished if ($this->home == '1' && $this->language === '*' && $array['published'] != '1') { $this->setError(\JText::_('JLIB_DATABASE_ERROR_MENU_UNPUBLISH_DEFAULT_HOME')); return false; } if (isset($array['params']) && is_array($array['params'])) { $registry = new Registry($array['params']); $array['params'] = (string) $registry; } return parent::bind($array, $ignore); } /** * Overloaded check function * * @return boolean True on success * * @see Table::check() * @since 1.5 */ public function check() { // Check for a title. if (trim($this->title) === '') { $this->setError(\JText::_('JLIB_DATABASE_ERROR_MUSTCONTAIN_A_TITLE_MENUITEM')); return false; } // Check for a path. if (trim($this->path) === '') { $this->path = $this->alias; } // Check for params. if (trim($this->params) === '') { $this->params = '{}'; } // Check for img. if (trim($this->img) === '') { $this->img = ' '; } // Cast the home property to an int for checking. $this->home = (int) $this->home; // Verify that the home item is a component. if ($this->home && $this->type !== 'component') { $this->setError(\JText::_('JLIB_DATABASE_ERROR_MENU_HOME_NOT_COMPONENT')); return false; } return true; } /** * Overloaded store function * * @param boolean $updateNulls True to update fields even if they are null. * * @return mixed False on failure, positive integer on success. * * @see Table::store() * @since 1.6 */ public function store($updateNulls = false) { $db = $this->getDbo(); // Verify that the alias is unique $table = Table::getInstance('Menu', 'JTable', array('dbo' => $db)); $originalAlias = trim($this->alias); $this->alias = !$originalAlias ? $this->title : $originalAlias; $this->alias = ApplicationHelper::stringURLSafe(trim($this->alias), $this->language); if ($this->parent_id == 1 && $this->client_id == 0) { // Verify that a first level menu item alias is not 'component'. if ($this->alias == 'component') { $this->setError(\JText::_('JLIB_DATABASE_ERROR_MENU_ROOT_ALIAS_COMPONENT')); return false; } // Verify that a first level menu item alias is not the name of a folder. jimport('joomla.filesystem.folder'); if (in_array($this->alias, \JFolder::folders(JPATH_ROOT))) { $this->setError(\JText::sprintf('JLIB_DATABASE_ERROR_MENU_ROOT_ALIAS_FOLDER', $this->alias, $this->alias)); return false; } } // If alias still empty (for instance, new menu item with chinese characters with no unicode alias setting). if (empty($this->alias)) { $this->alias = \JFactory::getDate()->format('Y-m-d-H-i-s'); } else { $itemSearch = array('alias' => $this->alias, 'parent_id' => $this->parent_id, 'client_id' => (int) $this->client_id); $error = false; // Check if the alias already exists. For multilingual site. if (Multilanguage::isEnabled() && (int) $this->client_id == 0) { // If there is a menu item at the same level with the same alias (in the All or the same language). if (($table->load(array_replace($itemSearch, array('language' => '*'))) && ($table->id != $this->id || $this->id == 0)) || ($table->load(array_replace($itemSearch, array('language' => $this->language))) && ($table->id != $this->id || $this->id == 0)) || ($this->language === '*' && $this->id == 0 && $table->load($itemSearch))) { $error = true; } // When editing an item with All language check if there are more menu items with the same alias in any language. elseif ($this->language === '*' && $this->id != 0) { $query = $db->getQuery(true) ->select('id') ->from($db->quoteName('#__menu')) ->where($db->quoteName('parent_id') . ' = 1') ->where($db->quoteName('client_id') . ' = 0') ->where($db->quoteName('id') . ' != ' . (int) $this->id) ->where($db->quoteName('alias') . ' = ' . $db->quote($this->alias)); $otherMenuItemId = (int) $db->setQuery($query)->loadResult(); if ($otherMenuItemId) { $table->load(array('id' => $otherMenuItemId)); $error = true; } } } // Check if the alias already exists. For monolingual site. else { // If there is a menu item at the same level with the same alias (in any language). if ($table->load($itemSearch) && ($table->id != $this->id || $this->id == 0)) { $error = true; } } // The alias already exists. Enqueue an error message. if ($error) { $menuTypeTable = Table::getInstance('MenuType', 'JTable', array('dbo' => $db)); $menuTypeTable->load(array('menutype' => $table->menutype)); $this->setError(\JText::sprintf('JLIB_DATABASE_ERROR_MENU_UNIQUE_ALIAS', $this->alias, $table->title, $menuTypeTable->title)); return false; } } if ($this->home == '1') { // Verify that the home page for this menu is unique. if ($table->load( array( 'menutype' => $this->menutype, 'client_id' => (int) $this->client_id, 'home' => '1', ) ) && ($table->language != $this->language)) { $this->setError(\JText::_('JLIB_DATABASE_ERROR_MENU_HOME_NOT_UNIQUE_IN_MENU')); return false; } // Verify that the home page for this language is unique per client id if ($table->load(array('home' => '1', 'language' => $this->language, 'client_id' => (int) $this->client_id))) { if ($table->checked_out && $table->checked_out != $this->checked_out) { $this->setError(\JText::_('JLIB_DATABASE_ERROR_MENU_DEFAULT_CHECKIN_USER_MISMATCH')); return false; } $table->home = 0; $table->checked_out = 0; $table->checked_out_time = $db->getNullDate(); $table->store(); } } if (!parent::store($updateNulls)) { return false; } // Get the new path in case the node was moved $pathNodes = $this->getPath(); $segments = array(); foreach ($pathNodes as $node) { // Don't include root in path if ($node->alias !== 'root') { $segments[] = $node->alias; } } $newPath = trim(implode('/', $segments), ' /\\'); // Use new path for partial rebuild of table // Rebuild will return positive integer on success, false on failure return $this->rebuild($this->{$this->_tbl_key}, $this->lft, $this->level, $newPath) > 0; } } PK �V�[Qr��� � MenuType.phpnu �[��� <?php /** * Joomla! Content Management System * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Table; defined('JPATH_PLATFORM') or die; use Joomla\CMS\Application\ApplicationHelper; /** * Menu Types table * * @since 1.6 */ class MenuType extends Table { /** * Constructor * * @param \JDatabaseDriver $db Database driver object. * * @since 1.6 */ public function __construct(\JDatabaseDriver $db) { parent::__construct('#__menu_types', 'id', $db); } /** * Overloaded check function * * @return boolean True on success, false on failure * * @see Table::check() * @since 1.6 */ public function check() { $this->menutype = ApplicationHelper::stringURLSafe($this->menutype); if (empty($this->menutype)) { $this->setError(\JText::_('JLIB_DATABASE_ERROR_MENUTYPE_EMPTY')); return false; } // Sanitise data. if (trim($this->title) === '') { $this->title = $this->menutype; } // Check for unique menutype. $query = $this->_db->getQuery(true) ->select('COUNT(id)') ->from($this->_db->quoteName('#__menu_types')) ->where($this->_db->quoteName('menutype') . ' = ' . $this->_db->quote($this->menutype)) ->where($this->_db->quoteName('id') . ' <> ' . (int) $this->id); $this->_db->setQuery($query); if ($this->_db->loadResult()) { $this->setError(\JText::sprintf('JLIB_DATABASE_ERROR_MENUTYPE_EXISTS', $this->menutype)); return false; } return true; } /** * Method to store a row in the database from the Table instance properties. * * If a primary key value is set the row with that primary key value will be updated with the instance property values. * If no primary key value is set a new row will be inserted into the database with the properties from the Table instance. * * @param boolean $updateNulls True to update fields even if they are null. * * @return boolean True on success. * * @since 1.6 */ public function store($updateNulls = false) { if ($this->id) { // Get the user id $userId = \JFactory::getUser()->id; // Get the old value of the table $table = Table::getInstance('Menutype', 'JTable', array('dbo' => $this->getDbo())); $table->load($this->id); // Verify that no items are checked out $query = $this->_db->getQuery(true) ->select('id') ->from('#__menu') ->where('menutype=' . $this->_db->quote($table->menutype)) ->where('checked_out !=' . (int) $userId) ->where('checked_out !=0'); $this->_db->setQuery($query); if ($this->_db->loadRowList()) { $this->setError( \JText::sprintf('JLIB_DATABASE_ERROR_STORE_FAILED', get_class($this), \JText::_('JLIB_DATABASE_ERROR_MENUTYPE_CHECKOUT')) ); return false; } // Verify that no module for this menu are checked out $query->clear() ->select('id') ->from('#__modules') ->where('module=' . $this->_db->quote('mod_menu')) ->where('params LIKE ' . $this->_db->quote('%"menutype":' . json_encode($table->menutype) . '%')) ->where('checked_out !=' . (int) $userId) ->where('checked_out !=0'); $this->_db->setQuery($query); if ($this->_db->loadRowList()) { $this->setError( \JText::sprintf('JLIB_DATABASE_ERROR_STORE_FAILED', get_class($this), \JText::_('JLIB_DATABASE_ERROR_MENUTYPE_CHECKOUT')) ); return false; } // Update the menu items $query->clear() ->update('#__menu') ->set('menutype=' . $this->_db->quote($this->menutype)) ->where('menutype=' . $this->_db->quote($table->menutype)); $this->_db->setQuery($query); $this->_db->execute(); // Update the module items $query->clear() ->update('#__modules') ->set( 'params=REPLACE(params,' . $this->_db->quote('"menutype":' . json_encode($table->menutype)) . ',' . $this->_db->quote('"menutype":' . json_encode($this->menutype)) . ')' ); $query->where('module=' . $this->_db->quote('mod_menu')) ->where('params LIKE ' . $this->_db->quote('%"menutype":' . json_encode($table->menutype) . '%')); $this->_db->setQuery($query); $this->_db->execute(); } return parent::store($updateNulls); } /** * Method to delete a row from the database table by primary key value. * * @param mixed $pk An optional primary key value to delete. If not set the instance property value is used. * * @return boolean True on success. * * @since 1.6 */ public function delete($pk = null) { $k = $this->_tbl_key; $pk = $pk === null ? $this->$k : $pk; // If no primary key is given, return false. if ($pk !== null) { // Get the user id $userId = \JFactory::getUser()->id; // Get the old value of the table $table = Table::getInstance('Menutype', 'JTable', array('dbo' => $this->getDbo())); $table->load($pk); // Verify that no items are checked out $query = $this->_db->getQuery(true) ->select('id') ->from('#__menu') ->where('menutype=' . $this->_db->quote($table->menutype)) ->where('(checked_out NOT IN (0,' . (int) $userId . ') OR home=1 AND language=' . $this->_db->quote('*') . ')'); $this->_db->setQuery($query); if ($this->_db->loadRowList()) { $this->setError(\JText::sprintf('JLIB_DATABASE_ERROR_DELETE_FAILED', get_class($this), \JText::_('JLIB_DATABASE_ERROR_MENUTYPE'))); return false; } // Verify that no module for this menu are checked out $query->clear() ->select('id') ->from('#__modules') ->where('module=' . $this->_db->quote('mod_menu')) ->where('params LIKE ' . $this->_db->quote('%"menutype":' . json_encode($table->menutype) . '%')) ->where('checked_out !=' . (int) $userId) ->where('checked_out !=0'); $this->_db->setQuery($query); if ($this->_db->loadRowList()) { $this->setError(\JText::sprintf('JLIB_DATABASE_ERROR_DELETE_FAILED', get_class($this), \JText::_('JLIB_DATABASE_ERROR_MENUTYPE'))); return false; } // Delete the menu items $query->clear() ->delete('#__menu') ->where('menutype=' . $this->_db->quote($table->menutype)); $this->_db->setQuery($query); $this->_db->execute(); // Update the module items $query->clear() ->delete('#__modules') ->where('module=' . $this->_db->quote('mod_menu')) ->where('params LIKE ' . $this->_db->quote('%"menutype":' . json_encode($table->menutype) . '%')); $this->_db->setQuery($query); $this->_db->execute(); } return parent::delete($pk); } /** * Method to compute the default name of the asset. * The default name is in the form table_name.id * where id is the value of the primary key of the table. * * @return string * * @since 3.6 */ protected function _getAssetName() { return 'com_menus.menu.' . $this->id; } /** * Method to return the title to use for the asset table. * * @return string * * @since 3.6 */ protected function _getAssetTitle() { return $this->title; } /** * Method to get the parent asset under which to register this one. * By default, all assets are registered to the ROOT node with ID, * which will default to 1 if none exists. * The extended class can define a table and id to lookup. If the * asset does not exist it will be created. * * @param Table $table A Table object for the asset parent. * @param integer $id Id to look up * * @return integer * * @since 3.6 */ protected function _getAssetParentId(Table $table = null, $id = null) { $assetId = null; $asset = Table::getInstance('asset'); if ($asset->loadByName('com_menus')) { $assetId = $asset->id; } return $assetId === null ? parent::_getAssetParentId($table, $id) : $assetId; } } PK �V�[�f�F F Module.phpnu �[��� <?php /** * Joomla! Content Management System * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Table; defined('JPATH_PLATFORM') or die; use Joomla\CMS\Access\Rules; use Joomla\Registry\Registry; /** * Module table * * @since 1.5 */ class Module extends Table { /** * Constructor. * * @param \JDatabaseDriver $db Database driver object. * * @since 1.5 */ public function __construct(\JDatabaseDriver $db) { parent::__construct('#__modules', 'id', $db); $this->access = (int) \JFactory::getConfig()->get('access'); } /** * Method to compute the default name of the asset. * The default name is in the form table_name.id * where id is the value of the primary key of the table. * * @return string * * @since 3.2 */ protected function _getAssetName() { $k = $this->_tbl_key; return 'com_modules.module.' . (int) $this->$k; } /** * Method to return the title to use for the asset table. * * @return string * * @since 3.2 */ protected function _getAssetTitle() { return $this->title; } /** * Method to get the parent asset id for the record * * @param Table $table A Table object (optional) for the asset parent * @param integer $id The id (optional) of the content. * * @return integer * * @since 3.2 */ protected function _getAssetParentId(Table $table = null, $id = null) { $assetId = null; // This is a module that needs to parent with the extension. if ($assetId === null) { // Build the query to get the asset id of the parent component. $query = $this->_db->getQuery(true) ->select($this->_db->quoteName('id')) ->from($this->_db->quoteName('#__assets')) ->where($this->_db->quoteName('name') . ' = ' . $this->_db->quote('com_modules')); // Get the asset id from the database. $this->_db->setQuery($query); if ($result = $this->_db->loadResult()) { $assetId = (int) $result; } } // Return the asset id. if ($assetId) { return $assetId; } else { return parent::_getAssetParentId($table, $id); } } /** * Overloaded check function. * * @return boolean True if the instance is sane and able to be stored in the database. * * @see Table::check() * @since 1.5 */ public function check() { // Check for valid name if (trim($this->title) === '') { $this->setError(\JText::_('JLIB_DATABASE_ERROR_MUSTCONTAIN_A_TITLE_MODULE')); return false; } // Prevent to save too large content > 65535 if ((strlen($this->content) > 65535) || (strlen($this->params) > 65535)) { $this->setError(\JText::_('COM_MODULES_FIELD_CONTENT_TOO_LARGE')); return false; } // Check the publish down date is not earlier than publish up. if ((int) $this->publish_down > 0 && $this->publish_down < $this->publish_up) { // Swap the dates. $temp = $this->publish_up; $this->publish_up = $this->publish_down; $this->publish_down = $temp; } return true; } /** * Overloaded bind function. * * @param array $array Named array. * @param mixed $ignore An optional array or space separated list of properties to ignore while binding. * * @return mixed Null if operation was satisfactory, otherwise returns an error * * @see Table::bind() * @since 1.5 */ public function bind($array, $ignore = '') { if (isset($array['params']) && is_array($array['params'])) { $registry = new Registry($array['params']); $array['params'] = (string) $registry; } // Bind the rules. if (isset($array['rules']) && is_array($array['rules'])) { $rules = new Rules($array['rules']); $this->setRules($rules); } return parent::bind($array, $ignore); } /** * Stores a module. * * @param boolean $updateNulls True to update fields even if they are null. * * @return boolean True on success, false on failure. * * @since 3.7.0 */ public function store($updateNulls = false) { // Set publish_up, publish_down and checked_out_time to null date if not set if (!$this->publish_up) { $this->publish_up = $this->_db->getNullDate(); } if (!$this->publish_down) { $this->publish_down = $this->_db->getNullDate(); } return parent::store($updateNulls); } } PK �V�[:��X� X� Nested.phpnu �[��� <?php /** * Joomla! Content Management System * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Table; defined('JPATH_PLATFORM') or die; use Joomla\Utilities\ArrayHelper; /** * Table class supporting modified pre-order tree traversal behavior. * * @since 1.7.0 */ class Nested extends Table { /** * Object property holding the primary key of the parent node. Provides adjacency list data for nodes. * * @var integer * @since 1.7.0 */ public $parent_id; /** * Object property holding the depth level of the node in the tree. * * @var integer * @since 1.7.0 */ public $level; /** * Object property holding the left value of the node for managing its placement in the nested sets tree. * * @var integer * @since 1.7.0 */ public $lft; /** * Object property holding the right value of the node for managing its placement in the nested sets tree. * * @var integer * @since 1.7.0 */ public $rgt; /** * Object property holding the alias of this node used to constuct the full text path, forward-slash delimited. * * @var string * @since 1.7.0 */ public $alias; /** * Object property to hold the location type to use when storing the row. * * @var string * @since 1.7.0 * @see Nested::$_validLocations */ protected $_location; /** * Object property to hold the primary key of the location reference node to use when storing the row. * * A combination of location type and reference node describes where to store the current node in the tree. * * @var integer * @since 1.7.0 */ protected $_location_id; /** * An array to cache values in recursive processes. * * @var array * @since 1.7.0 */ protected $_cache = array(); /** * Debug level * * @var integer * @since 1.7.0 */ protected $_debug = 0; /** * Cache for the root ID * * @var integer * @since 3.3 */ protected static $root_id = 0; /** * Array declaring the valid location values for moving a node * * @var array * @since 3.7.0 */ private $_validLocations = array('before', 'after', 'first-child', 'last-child'); /** * Sets the debug level on or off * * @param integer $level 0 = off, 1 = on * * @return void * * @since 1.7.0 */ public function debug($level) { $this->_debug = (int) $level; } /** * Method to get an array of nodes from a given node to its root. * * @param integer $pk Primary key of the node for which to get the path. * @param boolean $diagnostic Only select diagnostic data for the nested sets. * * @return mixed An array of node objects including the start node. * * @since 1.7.0 * @throws \RuntimeException on database error */ public function getPath($pk = null, $diagnostic = false) { $k = $this->_tbl_key; $pk = (is_null($pk)) ? $this->$k : $pk; // Get the path from the node to the root. $select = ($diagnostic) ? 'p.' . $k . ', p.parent_id, p.level, p.lft, p.rgt' : 'p.*'; $query = $this->_db->getQuery(true) ->select($select) ->from($this->_tbl . ' AS n, ' . $this->_tbl . ' AS p') ->where('n.lft BETWEEN p.lft AND p.rgt') ->where('n.' . $k . ' = ' . (int) $pk) ->order('p.lft'); $this->_db->setQuery($query); return $this->_db->loadObjectList(); } /** * Method to get a node and all its child nodes. * * @param integer $pk Primary key of the node for which to get the tree. * @param boolean $diagnostic Only select diagnostic data for the nested sets. * * @return mixed Boolean false on failure or array of node objects on success. * * @since 1.7.0 * @throws \RuntimeException on database error. */ public function getTree($pk = null, $diagnostic = false) { $k = $this->_tbl_key; $pk = (is_null($pk)) ? $this->$k : $pk; // Get the node and children as a tree. $select = ($diagnostic) ? 'n.' . $k . ', n.parent_id, n.level, n.lft, n.rgt' : 'n.*'; $query = $this->_db->getQuery(true) ->select($select) ->from($this->_tbl . ' AS n, ' . $this->_tbl . ' AS p') ->where('n.lft BETWEEN p.lft AND p.rgt') ->where('p.' . $k . ' = ' . (int) $pk) ->order('n.lft'); return $this->_db->setQuery($query)->loadObjectList(); } /** * Method to determine if a node is a leaf node in the tree (has no children). * * @param integer $pk Primary key of the node to check. * * @return boolean True if a leaf node, false if not or null if the node does not exist. * * @note Since 3.0.0 this method returns null if the node does not exist. * @since 1.7.0 * @throws \RuntimeException on database error. */ public function isLeaf($pk = null) { $k = $this->_tbl_key; $pk = (is_null($pk)) ? $this->$k : $pk; $node = $this->_getNode($pk); // Get the node by primary key. if (empty($node)) { // Error message set in getNode method. return; } // The node is a leaf node. return ($node->rgt - $node->lft) == 1; } /** * Method to set the location of a node in the tree object. This method does not * save the new location to the database, but will set it in the object so * that when the node is stored it will be stored in the new location. * * @param integer $referenceId The primary key of the node to reference new location by. * @param string $position Location type string. * * @return void * * @note Since 3.0.0 this method returns void and throws an \InvalidArgumentException when an invalid position is passed. * @see Nested::$_validLocations * @since 1.7.0 * @throws \InvalidArgumentException */ public function setLocation($referenceId, $position = 'after') { // Make sure the location is valid. if (!in_array($position, $this->_validLocations)) { throw new \InvalidArgumentException( sprintf('Invalid location "%1$s" given, valid values are %2$s', $position, implode(', ', $this->_validLocations)) ); } // Set the location properties. $this->_location = $position; $this->_location_id = $referenceId; } /** * Method to move a row in the ordering sequence of a group of rows defined by an SQL WHERE clause. * Negative numbers move the row up in the sequence and positive numbers move it down. * * @param integer $delta The direction and magnitude to move the row in the ordering sequence. * @param string $where WHERE clause to use for limiting the selection of rows to compact the * ordering values. * * @return mixed Boolean true on success. * * @since 1.7.0 */ public function move($delta, $where = '') { $k = $this->_tbl_key; $pk = $this->$k; $query = $this->_db->getQuery(true) ->select($k) ->from($this->_tbl) ->where('parent_id = ' . $this->parent_id); if ($where) { $query->where($where); } if ($delta > 0) { $query->where('rgt > ' . $this->rgt) ->order('rgt ASC'); $position = 'after'; } else { $query->where('lft < ' . $this->lft) ->order('lft DESC'); $position = 'before'; } $this->_db->setQuery($query); $referenceId = $this->_db->loadResult(); if ($referenceId) { return $this->moveByReference($referenceId, $position, $pk); } else { return false; } } /** * Method to move a node and its children to a new location in the tree. * * @param integer $referenceId The primary key of the node to reference new location by. * @param string $position Location type string. ['before', 'after', 'first-child', 'last-child'] * @param integer $pk The primary key of the node to move. * @param boolean $recursiveUpdate Flag indicate that method recursiveUpdatePublishedColumn should be call. * * @return boolean True on success. * * @since 1.7.0 * @throws \RuntimeException on database error. */ public function moveByReference($referenceId, $position = 'after', $pk = null, $recursiveUpdate = true) { if ($this->_debug) { echo "\nMoving ReferenceId:$referenceId, Position:$position, PK:$pk"; } $k = $this->_tbl_key; $pk = (is_null($pk)) ? $this->$k : $pk; // Get the node by id. if (!$node = $this->_getNode($pk)) { // Error message set in getNode method. return false; } // Get the ids of child nodes. $query = $this->_db->getQuery(true) ->select($k) ->from($this->_tbl) ->where('lft BETWEEN ' . (int) $node->lft . ' AND ' . (int) $node->rgt); $children = $this->_db->setQuery($query)->loadColumn(); if ($this->_debug) { $this->_logtable(false); } // Cannot move the node to be a child of itself. if (in_array($referenceId, $children)) { $this->setError( new \UnexpectedValueException( sprintf('%1$s::moveByReference() is trying to make record ID %2$d a child of itself.', get_class($this), $pk) ) ); return false; } // Lock the table for writing. if (!$this->_lock()) { return false; } /* * Move the sub-tree out of the nested sets by negating its left and right values. */ $query->clear() ->update($this->_tbl) ->set('lft = lft * (-1), rgt = rgt * (-1)') ->where('lft BETWEEN ' . (int) $node->lft . ' AND ' . (int) $node->rgt); $this->_db->setQuery($query); $this->_runQuery($query, 'JLIB_DATABASE_ERROR_MOVE_FAILED'); /* * Close the hole in the tree that was opened by removing the sub-tree from the nested sets. */ // Compress the left values. $query->clear() ->update($this->_tbl) ->set('lft = lft - ' . (int) $node->width) ->where('lft > ' . (int) $node->rgt); $this->_db->setQuery($query); $this->_runQuery($query, 'JLIB_DATABASE_ERROR_MOVE_FAILED'); // Compress the right values. $query->clear() ->update($this->_tbl) ->set('rgt = rgt - ' . (int) $node->width) ->where('rgt > ' . (int) $node->rgt); $this->_db->setQuery($query); $this->_runQuery($query, 'JLIB_DATABASE_ERROR_MOVE_FAILED'); // We are moving the tree relative to a reference node. if ($referenceId) { // Get the reference node by primary key. if (!$reference = $this->_getNode($referenceId)) { // Error message set in getNode method. $this->_unlock(); return false; } // Get the reposition data for shifting the tree and re-inserting the node. if (!$repositionData = $this->_getTreeRepositionData($reference, $node->width, $position)) { // Error message set in getNode method. $this->_unlock(); return false; } } // We are moving the tree to be the last child of the root node else { // Get the last root node as the reference node. $query->clear() ->select($this->_tbl_key . ', parent_id, level, lft, rgt') ->from($this->_tbl) ->where('parent_id = 0') ->order('lft DESC'); $this->_db->setQuery($query, 0, 1); $reference = $this->_db->loadObject(); if ($this->_debug) { $this->_logtable(false); } // Get the reposition data for re-inserting the node after the found root. if (!$repositionData = $this->_getTreeRepositionData($reference, $node->width, 'last-child')) { // Error message set in getNode method. $this->_unlock(); return false; } } /* * Create space in the nested sets at the new location for the moved sub-tree. */ // Shift left values. $query->clear() ->update($this->_tbl) ->set('lft = lft + ' . (int) $node->width) ->where($repositionData->left_where); $this->_db->setQuery($query); $this->_runQuery($query, 'JLIB_DATABASE_ERROR_MOVE_FAILED'); // Shift right values. $query->clear() ->update($this->_tbl) ->set('rgt = rgt + ' . (int) $node->width) ->where($repositionData->right_where); $this->_db->setQuery($query); $this->_runQuery($query, 'JLIB_DATABASE_ERROR_MOVE_FAILED'); /* * Calculate the offset between where the node used to be in the tree and * where it needs to be in the tree for left ids (also works for right ids). */ $offset = $repositionData->new_lft - $node->lft; $levelOffset = $repositionData->new_level - $node->level; // Move the nodes back into position in the tree using the calculated offsets. $query->clear() ->update($this->_tbl) ->set('rgt = ' . (int) $offset . ' - rgt') ->set('lft = ' . (int) $offset . ' - lft') ->set('level = level + ' . (int) $levelOffset) ->where('lft < 0'); $this->_db->setQuery($query); $this->_runQuery($query, 'JLIB_DATABASE_ERROR_MOVE_FAILED'); // Set the correct parent id for the moved node if required. if ($node->parent_id != $repositionData->new_parent_id) { $query = $this->_db->getQuery(true) ->update($this->_tbl); // Update the title and alias fields if they exist for the table. $fields = $this->getFields(); if (property_exists($this, 'title') && $this->title !== null) { $query->set('title = ' . $this->_db->quote($this->title)); } if (array_key_exists('alias', $fields) && $this->alias !== null) { $query->set('alias = ' . $this->_db->quote($this->alias)); } $query->set('parent_id = ' . (int) $repositionData->new_parent_id) ->where($this->_tbl_key . ' = ' . (int) $node->$k); $this->_db->setQuery($query); $this->_runQuery($query, 'JLIB_DATABASE_ERROR_MOVE_FAILED'); } // Unlock the table for writing. $this->_unlock(); if (property_exists($this, 'published') && $recursiveUpdate) { $this->recursiveUpdatePublishedColumn($node->$k); } // Set the object values. $this->parent_id = $repositionData->new_parent_id; $this->level = $repositionData->new_level; $this->lft = $repositionData->new_lft; $this->rgt = $repositionData->new_rgt; return true; } /** * 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. * * @since 1.7.0 */ public function delete($pk = null, $children = true) { $k = $this->_tbl_key; $pk = (is_null($pk)) ? $this->$k : $pk; // Implement \JObservableInterface: Pre-processing by observers $this->_observers->update('onBeforeDelete', array($pk)); // Lock the table for writing. if (!$this->_lock()) { // Error message set in lock method. return false; } // If tracking assets, remove the asset first. if ($this->_trackAssets) { $name = $this->_getAssetName(); $asset = Table::getInstance('Asset', 'JTable', array('dbo' => $this->getDbo())); // Lock the table for writing. if (!$asset->_lock()) { // Error message set in lock method. return false; } if ($asset->loadByName($name)) { // Delete the node in assets table. if (!$asset->delete(null, $children)) { $this->setError($asset->getError()); $asset->_unlock(); return false; } $asset->_unlock(); } else { $this->setError($asset->getError()); $asset->_unlock(); return false; } } // Get the node by id. $node = $this->_getNode($pk); if (empty($node)) { // Error message set in getNode method. $this->_unlock(); return false; } $query = $this->_db->getQuery(true); // Should we delete all children along with the node? if ($children) { // Delete the node and all of its children. $query->clear() ->delete($this->_tbl) ->where('lft BETWEEN ' . (int) $node->lft . ' AND ' . (int) $node->rgt); $this->_runQuery($query, 'JLIB_DATABASE_ERROR_DELETE_FAILED'); // Compress the left values. $query->clear() ->update($this->_tbl) ->set('lft = lft - ' . (int) $node->width) ->where('lft > ' . (int) $node->rgt); $this->_runQuery($query, 'JLIB_DATABASE_ERROR_DELETE_FAILED'); // Compress the right values. $query->clear() ->update($this->_tbl) ->set('rgt = rgt - ' . (int) $node->width) ->where('rgt > ' . (int) $node->rgt); $this->_runQuery($query, 'JLIB_DATABASE_ERROR_DELETE_FAILED'); } // Leave the children and move them up a level. else { // Delete the node. $query->clear() ->delete($this->_tbl) ->where('lft = ' . (int) $node->lft); $this->_runQuery($query, 'JLIB_DATABASE_ERROR_DELETE_FAILED'); // Shift all node's children up a level. $query->clear() ->update($this->_tbl) ->set('lft = lft - 1') ->set('rgt = rgt - 1') ->set('level = level - 1') ->where('lft BETWEEN ' . (int) $node->lft . ' AND ' . (int) $node->rgt); $this->_runQuery($query, 'JLIB_DATABASE_ERROR_DELETE_FAILED'); // Adjust all the parent values for direct children of the deleted node. $query->clear() ->update($this->_tbl) ->set('parent_id = ' . (int) $node->parent_id) ->where('parent_id = ' . (int) $node->$k); $this->_runQuery($query, 'JLIB_DATABASE_ERROR_DELETE_FAILED'); // Shift all of the left values that are right of the node. $query->clear() ->update($this->_tbl) ->set('lft = lft - 2') ->where('lft > ' . (int) $node->rgt); $this->_runQuery($query, 'JLIB_DATABASE_ERROR_DELETE_FAILED'); // Shift all of the right values that are right of the node. $query->clear() ->update($this->_tbl) ->set('rgt = rgt - 2') ->where('rgt > ' . (int) $node->rgt); $this->_runQuery($query, 'JLIB_DATABASE_ERROR_DELETE_FAILED'); } // Unlock the table for writing. $this->_unlock(); // Implement \JObservableInterface: Post-processing by observers $this->_observers->update('onAfterDelete', array($pk)); 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. * * @since 1.7.0 */ public function check() { $this->parent_id = (int) $this->parent_id; // Set up a mini exception handler. try { // Check that the parent_id field is valid. if ($this->parent_id == 0) { throw new \UnexpectedValueException(sprintf('Invalid `parent_id` [%1$d] in %2$s::check()', $this->parent_id, get_class($this))); } $query = $this->_db->getQuery(true) ->select('1') ->from($this->_tbl) ->where($this->_tbl_key . ' = ' . $this->parent_id); if (!$this->_db->setQuery($query)->loadResult()) { throw new \UnexpectedValueException(sprintf('Invalid `parent_id` [%1$d] in %2$s::check()', $this->parent_id, get_class($this))); } } catch (\UnexpectedValueException $e) { // Validation error - record it and return false. $this->setError($e); 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. * * @since 1.7.0 */ public function store($updateNulls = false) { $k = $this->_tbl_key; // Implement \JObservableInterface: Pre-processing by observers // 2.5 upgrade issue - check if property_exists before executing if (property_exists($this, '_observers')) { $this->_observers->update('onBeforeStore', array($updateNulls, $k)); } if ($this->_debug) { echo "\n" . get_class($this) . "::store\n"; $this->_logtable(true, false); } /* * If the primary key is empty, then we assume we are inserting a new node into the * tree. From this point we would need to determine where in the tree to insert it. */ if (empty($this->$k)) { /* * We are inserting a node somewhere in the tree with a known reference * node. We have to make room for the new node and set the left and right * values before we insert the row. */ if ($this->_location_id >= 0) { // Lock the table for writing. if (!$this->_lock()) { // Error message set in lock method. return false; } // We are inserting a node relative to the last root node. if ($this->_location_id == 0) { // Get the last root node as the reference node. $query = $this->_db->getQuery(true) ->select($this->_tbl_key . ', parent_id, level, lft, rgt') ->from($this->_tbl) ->where('parent_id = 0') ->order('lft DESC'); $this->_db->setQuery($query, 0, 1); $reference = $this->_db->loadObject(); if ($this->_debug) { $this->_logtable(false); } } // We have a real node set as a location reference. else { // Get the reference node by primary key. if (!$reference = $this->_getNode($this->_location_id)) { // Error message set in getNode method. $this->_unlock(); return false; } } // Get the reposition data for shifting the tree and re-inserting the node. if (!($repositionData = $this->_getTreeRepositionData($reference, 2, $this->_location))) { // Error message set in getNode method. $this->_unlock(); return false; } // Create space in the tree at the new location for the new node in left ids. $query = $this->_db->getQuery(true) ->update($this->_tbl) ->set('lft = lft + 2') ->where($repositionData->left_where); $this->_runQuery($query, 'JLIB_DATABASE_ERROR_STORE_FAILED'); // Create space in the tree at the new location for the new node in right ids. $query->clear() ->update($this->_tbl) ->set('rgt = rgt + 2') ->where($repositionData->right_where); $this->_runQuery($query, 'JLIB_DATABASE_ERROR_STORE_FAILED'); // Set the object values. $this->parent_id = $repositionData->new_parent_id; $this->level = $repositionData->new_level; $this->lft = $repositionData->new_lft; $this->rgt = $repositionData->new_rgt; } else { // Negative parent ids are invalid $e = new \UnexpectedValueException(sprintf('%s::store() used a negative _location_id', get_class($this))); $this->setError($e); return false; } } /* * If we have a given primary key then we assume we are simply updating this * node in the tree. We should assess whether or not we are moving the node * or just updating its data fields. */ else { // If the location has been set, move the node to its new location. if ($this->_location_id > 0) { // Skip recursiveUpdatePublishedColumn method, it will be called later. if (!$this->moveByReference($this->_location_id, $this->_location, $this->$k, false)) { // Error message set in move method. return false; } } // Lock the table for writing. if (!$this->_lock()) { // Error message set in lock method. return false; } } // Implement \JObservableInterface: We do not want parent::store to update observers, // since tables are locked and we are updating it from this level of store(): // 2.5 upgrade issue - check if property_exists before executing if (property_exists($this, '_observers')) { $oldCallObservers = $this->_observers->doCallObservers(false); } $result = parent::store($updateNulls); // Implement \JObservableInterface: Restore previous callable observers state: // 2.5 upgrade issue - check if property_exists before executing if (property_exists($this, '_observers')) { $this->_observers->doCallObservers($oldCallObservers); } if ($result) { if ($this->_debug) { $this->_logtable(); } } // Unlock the table for writing. $this->_unlock(); if (property_exists($this, 'published')) { $this->recursiveUpdatePublishedColumn($this->$k); } // Implement \JObservableInterface: Post-processing by observers // 2.5 upgrade issue - check if property_exists before executing if (property_exists($this, '_observers')) { $this->_observers->update('onAfterStore', array(&$result)); } return $result; } /** * Method to set the publishing state for a node or list of nodes in the database * table. The method respects rows checked out by other users and will attempt * to checkin rows that it can after adjustments are made. The method will not * allow you to set a publishing state higher than any ancestor node and will * not allow you to set a publishing state on a node with a checked out child. * * @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. * * @since 1.7.0 * @throws \UnexpectedValueException */ public function publish($pks = null, $state = 1, $userId = 0) { $k = $this->_tbl_key; $query = $this->_db->getQuery(true); $table = $this->_db->quoteName($this->_tbl); $published = $this->_db->quoteName($this->getColumnAlias('published')); $key = $this->_db->quoteName($k); // Sanitize input. $pks = ArrayHelper::toInteger($pks); $userId = (int) $userId; $state = (int) $state; // If $state > 1, then we allow state changes even if an ancestor has lower state // (for example, can change a child state to Archived (2) if an ancestor is Published (1) $compareState = ($state > 1) ? 1 : $state; // If there are no primary keys set check to see if the instance key is set. if (empty($pks)) { if ($this->$k) { $pks = explode(',', $this->$k); } // Nothing to set publishing state on, return false. else { $e = new \UnexpectedValueException(sprintf('%s::publish(%s, %d, %d) empty.', get_class($this), $pks[0], $state, $userId)); $this->setError($e); return false; } } // Determine if there is checkout support for the table. $checkoutSupport = (property_exists($this, 'checked_out') || property_exists($this, 'checked_out_time')); // Iterate over the primary keys to execute the publish action if possible. foreach ($pks as $pk) { // Get the node by primary key. if (!$node = $this->_getNode($pk)) { // Error message set in getNode method. return false; } // If the table has checkout support, verify no children are checked out. if ($checkoutSupport) { // Ensure that children are not checked out. $query->clear() ->select('COUNT(' . $k . ')') ->from($this->_tbl) ->where('lft BETWEEN ' . (int) $node->lft . ' AND ' . (int) $node->rgt) ->where('(checked_out <> 0 AND checked_out <> ' . (int) $userId . ')'); $this->_db->setQuery($query); // Check for checked out children. if ($this->_db->loadResult()) { // TODO Convert to a conflict exception when available. $e = new \RuntimeException(sprintf('%s::publish(%s, %d, %d) checked-out conflict.', get_class($this), $pks[0], $state, $userId)); $this->setError($e); return false; } } // If any parent nodes have lower published state values, we cannot continue. if ($node->parent_id) { // Get any ancestor nodes that have a lower publishing state. $query->clear() ->select('1') ->from($table) ->where('lft < ' . (int) $node->lft) ->where('rgt > ' . (int) $node->rgt) ->where('parent_id > 0') ->where($published . ' < ' . (int) $compareState); // Just fetch one row (one is one too many). $this->_db->setQuery($query, 0, 1); if ($this->_db->loadResult()) { $e = new \UnexpectedValueException( sprintf('%s::publish(%s, %d, %d) ancestors have lower state.', get_class($this), $pks[0], $state, $userId) ); $this->setError($e); return false; } } $this->recursiveUpdatePublishedColumn($pk, $state); // If checkout support exists for the object, check the row in. if ($checkoutSupport) { $this->checkin($pk); } } // If the Table instance value is in the list of primary keys that were set, set the instance. if (in_array($this->$k, $pks)) { $this->published = $state; } $this->setError(''); return true; } /** * Method to move a node one position to the left in the same level. * * @param integer $pk Primary key of the node to move. * * @return boolean True on success. * * @since 1.7.0 * @throws \RuntimeException on database error. */ public function orderUp($pk) { $k = $this->_tbl_key; $pk = (is_null($pk)) ? $this->$k : $pk; // Lock the table for writing. if (!$this->_lock()) { // Error message set in lock method. return false; } // Get the node by primary key. $node = $this->_getNode($pk); if (empty($node)) { // Error message set in getNode method. $this->_unlock(); return false; } // Get the left sibling node. $sibling = $this->_getNode($node->lft - 1, 'right'); if (empty($sibling)) { // Error message set in getNode method. $this->_unlock(); return false; } try { // Get the primary keys of child nodes. $query = $this->_db->getQuery(true) ->select($this->_tbl_key) ->from($this->_tbl) ->where('lft BETWEEN ' . (int) $node->lft . ' AND ' . (int) $node->rgt); $children = $this->_db->setQuery($query)->loadColumn(); // Shift left and right values for the node and its children. $query->clear() ->update($this->_tbl) ->set('lft = lft - ' . (int) $sibling->width) ->set('rgt = rgt - ' . (int) $sibling->width) ->where('lft BETWEEN ' . (int) $node->lft . ' AND ' . (int) $node->rgt); $this->_db->setQuery($query)->execute(); // Shift left and right values for the sibling and its children. $query->clear() ->update($this->_tbl) ->set('lft = lft + ' . (int) $node->width) ->set('rgt = rgt + ' . (int) $node->width) ->where('lft BETWEEN ' . (int) $sibling->lft . ' AND ' . (int) $sibling->rgt) ->where($this->_tbl_key . ' NOT IN (' . implode(',', $children) . ')'); $this->_db->setQuery($query)->execute(); } catch (\RuntimeException $e) { $this->_unlock(); throw $e; } // Unlock the table for writing. $this->_unlock(); return true; } /** * Method to move a node one position to the right in the same level. * * @param integer $pk Primary key of the node to move. * * @return boolean True on success. * * @since 1.7.0 * @throws \RuntimeException on database error. */ public function orderDown($pk) { $k = $this->_tbl_key; $pk = (is_null($pk)) ? $this->$k : $pk; // Lock the table for writing. if (!$this->_lock()) { // Error message set in lock method. return false; } // Get the node by primary key. $node = $this->_getNode($pk); if (empty($node)) { // Error message set in getNode method. $this->_unlock(); return false; } $query = $this->_db->getQuery(true); // Get the right sibling node. $sibling = $this->_getNode($node->rgt + 1, 'left'); if (empty($sibling)) { // Error message set in getNode method. $query->_unlock($this->_db); $this->_locked = false; return false; } try { // Get the primary keys of child nodes. $query->clear() ->select($this->_tbl_key) ->from($this->_tbl) ->where('lft BETWEEN ' . (int) $node->lft . ' AND ' . (int) $node->rgt); $this->_db->setQuery($query); $children = $this->_db->loadColumn(); // Shift left and right values for the node and its children. $query->clear() ->update($this->_tbl) ->set('lft = lft + ' . (int) $sibling->width) ->set('rgt = rgt + ' . (int) $sibling->width) ->where('lft BETWEEN ' . (int) $node->lft . ' AND ' . (int) $node->rgt); $this->_db->setQuery($query)->execute(); // Shift left and right values for the sibling and its children. $query->clear() ->update($this->_tbl) ->set('lft = lft - ' . (int) $node->width) ->set('rgt = rgt - ' . (int) $node->width) ->where('lft BETWEEN ' . (int) $sibling->lft . ' AND ' . (int) $sibling->rgt) ->where($this->_tbl_key . ' NOT IN (' . implode(',', $children) . ')'); $this->_db->setQuery($query)->execute(); } catch (\RuntimeException $e) { $this->_unlock(); throw $e; } // Unlock the table for writing. $this->_unlock(); return true; } /** * Gets the ID of the root item in the tree * * @return mixed The primary id of the root row, or false if not found and the internal error is set. * * @since 1.7.0 */ public function getRootId() { if ((int) self::$root_id > 0) { return self::$root_id; } // Get the root item. $k = $this->_tbl_key; // Test for a unique record with parent_id = 0 $query = $this->_db->getQuery(true) ->select($k) ->from($this->_tbl) ->where('parent_id = 0'); $result = $this->_db->setQuery($query)->loadColumn(); if (count($result) == 1) { self::$root_id = $result[0]; return self::$root_id; } // Test for a unique record with lft = 0 $query->clear() ->select($k) ->from($this->_tbl) ->where('lft = 0'); $result = $this->_db->setQuery($query)->loadColumn(); if (count($result) == 1) { self::$root_id = $result[0]; return self::$root_id; } $fields = $this->getFields(); if (array_key_exists('alias', $fields)) { // Test for a unique record alias = root $query->clear() ->select($k) ->from($this->_tbl) ->where('alias = ' . $this->_db->quote('root')); $result = $this->_db->setQuery($query)->loadColumn(); if (count($result) == 1) { self::$root_id = $result[0]; return self::$root_id; } } $e = new \UnexpectedValueException(sprintf('%s::getRootId', get_class($this))); $this->setError($e); self::$root_id = false; return false; } /** * Method to recursively rebuild the whole nested set tree. * * @param integer $parentId The root of the tree to rebuild. * @param integer $leftId The left id to start with in building the tree. * @param integer $level The level to assign to the current nodes. * @param string $path The path to the current nodes. * * @return integer 1 + value of root rgt on success, false on failure * * @since 1.7.0 * @throws \RuntimeException on database error. */ public function rebuild($parentId = null, $leftId = 0, $level = 0, $path = '') { // If no parent is provided, try to find it. if ($parentId === null) { // Get the root item. $parentId = $this->getRootId(); if ($parentId === false) { return false; } } $query = $this->_db->getQuery(true); // Build the structure of the recursive query. if (!isset($this->_cache['rebuild.sql'])) { $query->clear() ->select($this->_tbl_key . ', alias') ->from($this->_tbl) ->where('parent_id = %d'); // If the table has an ordering field, use that for ordering. $orderingField = $this->getColumnAlias('ordering'); if (property_exists($this, $orderingField)) { $query->order('parent_id, ' . $this->_db->quoteName($orderingField) . ', lft'); } else { $query->order('parent_id, lft'); } $this->_cache['rebuild.sql'] = (string) $query; } // Make a shortcut to database object. // Assemble the query to find all children of this node. $this->_db->setQuery(sprintf($this->_cache['rebuild.sql'], (int) $parentId)); $children = $this->_db->loadObjectList(); // The right value of this node is the left value + 1 $rightId = $leftId + 1; // Execute this function recursively over all children foreach ($children as $node) { /* * $rightId is the current right value, which is incremented on recursion return. * Increment the level for the children. * Add this item's alias to the path (but avoid a leading /) */ $rightId = $this->rebuild($node->{$this->_tbl_key}, $rightId, $level + 1, $path . (empty($path) ? '' : '/') . $node->alias); // If there is an update failure, return false to break out of the recursion. if ($rightId === false) { return false; } } // We've got the left value, and now that we've processed // the children of this node we also know the right value. $query->clear() ->update($this->_tbl) ->set('lft = ' . (int) $leftId) ->set('rgt = ' . (int) $rightId) ->set('level = ' . (int) $level) ->set('path = ' . $this->_db->quote($path)) ->where($this->_tbl_key . ' = ' . (int) $parentId); $this->_db->setQuery($query)->execute(); // Return the right value of this node + 1. return $rightId + 1; } /** * Method to rebuild the node's path field from the alias values of the nodes from the current node to the root node of the tree. * * @param integer $pk Primary key of the node for which to get the path. * * @return boolean True on success. * * @since 1.7.0 */ public function rebuildPath($pk = null) { $fields = $this->getFields(); // If there is no alias or path field, just return true. if (!array_key_exists('alias', $fields) || !array_key_exists('path', $fields)) { return true; } $k = $this->_tbl_key; $pk = (is_null($pk)) ? $this->$k : $pk; // Get the aliases for the path from the node to the root node. $query = $this->_db->getQuery(true) ->select('p.alias') ->from($this->_tbl . ' AS n, ' . $this->_tbl . ' AS p') ->where('n.lft BETWEEN p.lft AND p.rgt') ->where('n.' . $this->_tbl_key . ' = ' . (int) $pk) ->order('p.lft'); $this->_db->setQuery($query); $segments = $this->_db->loadColumn(); // Make sure to remove the root path if it exists in the list. if ($segments[0] == 'root') { array_shift($segments); } // Build the path. $path = trim(implode('/', $segments), ' /\\'); // Update the path field for the node. $query->clear() ->update($this->_tbl) ->set('path = ' . $this->_db->quote($path)) ->where($this->_tbl_key . ' = ' . (int) $pk); $this->_db->setQuery($query)->execute(); // Update the current record's path to the new one: $this->path = $path; return true; } /** * Method to reset class properties to the defaults set in the class * definition. It will ignore the primary key as well as any private class * properties (except $_errors). * * @return void * * @since 3.2.1 */ public function reset() { parent::reset(); // Reset the location properties. $this->setLocation(0); } /** * Method to update order of table rows * * @param array $idArray id numbers of rows to be reordered. * @param array $lftArray lft values of rows to be reordered. * * @return integer 1 + value of root rgt on success, false on failure. * * @since 1.7.0 * @throws \Exception on database error. */ public function saveorder($idArray = null, $lftArray = null) { try { $query = $this->_db->getQuery(true); // Validate arguments if (is_array($idArray) && is_array($lftArray) && count($idArray) == count($lftArray)) { for ($i = 0, $count = count($idArray); $i < $count; $i++) { // Do an update to change the lft values in the table for each id $query->clear() ->update($this->_tbl) ->where($this->_tbl_key . ' = ' . (int) $idArray[$i]) ->set('lft = ' . (int) $lftArray[$i]); $this->_db->setQuery($query)->execute(); if ($this->_debug) { $this->_logtable(); } } return $this->rebuild(); } else { return false; } } catch (\Exception $e) { $this->_unlock(); throw $e; } } /** * Method to recursive update published column for children rows. * * @param integer $pk Id number of row which published column was changed. * @param integer $newState An optional value for published column of row identified by $pk. * * @return boolean True on success. * * @since 3.7.0 * @throws \RuntimeException on database error. */ protected function recursiveUpdatePublishedColumn($pk, $newState = null) { $query = $this->_db->getQuery(true); $table = $this->_db->quoteName($this->_tbl); $key = $this->_db->quoteName($this->_tbl_key); $published = $this->_db->quoteName($this->getColumnAlias('published')); if ($newState !== null) { // Use a new published state in changed row. $newState = "(CASE WHEN p2.$key = " . (int) $pk . " THEN " . (int) $newState . " ELSE p2.$published END)"; } else { $newState = "p2.$published"; } /** * We have to calculate the correct value for c2.published * based on p2.published and own c2.published column, * where (p2) is parent category is and (c2) current category * * p2.published <= c2.published AND p2.published > 0 THEN c2.published * 2 <= 2 THEN 2 (If archived in archived then archived) * 1 <= 2 THEN 2 (If archived in published then archived) * 1 <= 1 THEN 1 (If published in published then published) * * p2.published > c2.published AND c2.published > 0 THEN p2.published * 2 > 1 THEN 2 (If published in archived then archived) * * p2.published > c2.published THEN c2.published ELSE p2.published * 2 > -2 THEN -2 (If trashed in archived then trashed) * 2 > 0 THEN 0 (If unpublished in archived then unpublished) * 1 > 0 THEN 0 (If unpublished in published then unpublished) * 0 > -2 THEN -2 (If trashed in unpublished then trashed) * ELSE * 0 <= 2 THEN 0 (If archived in unpublished then unpublished) * 0 <= 1 THEN 0 (If published in unpublished then unpublished) * 0 <= 0 THEN 0 (If unpublished in unpublished then unpublished) * -2 <= -2 THEN -2 (If trashed in trashed then trashed) * -2 <= 0 THEN -2 (If unpublished in trashed then trashed) * -2 <= 1 THEN -2 (If published in trashed then trashed) * -2 <= 2 THEN -2 (If archived in trashed then trashed) */ // Find node and all children keys $query->select("c.$key") ->from("$table AS node") ->leftJoin("$table AS c ON node.lft <= c.lft AND c.rgt <= node.rgt") ->where("node.$key = " . (int) $pk); $pks = $this->_db->setQuery($query)->loadColumn(); // Prepare a list of correct published states. $subquery = (string) $query->clear() ->select("c2.$key AS newId") ->select("CASE WHEN MIN($newState) > 0 THEN MAX($newState) ELSE MIN($newState) END AS newPublished") ->from("$table AS c2") ->innerJoin("$table AS p2 ON p2.lft <= c2.lft AND c2.rgt <= p2.rgt") ->where("c2.$key IN (" . implode(',', $pks) . ")") ->group("c2.$key"); // Update and cascade the publishing state. $query->clear() ->update("$table AS c") ->innerJoin("($subquery) AS c2 ON c2.newId = c.$key") ->set("$published = c2.newPublished") ->where("c.$key IN (" . implode(',', $pks) . ")"); $this->_runQuery($query, 'JLIB_DATABASE_ERROR_STORE_FAILED'); return true; } /** * Method to get nested set properties for a node in the tree. * * @param integer $id Value to look up the node by. * @param string $key An optional key to look up the node by (parent | left | right). * If omitted, the primary key of the table is used. * * @return mixed Boolean false on failure or node object on success. * * @since 1.7.0 * @throws \RuntimeException on database error. */ protected function _getNode($id, $key = null) { // Determine which key to get the node base on. switch ($key) { case 'parent': $k = 'parent_id'; break; case 'left': $k = 'lft'; break; case 'right': $k = 'rgt'; break; default: $k = $this->_tbl_key; break; } // Get the node data. $query = $this->_db->getQuery(true) ->select($this->_tbl_key . ', parent_id, level, lft, rgt') ->from($this->_tbl) ->where($k . ' = ' . (int) $id); $row = $this->_db->setQuery($query, 0, 1)->loadObject(); // Check for no $row returned if (empty($row)) { $e = new \UnexpectedValueException(sprintf('%s::_getNode(%d, %s) failed.', get_class($this), $id, $key)); $this->setError($e); return false; } // Do some simple calculations. $row->numChildren = (int) ($row->rgt - $row->lft - 1) / 2; $row->width = (int) $row->rgt - $row->lft + 1; return $row; } /** * Method to get various data necessary to make room in the tree at a location * for a node and its children. The returned data object includes conditions * for SQL WHERE clauses for updating left and right id values to make room for * the node as well as the new left and right ids for the node. * * @param object $referenceNode A node object with at least a 'lft' and 'rgt' with * which to make room in the tree around for a new node. * @param integer $nodeWidth The width of the node for which to make room in the tree. * @param string $position The position relative to the reference node where the room * should be made. * * @return mixed Boolean false on failure or data object on success. * * @since 1.7.0 */ protected function _getTreeRepositionData($referenceNode, $nodeWidth, $position = 'before') { // Make sure the reference an object with a left and right id. if (!is_object($referenceNode) || !(isset($referenceNode->lft) && isset($referenceNode->rgt))) { return false; } // A valid node cannot have a width less than 2. if ($nodeWidth < 2) { return false; } $k = $this->_tbl_key; $data = new \stdClass; // Run the calculations and build the data object by reference position. switch ($position) { case 'first-child': $data->left_where = 'lft > ' . $referenceNode->lft; $data->right_where = 'rgt >= ' . $referenceNode->lft; $data->new_lft = $referenceNode->lft + 1; $data->new_rgt = $referenceNode->lft + $nodeWidth; $data->new_parent_id = $referenceNode->$k; $data->new_level = $referenceNode->level + 1; break; case 'last-child': $data->left_where = 'lft > ' . ($referenceNode->rgt); $data->right_where = 'rgt >= ' . ($referenceNode->rgt); $data->new_lft = $referenceNode->rgt; $data->new_rgt = $referenceNode->rgt + $nodeWidth - 1; $data->new_parent_id = $referenceNode->$k; $data->new_level = $referenceNode->level + 1; break; case 'before': $data->left_where = 'lft >= ' . $referenceNode->lft; $data->right_where = 'rgt >= ' . $referenceNode->lft; $data->new_lft = $referenceNode->lft; $data->new_rgt = $referenceNode->lft + $nodeWidth - 1; $data->new_parent_id = $referenceNode->parent_id; $data->new_level = $referenceNode->level; break; default: case 'after': $data->left_where = 'lft > ' . $referenceNode->rgt; $data->right_where = 'rgt > ' . $referenceNode->rgt; $data->new_lft = $referenceNode->rgt + 1; $data->new_rgt = $referenceNode->rgt + $nodeWidth; $data->new_parent_id = $referenceNode->parent_id; $data->new_level = $referenceNode->level; break; } if ($this->_debug) { echo "\nRepositioning Data for $position" . "\n-----------------------------------" . "\nLeft Where: $data->left_where" . "\nRight Where: $data->right_where" . "\nNew Lft: $data->new_lft" . "\nNew Rgt: $data->new_rgt" . "\nNew Parent ID: $data->new_parent_id" . "\nNew Level: $data->new_level" . "\n"; } return $data; } /** * Method to create a log table in the buffer optionally showing the query and/or data. * * @param boolean $showData True to show data * @param boolean $showQuery True to show query * * @return void * * @codeCoverageIgnore * @since 1.7.0 */ protected function _logtable($showData = true, $showQuery = true) { $sep = "\n" . str_pad('', 40, '-'); $buffer = ''; if ($showQuery) { $buffer .= "\n" . htmlspecialchars($this->_db->getQuery(), ENT_QUOTES, 'UTF-8') . $sep; } if ($showData) { $query = $this->_db->getQuery(true) ->select($this->_tbl_key . ', parent_id, lft, rgt, level') ->from($this->_tbl) ->order($this->_tbl_key); $this->_db->setQuery($query); $rows = $this->_db->loadRowList(); $buffer .= sprintf("\n| %4s | %4s | %4s | %4s |", $this->_tbl_key, 'par', 'lft', 'rgt'); $buffer .= $sep; foreach ($rows as $row) { $buffer .= sprintf("\n| %4s | %4s | %4s | %4s |", $row[0], $row[1], $row[2], $row[3]); } $buffer .= $sep; } echo $buffer; } /** * Runs a query and unlocks the database on an error. * * @param mixed $query A string or \JDatabaseQuery object. * @param string $errorMessage Unused. * * @return boolean void * * @note Since 3.0.0 this method returns void and will rethrow the database exception. * @since 1.7.0 * @throws \Exception on database error. */ protected function _runQuery($query, $errorMessage) { // Prepare to catch an exception. try { $this->_db->setQuery($query)->execute(); if ($this->_debug) { $this->_logtable(); } } catch (\Exception $e) { // Unlock the tables and rethrow. $this->_unlock(); throw $e; } } } PK �V�[{��Y Y Observer/AbstractObserver.phpnu �[��� <?php /** * Joomla! Content Management System * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Table\Observer; defined('JPATH_PLATFORM') or die; use Joomla\CMS\Table\TableInterface; use Joomla\CMS\Table\Table; /** * Table class supporting modified pre-order tree traversal behavior. * * @since 3.1.2 */ abstract class AbstractObserver implements \JObserverInterface { /** * The observed table * * @var Table * @since 3.1.2 */ protected $table; /** * Constructor: Associates to $table $this observer * * @param TableInterface $table Table to be observed * * @since 3.1.2 */ public function __construct(TableInterface $table) { $table->attachObserver($this); $this->table = $table; } /** * Pre-processor for $table->load($keys, $reset) * * @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 void * * @since 3.1.2 */ public function onBeforeLoad($keys, $reset) { } /** * Post-processor for $table->load($keys, $reset) * * @param boolean &$result The result of the load * @param array $row The loaded (and already binded to $this->table) row of the database table * * @return void * * @since 3.1.2 */ public function onAfterLoad(&$result, $row) { } /** * Pre-processor for $table->store($updateNulls) * * @param boolean $updateNulls The result of the load * @param string $tableKey The key of the table * * @return void * * @since 3.1.2 */ public function onBeforeStore($updateNulls, $tableKey) { } /** * Post-processor for $table->store($updateNulls) * * @param boolean &$result The result of the store * * @return void * * @since 3.1.2 */ public function onAfterStore(&$result) { } /** * Pre-processor for $table->delete($pk) * * @param mixed $pk An optional primary key value to delete. If not set the instance property value is used. * * @return void * * @since 3.1.2 * @throws \UnexpectedValueException */ public function onBeforeDelete($pk) { } /** * Post-processor for $table->delete($pk) * * @param mixed $pk The deleted primary key value. * * @return void * * @since 3.1.2 */ public function onAfterDelete($pk) { } } PK �V�[lK�%L L Observer/ContentHistory.phpnu �[��� <?php /** * Joomla! Content Management System * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Table\Observer; defined('JPATH_PLATFORM') or die; /** * Table class supporting modified pre-order tree traversal behavior. * * @since 3.2 */ class ContentHistory extends AbstractObserver { /** * Helper object for storing and deleting version history information associated with this table observer * * @var \JHelperContenthistory * @since 3.2 */ protected $contenthistoryHelper; /** * The pattern for this table's TypeAlias * * @var string * @since 3.2 */ protected $typeAliasPattern = null; /** * Not public, so marking private and deprecated, but needed internally in parseTypeAlias for * PHP < 5.4.0 as it's not passing context $this to closure function. * * @var ContentHistory * @since 3.2 * @deprecated Never use this * @private */ public static $_myTableForPregreplaceOnly; /** * Creates the associated observer instance and attaches it to the $observableObject * Creates the associated content history helper class instance * $typeAlias can be of the form "{variableName}.type", automatically replacing {variableName} with table-instance variables variableName * * @param \JObservableInterface $observableObject The subject object to be observed * @param array $params ( 'typeAlias' => $typeAlias ) * * @return ContentHistory * * @since 3.2 */ public static function createObserver(\JObservableInterface $observableObject, $params = array()) { $typeAlias = $params['typeAlias']; $observer = new self($observableObject); $observer->contenthistoryHelper = new \JHelperContenthistory($typeAlias); $observer->typeAliasPattern = $typeAlias; return $observer; } /** * Post-processor for $table->store($updateNulls) * * @param boolean &$result The result of the load * * @return void * * @since 3.2 */ public function onAfterStore(&$result) { if ($result) { $this->parseTypeAlias(); $aliasParts = explode('.', $this->contenthistoryHelper->typeAlias); if (\JComponentHelper::getParams($aliasParts[0])->get('save_history', 0)) { $this->contenthistoryHelper->store($this->table); } } } /** * Pre-processor for $table->delete($pk) * * @param mixed $pk An optional primary key value to delete. If not set the instance property value is used. * * @return void * * @since 3.2 * @throws \UnexpectedValueException */ public function onBeforeDelete($pk) { $this->parseTypeAlias(); $aliasParts = explode('.', $this->contenthistoryHelper->typeAlias); if (\JComponentHelper::getParams($aliasParts[0])->get('save_history', 0)) { $this->parseTypeAlias(); $this->contenthistoryHelper->deleteHistory($this->table); } } /** * Internal method * Parses a TypeAlias of the form "{variableName}.type", replacing {variableName} with table-instance variables variableName * Storing result into $this->contenthistoryHelper->typeAlias * * @return void * * @since 3.2 */ protected function parseTypeAlias() { // Needed for PHP < 5.4.0 as it's not passing context $this to closure function static::$_myTableForPregreplaceOnly = $this->table; $this->contenthistoryHelper->typeAlias = preg_replace_callback('/{([^}]+)}/', function($matches) { return ContentHistory::$_myTableForPregreplaceOnly->{$matches[1]}; }, $this->typeAliasPattern ); } } PK �V�[:��� Observer/Tags.phpnu �[��� <?php /** * Joomla! Content Management System * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Table\Observer; defined('JPATH_PLATFORM') or die; /** * Abstract class defining methods that can be * implemented by an Observer class of a Table class (which is an Observable). * Attaches $this Observer to the $table in the constructor. * The classes extending this class should not be instantiated directly, as they * are automatically instanciated by the \JObserverMapper * * @since 3.1.2 */ class Tags extends AbstractObserver { /** * Helper object for managing tags * * @var \JHelperTags * @since 3.1.2 */ protected $tagsHelper; /** * The pattern for this table's TypeAlias * * @var string * @since 3.1.2 */ protected $typeAliasPattern = null; /** * Override for postStoreProcess param newTags, Set by setNewTags, used by onAfterStore and onBeforeStore * * @var array * @since 3.1.2 */ protected $newTags = false; /** * Override for postStoreProcess param replaceTags. Set by setNewTags, used by onAfterStore * * @var boolean * @since 3.1.2 */ protected $replaceTags = true; /** * Not public, so marking private and deprecated, but needed internally in parseTypeAlias for * PHP < 5.4.0 as it's not passing context $this to closure function. * * @var Tags * @since 3.1.2 * @deprecated Never use this * @private */ public static $_myTableForPregreplaceOnly; /** * Creates the associated observer instance and attaches it to the $observableObject * Creates the associated tags helper class instance * $typeAlias can be of the form "{variableName}.type", automatically replacing {variableName} with table-instance variables variableName * * @param \JObservableInterface $observableObject The subject object to be observed * @param array $params ( 'typeAlias' => $typeAlias ) * * @return Tags * * @since 3.1.2 */ public static function createObserver(\JObservableInterface $observableObject, $params = array()) { $typeAlias = $params['typeAlias']; $observer = new self($observableObject); $observer->tagsHelper = new \JHelperTags; $observer->typeAliasPattern = $typeAlias; return $observer; } /** * Pre-processor for $table->store($updateNulls) * * @param boolean $updateNulls The result of the load * @param string $tableKey The key of the table * * @return void * * @since 3.1.2 */ public function onBeforeStore($updateNulls, $tableKey) { $this->parseTypeAlias(); if (empty($this->table->tagsHelper->tags)) { $this->tagsHelper->preStoreProcess($this->table); } else { $this->tagsHelper->preStoreProcess($this->table, (array) $this->table->tagsHelper->tags); } } /** * Post-processor for $table->store($updateNulls) * You can change optional params newTags and replaceTags of tagsHelper with method setNewTagsToAdd * * @param boolean &$result The result of the load * * @return void * * @since 3.1.2 */ public function onAfterStore(&$result) { if ($result) { if (empty($this->table->tagsHelper->tags)) { $result = $this->tagsHelper->postStoreProcess($this->table); } else { $result = $this->tagsHelper->postStoreProcess($this->table, $this->table->tagsHelper->tags); } // Restore default values for the optional params: $this->newTags = array(); $this->replaceTags = true; } } /** * Pre-processor for $table->delete($pk) * * @param mixed $pk An optional primary key value to delete. If not set the instance property value is used. * * @return void * * @since 3.1.2 * @throws \UnexpectedValueException */ public function onBeforeDelete($pk) { $this->parseTypeAlias(); $this->tagsHelper->deleteTagData($this->table, $pk); } /** * Sets the new tags to be added or to replace existing tags * * @param array $newTags New tags to be added to or replace current tags for an item * @param boolean $replaceTags Replace tags (true) or add them (false) * * @return boolean * * @since 3.1.2 */ public function setNewTags($newTags, $replaceTags) { $this->parseTypeAlias(); return $this->tagsHelper->postStoreProcess($this->table, $newTags, $replaceTags); } /** * Internal method * Parses a TypeAlias of the form "{variableName}.type", replacing {variableName} with table-instance variables variableName * Storing result into $this->tagsHelper->typeAlias * * @return void * * @since 3.1.2 */ protected function parseTypeAlias() { // Needed for PHP < 5.4.0 as it's not passing context $this to closure function static::$_myTableForPregreplaceOnly = $this->table; $this->tagsHelper->typeAlias = preg_replace_callback('/{([^}]+)}/', function($matches) { return Tags::$_myTableForPregreplaceOnly->{$matches[1]}; }, $this->typeAliasPattern ); } } PK �V�[���� � Table.phpnu �[��� <?php /** * Joomla! Content Management System * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Table; defined('JPATH_PLATFORM') or die; \JLoader::import('joomla.filesystem.path'); /** * Abstract Table class * * Parent class to all tables. * * @since 1.7.0 * @tutorial Joomla.Platform/jtable.cls */ abstract class Table extends \JObject implements \JObservableInterface, \JTableInterface { /** * Include paths for searching for Table classes. * * @var array * @since 3.0.0 */ private static $_includePaths = array(); /** * Name of the database table to model. * * @var string * @since 1.7.0 */ protected $_tbl = ''; /** * Name of the primary key field in the table. * * @var string * @since 1.7.0 */ protected $_tbl_key = ''; /** * Name of the primary key fields in the table. * * @var array * @since 3.0.1 */ protected $_tbl_keys = array(); /** * \JDatabaseDriver object. * * @var \JDatabaseDriver * @since 1.7.0 */ protected $_db; /** * Should rows be tracked as ACL assets? * * @var boolean * @since 1.7.0 */ protected $_trackAssets = false; /** * The rules associated with this record. * * @var \JAccessRules A \JAccessRules object. * @since 1.7.0 */ protected $_rules; /** * Indicator that the tables have been locked. * * @var boolean * @since 1.7.0 */ protected $_locked = false; /** * Indicates that the primary keys autoincrement. * * @var boolean * @since 3.1.4 */ protected $_autoincrement = true; /** * Generic observers for this Table (Used e.g. for tags Processing) * * @var \JObserverUpdater * @since 3.1.2 */ protected $_observers; /** * Array with alias for "special" columns such as ordering, hits etc etc * * @var array * @since 3.4.0 */ protected $_columnAlias = array(); /** * An array of key names to be json encoded in the bind function * * @var array * @since 3.3 */ protected $_jsonEncode = array(); /** * Object constructor to set table and key fields. In most cases this will * be overridden by child classes to explicitly set the table and key fields * for a particular database table. * * @param string $table Name of the table to model. * @param mixed $key Name of the primary key field in the table or array of field names that compose the primary key. * @param \JDatabaseDriver $db \JDatabaseDriver object. * * @since 1.7.0 */ public function __construct($table, $key, $db) { // Set internal variables. $this->_tbl = $table; // Set the key to be an array. if (is_string($key)) { $key = array($key); } elseif (is_object($key)) { $key = (array) $key; } $this->_tbl_keys = $key; if (count($key) == 1) { $this->_autoincrement = true; } else { $this->_autoincrement = false; } // Set the singular table key for backwards compatibility. $this->_tbl_key = $this->getKeyName(); $this->_db = $db; // Initialise the table properties. $fields = $this->getFields(); if ($fields) { foreach ($fields as $name => $v) { // Add the field if it is not already present. if (!property_exists($this, $name)) { $this->$name = null; } } } // If we are tracking assets, make sure an access field exists and initially set the default. if (property_exists($this, 'asset_id')) { $this->_trackAssets = true; } // If the access property exists, set the default. if (property_exists($this, 'access')) { $this->access = (int) \JFactory::getConfig()->get('access'); } // Implement \JObservableInterface: // Create observer updater and attaches all observers interested by $this class: $this->_observers = new \JObserverUpdater($this); \JObserverMapper::attachAllObservers($this); } /** * Implement \JObservableInterface: * Adds an observer to this instance. * This method will be called fron the constructor of classes implementing \JObserverInterface * which is instanciated by the constructor of $this with \JObserverMapper::attachAllObservers($this) * * @param \JObserverInterface|\JTableObserver $observer The observer object * * @return void * * @since 3.1.2 */ public function attachObserver(\JObserverInterface $observer) { $this->_observers->attachObserver($observer); } /** * Gets the instance of the observer of class $observerClass * * @param string $observerClass The observer class-name to return the object of * * @return \JTableObserver|null * * @since 3.1.2 */ public function getObserverOfClass($observerClass) { return $this->_observers->getObserverOfClass($observerClass); } /** * Get the columns from database table. * * @param bool $reload flag to reload cache * * @return mixed An array of the field names, or false if an error occurs. * * @since 1.7.0 * @throws \UnexpectedValueException */ public function getFields($reload = false) { static $cache = null; if ($cache === null || $reload) { // Lookup the fields for this table only once. $name = $this->_tbl; $fields = $this->_db->getTableColumns($name, false); if (empty($fields)) { throw new \UnexpectedValueException(sprintf('No columns found for %s table', $name)); } $cache = $fields; } return $cache; } /** * Static method to get an instance of a Table class if it can be found in the table include paths. * * To add include paths for searching for Table classes see Table::addIncludePath(). * * @param string $type The type (name) of the Table class to get an instance of. * @param string $prefix An optional prefix for the table class name. * @param array $config An optional array of configuration values for the Table object. * * @return Table|boolean A Table object if found or boolean false on failure. * * @since 1.7.0 */ public static function getInstance($type, $prefix = 'JTable', $config = array()) { // Sanitize and prepare the table class name. $type = preg_replace('/[^A-Z0-9_\.-]/i', '', $type); $tableClass = $prefix . ucfirst($type); // Only try to load the class if it doesn't already exist. if (!class_exists($tableClass)) { // Search for the class file in the JTable include paths. jimport('joomla.filesystem.path'); $paths = self::addIncludePath(); $pathIndex = 0; while (!class_exists($tableClass) && $pathIndex < count($paths)) { if ($tryThis = \JPath::find($paths[$pathIndex++], strtolower($type) . '.php')) { // Import the class file. include_once $tryThis; } } if (!class_exists($tableClass)) { /* * If unable to find the class file in the Table include paths. Return false. * The warning JLIB_DATABASE_ERROR_NOT_SUPPORTED_FILE_NOT_FOUND has been removed in 3.6.3. * In 4.0 an Exception (type to be determined) will be thrown. * For more info see https://github.com/joomla/joomla-cms/issues/11570 */ return false; } } // If a database object was passed in the configuration array use it, otherwise get the global one from \JFactory. $db = isset($config['dbo']) ? $config['dbo'] : \JFactory::getDbo(); // Instantiate a new table class and return it. return new $tableClass($db); } /** * Add a filesystem path where Table should search for table class files. * * @param array|string $path A filesystem path or array of filesystem paths to add. * * @return array An array of filesystem paths to find Table classes in. * * @since 1.7.0 */ public static function addIncludePath($path = null) { // If the internal paths have not been initialised, do so with the base table path. if (empty(self::$_includePaths)) { self::$_includePaths = array(__DIR__); } // Convert the passed path(s) to add to an array. settype($path, 'array'); // If we have new paths to add, do so. if (!empty($path)) { // Check and add each individual new path. foreach ($path as $dir) { // Sanitize path. $dir = trim($dir); // Add to the front of the list so that custom paths are searched first. if (!in_array($dir, self::$_includePaths)) { array_unshift(self::$_includePaths, $dir); } } } return self::$_includePaths; } /** * Method to compute the default name of the asset. * The default name is in the form table_name.id * where id is the value of the primary key of the table. * * @return string * * @since 1.7.0 */ protected function _getAssetName() { $keys = array(); foreach ($this->_tbl_keys as $k) { $keys[] = (int) $this->$k; } return $this->_tbl . '.' . implode('.', $keys); } /** * Method to return the title to use for the asset table. * * In tracking the assets a title is kept for each asset so that there is some context available in a unified access manager. * Usually this would just return $this->title or $this->name or whatever is being used for the primary name of the row. * If this method is not overridden, the asset name is used. * * @return string The string to use as the title in the asset table. * * @since 1.7.0 */ protected function _getAssetTitle() { return $this->_getAssetName(); } /** * Method to get the parent asset under which to register this one. * * By default, all assets are registered to the ROOT node with ID, which will default to 1 if none exists. * An extended class can define a table and ID to lookup. If the asset does not exist it will be created. * * @param Table $table A Table object for the asset parent. * @param integer $id Id to look up * * @return integer * * @since 1.7.0 */ protected function _getAssetParentId(Table $table = null, $id = null) { // For simple cases, parent to the asset root. /** @var \JTableAsset $assets */ $assets = self::getInstance('Asset', 'JTable', array('dbo' => $this->getDbo())); $rootId = $assets->getRootId(); if (!empty($rootId)) { return $rootId; } return 1; } /** * Method to append the primary keys for this table to a query. * * @param \JDatabaseQuery $query A query object to append. * @param mixed $pk Optional primary key parameter. * * @return void * * @since 3.1.4 */ public function appendPrimaryKeys($query, $pk = null) { if (is_null($pk)) { foreach ($this->_tbl_keys as $k) { $query->where($this->_db->quoteName($k) . ' = ' . $this->_db->quote($this->$k)); } } else { if (is_string($pk)) { $pk = array($this->_tbl_key => $pk); } $pk = (object) $pk; foreach ($this->_tbl_keys as $k) { $query->where($this->_db->quoteName($k) . ' = ' . $this->_db->quote($pk->$k)); } } } /** * Method to get the database table name for the class. * * @return string The name of the database table being modeled. * * @since 1.7.0 */ public function getTableName() { return $this->_tbl; } /** * Method to get the primary key field name for the table. * * @param boolean $multiple True to return all primary keys (as an array) or false to return just the first one (as a string). * * @return mixed Array of primary key field names or string containing the first primary key field. * * @since 1.7.0 */ public function getKeyName($multiple = false) { // Count the number of keys if (count($this->_tbl_keys)) { if ($multiple) { // If we want multiple keys, return the raw array. return $this->_tbl_keys; } else { // If we want the standard method, just return the first key. return $this->_tbl_keys[0]; } } return ''; } /** * Method to get the \JDatabaseDriver object. * * @return \JDatabaseDriver The internal database driver object. * * @since 1.7.0 */ public function getDbo() { return $this->_db; } /** * Method to set the \JDatabaseDriver object. * * @param \JDatabaseDriver $db A \JDatabaseDriver object to be used by the table object. * * @return boolean True on success. * * @since 1.7.0 */ public function setDbo($db) { $this->_db = $db; return true; } /** * Method to set rules for the record. * * @param mixed $input A \JAccessRules object, JSON string, or array. * * @return void * * @since 1.7.0 */ public function setRules($input) { if ($input instanceof \JAccessRules) { $this->_rules = $input; } else { $this->_rules = new \JAccessRules($input); } } /** * Method to get the rules for the record. * * @return \JAccessRules object * * @since 1.7.0 */ public function getRules() { return $this->_rules; } /** * Method to reset class properties to the defaults set in the class * definition. It will ignore the primary key as well as any private class * properties (except $_errors). * * @return void * * @since 1.7.0 */ public function reset() { // Get the default values for the class from the table. foreach ($this->getFields() as $k => $v) { // If the property is not the primary key or private, reset it. if (!in_array($k, $this->_tbl_keys) && (strpos($k, '_') !== 0)) { $this->$k = $v->Default; } } // Reset table errors $this->_errors = array(); } /** * Method to bind an associative array or object to the Table instance.This * method only binds properties that are publicly accessible and optionally * takes an array of properties to ignore when binding. * * @param array|object $src An associative array or object to bind to the Table instance. * @param array|string $ignore An optional array or space separated list of properties to ignore while binding. * * @return boolean True on success. * * @since 1.7.0 * @throws \InvalidArgumentException */ public function bind($src, $ignore = array()) { // JSON encode any fields required if (!empty($this->_jsonEncode)) { foreach ($this->_jsonEncode as $field) { if (isset($src[$field]) && is_array($src[$field])) { $src[$field] = json_encode($src[$field]); } } } // Check if the source value is an array or object if (!is_object($src) && !is_array($src)) { throw new \InvalidArgumentException( sprintf( 'Could not bind the data source in %1$s::bind(), the source must be an array or object but a "%2$s" was given.', get_class($this), gettype($src) ) ); } // If the source value is an object, get its accessible properties. if (is_object($src)) { $src = get_object_vars($src); } // If the ignore value is a string, explode it over spaces. if (!is_array($ignore)) { $ignore = explode(' ', $ignore); } // Bind the source value, excluding the ignored fields. foreach ($this->getProperties() as $k => $v) { // Only process fields not in the ignore array. if (!in_array($k, $ignore)) { if (isset($src[$k])) { $this->$k = $src[$k]; } } } return true; } /** * Method to load a row from the database by primary key and bind the fields to the Table 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. * * @since 1.7.0 * @throws \InvalidArgumentException * @throws \RuntimeException * @throws \UnexpectedValueException */ public function load($keys = null, $reset = true) { // Implement \JObservableInterface: Pre-processing by observers $this->_observers->update('onBeforeLoad', array($keys, $reset)); if (empty($keys)) { $empty = true; $keys = array(); // If empty, use the value of the current key foreach ($this->_tbl_keys as $key) { $empty = $empty && empty($this->$key); $keys[$key] = $this->$key; } // If empty primary key there's is no need to load anything if ($empty) { return true; } } elseif (!is_array($keys)) { // Load by primary key. $keyCount = count($this->_tbl_keys); if ($keyCount) { if ($keyCount > 1) { throw new \InvalidArgumentException('Table has multiple primary keys specified, only one primary key value provided.'); } $keys = array($this->getKeyName() => $keys); } else { throw new \RuntimeException('No table keys defined.'); } } if ($reset) { $this->reset(); } // Initialise the query. $query = $this->_db->getQuery(true) ->select('*') ->from($this->_tbl); $fields = array_keys($this->getProperties()); foreach ($keys as $field => $value) { // Check that $field is in the table. if (!in_array($field, $fields)) { throw new \UnexpectedValueException(sprintf('Missing field in database: %s   %s.', get_class($this), $field)); } // Add the search tuple to the query. $query->where($this->_db->quoteName($field) . ' = ' . $this->_db->quote($value)); } $this->_db->setQuery($query); $row = $this->_db->loadAssoc(); // Check that we have a result. if (empty($row)) { $result = false; } else { // Bind the object with the row and return. $result = $this->bind($row); } // Implement \JObservableInterface: Post-processing by observers $this->_observers->update('onAfterLoad', array(&$result, $row)); return $result; } /** * Method to perform sanity checks on the Table instance properties to ensure they are safe to store in the database. * * Child classes should override this method to make sure the data they are storing in the database is safe and as expected before storage. * * @return boolean True if the instance is sane and able to be stored in the database. * * @since 1.7.0 */ public function check() { return true; } /** * Method to store a row in the database from the Table instance properties. * * If a primary key value is set the row with that primary key value will be updated with the instance property values. * If no primary key value is set a new row will be inserted into the database with the properties from the Table instance. * * @param boolean $updateNulls True to update fields even if they are null. * * @return boolean True on success. * * @since 1.7.0 */ public function store($updateNulls = false) { $result = true; $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()) { $this->_db->updateObject($this->_tbl, $this, $this->_tbl_keys, $updateNulls); } else { $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($updateNulls)) { $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; } /** * Method to provide a shortcut to binding, checking and storing a Table instance to the database table. * * The method will check a row in once the data has been stored and if an ordering filter is present will attempt to reorder * the table rows based on the filter. The ordering filter is an instance property name. The rows that will be reordered * are those whose value matches the Table instance for the property specified. * * @param array|object $src An associative array or object to bind to the Table instance. * @param string $orderingFilter Filter for the order updating * @param array|string $ignore An optional array or space separated list of properties to ignore while binding. * * @return boolean True on success. * * @since 1.7.0 */ public function save($src, $orderingFilter = '', $ignore = '') { // Attempt to bind the source to the instance. if (!$this->bind($src, $ignore)) { return false; } // Run any sanity checks on the instance and verify that it is ready for storage. if (!$this->check()) { return false; } // Attempt to store the properties to the database table. if (!$this->store()) { return false; } // Attempt to check the row in, just in case it was checked out. if (!$this->checkin()) { return false; } // If an ordering filter is set, attempt reorder the rows in the table based on the filter and value. if ($orderingFilter) { $filterValue = $this->$orderingFilter; $this->reorder($orderingFilter ? $this->_db->quoteName($orderingFilter) . ' = ' . $this->_db->quote($filterValue) : ''); } // Set the error to empty and return true. $this->setError(''); return true; } /** * Method to delete a row from the database table by primary key value. * * @param mixed $pk An optional primary key value to delete. If not set the instance property value is used. * * @return boolean True on success. * * @since 1.7.0 * @throws \UnexpectedValueException */ public function delete($pk = null) { if (is_null($pk)) { $pk = array(); foreach ($this->_tbl_keys as $key) { $pk[$key] = $this->$key; } } elseif (!is_array($pk)) { $pk = array($this->_tbl_key => $pk); } foreach ($this->_tbl_keys as $key) { $pk[$key] = is_null($pk[$key]) ? $this->$key : $pk[$key]; if ($pk[$key] === null) { throw new \UnexpectedValueException('Null primary key not allowed.'); } $this->$key = $pk[$key]; } // Implement \JObservableInterface: Pre-processing by observers $this->_observers->update('onBeforeDelete', array($pk)); // If tracking assets, remove the asset first. if ($this->_trackAssets) { // Get the asset name $name = $this->_getAssetName(); /** @var \JTableAsset $asset */ $asset = self::getInstance('Asset'); if ($asset->loadByName($name)) { if (!$asset->delete()) { $this->setError($asset->getError()); return false; } } } // Delete the row by primary key. $query = $this->_db->getQuery(true) ->delete($this->_tbl); $this->appendPrimaryKeys($query, $pk); $this->_db->setQuery($query); // Check for a database error. $this->_db->execute(); // Implement \JObservableInterface: Post-processing by observers $this->_observers->update('onAfterDelete', array($pk)); return true; } /** * Method to check a row out if the necessary properties/fields exist. * * To prevent race conditions while editing rows in a database, a row can be checked out if the fields 'checked_out' and 'checked_out_time' * are available. While a row is checked out, any attempt to store the row by a user other than the one who checked the row out should be * held until the row is checked in again. * * @param integer $userId The Id of the user checking out the row. * @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. * * @since 1.7.0 * @throws \UnexpectedValueException */ public function checkOut($userId, $pk = null) { $checkedOutField = $this->getColumnAlias('checked_out'); $checkedOutTimeField = $this->getColumnAlias('checked_out_time'); // If there is no checked_out or checked_out_time field, just return true. if (!property_exists($this, $checkedOutField) || !property_exists($this, $checkedOutTimeField)) { return true; } if (is_null($pk)) { $pk = array(); foreach ($this->_tbl_keys as $key) { $pk[$key] = $this->$key; } } elseif (!is_array($pk)) { $pk = array($this->_tbl_key => $pk); } foreach ($this->_tbl_keys as $key) { $pk[$key] = is_null($pk[$key]) ? $this->$key : $pk[$key]; if ($pk[$key] === null) { throw new \UnexpectedValueException('Null primary key not allowed.'); } } // Get the current time in the database format. $time = \JFactory::getDate()->toSql(); // Check the row out by primary key. $query = $this->_db->getQuery(true) ->update($this->_tbl) ->set($this->_db->quoteName($checkedOutField) . ' = ' . (int) $userId) ->set($this->_db->quoteName($checkedOutTimeField) . ' = ' . $this->_db->quote($time)); $this->appendPrimaryKeys($query, $pk); $this->_db->setQuery($query); $this->_db->execute(); // Set table values in the object. $this->$checkedOutField = (int) $userId; $this->$checkedOutTimeField = $time; return true; } /** * Method to check a row in if the necessary properties/fields exist. * * Checking a row in will allow other users the ability to edit the row. * * @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. * * @since 1.7.0 * @throws \UnexpectedValueException */ public function checkIn($pk = null) { $checkedOutField = $this->getColumnAlias('checked_out'); $checkedOutTimeField = $this->getColumnAlias('checked_out_time'); // If there is no checked_out or checked_out_time field, just return true. if (!property_exists($this, $checkedOutField) || !property_exists($this, $checkedOutTimeField)) { return true; } if (is_null($pk)) { $pk = array(); foreach ($this->_tbl_keys as $key) { $pk[$this->$key] = $this->$key; } } elseif (!is_array($pk)) { $pk = array($this->_tbl_key => $pk); } foreach ($this->_tbl_keys as $key) { $pk[$key] = empty($pk[$key]) ? $this->$key : $pk[$key]; if ($pk[$key] === null) { throw new \UnexpectedValueException('Null primary key not allowed.'); } } // Check the row in by primary key. $query = $this->_db->getQuery(true) ->update($this->_tbl) ->set($this->_db->quoteName($checkedOutField) . ' = 0') ->set($this->_db->quoteName($checkedOutTimeField) . ' = ' . $this->_db->quote($this->_db->getNullDate())); $this->appendPrimaryKeys($query, $pk); $this->_db->setQuery($query); // Check for a database error. $this->_db->execute(); // Set table values in the object. $this->$checkedOutField = 0; $this->$checkedOutTimeField = ''; $dispatcher = \JEventDispatcher::getInstance(); $dispatcher->trigger('onAfterCheckin', array($this->_tbl)); return true; } /** * Validate that the primary key has been set. * * @return boolean True if the primary key(s) have been set. * * @since 3.1.4 */ public function hasPrimaryKey() { if ($this->_autoincrement) { $empty = true; foreach ($this->_tbl_keys as $key) { $empty = $empty && empty($this->$key); } } else { $query = $this->_db->getQuery(true) ->select('COUNT(*)') ->from($this->_tbl); $this->appendPrimaryKeys($query); $this->_db->setQuery($query); $count = $this->_db->loadResult(); if ($count == 1) { $empty = false; } else { $empty = true; } } return !$empty; } /** * Method to increment the hits for a row if the necessary property/field exists. * * @param mixed $pk An optional primary key value to increment. If not set the instance property value is used. * * @return boolean True on success. * * @since 1.7.0 * @throws \UnexpectedValueException */ public function hit($pk = null) { $hitsField = $this->getColumnAlias('hits'); // If there is no hits field, just return true. if (!property_exists($this, $hitsField)) { return true; } if (is_null($pk)) { $pk = array(); foreach ($this->_tbl_keys as $key) { $pk[$key] = $this->$key; } } elseif (!is_array($pk)) { $pk = array($this->_tbl_key => $pk); } foreach ($this->_tbl_keys as $key) { $pk[$key] = is_null($pk[$key]) ? $this->$key : $pk[$key]; if ($pk[$key] === null) { throw new \UnexpectedValueException('Null primary key not allowed.'); } } // Check the row in by primary key. $query = $this->_db->getQuery(true) ->update($this->_tbl) ->set($this->_db->quoteName($hitsField) . ' = (' . $this->_db->quoteName($hitsField) . ' + 1)'); $this->appendPrimaryKeys($query, $pk); $this->_db->setQuery($query); $this->_db->execute(); // Set table values in the object. $this->hits++; return true; } /** * Method to determine if a row is checked out and therefore uneditable by a user. * * If the row is checked out by the same user, then it is considered not checked out -- as the user can still edit it. * * @param integer $with The user ID to preform the match with, if an item is checked out by this user the function will return false. * @param integer $against The user ID to perform the match against when the function is used as a static function. * * @return boolean True if checked out. * * @since 1.7.0 */ public function isCheckedOut($with = 0, $against = null) { // Handle the non-static case. if (isset($this) && ($this instanceof Table) && is_null($against)) { $checkedOutField = $this->getColumnAlias('checked_out'); $against = $this->get($checkedOutField); } // The item is not checked out or is checked out by the same user. if (!$against || ($against == $with)) { return false; } $db = \JFactory::getDbo(); $query = $db->getQuery(true) ->select('COUNT(userid)') ->from($db->quoteName('#__session')) ->where($db->quoteName('userid') . ' = ' . (int) $against); $db->setQuery($query); $checkedOut = (boolean) $db->loadResult(); // If a session exists for the user then it is checked out. return $checkedOut; } /** * Method to get the next ordering value for a group of rows defined by an SQL WHERE clause. * * This is useful for placing a new item last in a group of items in the table. * * @param string $where WHERE clause to use for selecting the MAX(ordering) for the table. * * @return integer The next ordering value. * * @since 1.7.0 * @throws \UnexpectedValueException */ public function getNextOrder($where = '') { // Check if there is an ordering field set $orderingField = $this->getColumnAlias('ordering'); if (!property_exists($this, $orderingField)) { throw new \UnexpectedValueException(sprintf('%s does not support ordering.', get_class($this))); } // Get the largest ordering value for a given where clause. $query = $this->_db->getQuery(true) ->select('MAX(' . $this->_db->quoteName($orderingField) . ')') ->from($this->_tbl); if ($where) { $query->where($where); } $this->_db->setQuery($query); $max = (int) $this->_db->loadResult(); // Return the largest ordering value + 1. return $max + 1; } /** * Get the primary key values for this table using passed in values as a default. * * @param array $keys Optional primary key values to use. * * @return array An array of primary key names and values. * * @since 3.1.4 */ public function getPrimaryKey(array $keys = array()) { foreach ($this->_tbl_keys as $key) { if (!isset($keys[$key])) { if (!empty($this->$key)) { $keys[$key] = $this->$key; } } } return $keys; } /** * Method to compact the ordering values of rows in a group of rows defined by an SQL WHERE clause. * * @param string $where WHERE clause to use for limiting the selection of rows to compact the ordering values. * * @return mixed Boolean True on success. * * @since 1.7.0 * @throws \UnexpectedValueException */ public function reorder($where = '') { // Check if there is an ordering field set $orderingField = $this->getColumnAlias('ordering'); if (!property_exists($this, $orderingField)) { throw new \UnexpectedValueException(sprintf('%s does not support ordering.', get_class($this))); } $quotedOrderingField = $this->_db->quoteName($orderingField); $subquery = $this->_db->getQuery(true) ->from($this->_tbl) ->selectRowNumber($quotedOrderingField, 'new_ordering'); $query = $this->_db->getQuery(true) ->update($this->_tbl) ->set($quotedOrderingField . ' = sq.new_ordering'); $innerOn = array(); // Get the primary keys for the selection. foreach ($this->_tbl_keys as $i => $k) { $subquery->select($this->_db->quoteName($k, 'pk__' . $i)); $innerOn[] = $this->_db->quoteName($k) . ' = sq.' . $this->_db->quoteName('pk__' . $i); } // Setup the extra where and ordering clause data. if ($where) { $subquery->where($where); $query->where($where); } $subquery->where($quotedOrderingField . ' >= 0'); $query->where($quotedOrderingField . ' >= 0'); $query->innerJoin('(' . (string) $subquery . ') AS sq ON ' . implode(' AND ', $innerOn)); $this->_db->setQuery($query); $this->_db->execute(); return true; } /** * Method to move a row in the ordering sequence of a group of rows defined by an SQL WHERE clause. * * Negative numbers move the row up in the sequence and positive numbers move it down. * * @param integer $delta The direction and magnitude to move the row in the ordering sequence. * @param string $where WHERE clause to use for limiting the selection of rows to compact the ordering values. * * @return boolean True on success. * * @since 1.7.0 * @throws \UnexpectedValueException */ public function move($delta, $where = '') { // Check if there is an ordering field set $orderingField = $this->getColumnAlias('ordering'); if (!property_exists($this, $orderingField)) { throw new \UnexpectedValueException(sprintf('%s does not support ordering.', get_class($this))); } $quotedOrderingField = $this->_db->quoteName($orderingField); // If the change is none, do nothing. if (empty($delta)) { return true; } $row = null; $query = $this->_db->getQuery(true); // Select the primary key and ordering values from the table. $query->select(implode(',', $this->_tbl_keys) . ', ' . $quotedOrderingField) ->from($this->_tbl); // If the movement delta is negative move the row up. if ($delta < 0) { $query->where($quotedOrderingField . ' < ' . (int) $this->$orderingField) ->order($quotedOrderingField . ' DESC'); } // If the movement delta is positive move the row down. elseif ($delta > 0) { $query->where($quotedOrderingField . ' > ' . (int) $this->$orderingField) ->order($quotedOrderingField . ' ASC'); } // Add the custom WHERE clause if set. if ($where) { $query->where($where); } // Select the first row with the criteria. $this->_db->setQuery($query, 0, 1); $row = $this->_db->loadObject(); // If a row is found, move the item. if (!empty($row)) { // Update the ordering field for this instance to the row's ordering value. $query->clear() ->update($this->_tbl) ->set($quotedOrderingField . ' = ' . (int) $row->$orderingField); $this->appendPrimaryKeys($query); $this->_db->setQuery($query); $this->_db->execute(); // Update the ordering field for the row to this instance's ordering value. $query->clear() ->update($this->_tbl) ->set($quotedOrderingField . ' = ' . (int) $this->$orderingField); $this->appendPrimaryKeys($query, $row); $this->_db->setQuery($query); $this->_db->execute(); // Update the instance value. $this->$orderingField = $row->$orderingField; } else { // Update the ordering field for this instance. $query->clear() ->update($this->_tbl) ->set($quotedOrderingField . ' = ' . (int) $this->$orderingField); $this->appendPrimaryKeys($query); $this->_db->setQuery($query); $this->_db->execute(); } 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. * * @since 1.7.0 */ public function publish($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); } $publishedField = $this->getColumnAlias('published'); $checkedOutField = $this->getColumnAlias('checked_out'); 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($publishedField) . ' = ' . (int) $state); // If publishing, set published date/time if not previously set if ($state && property_exists($this, 'publish_up') && (int) $this->publish_up == 0) { $nowDate = $this->_db->quote(\JFactory::getDate()->toSql()); $query->set($this->_db->quoteName($this->getColumnAlias('publish_up')) . ' = ' . $nowDate); } // Determine if there is checkin support for the table. if (property_exists($this, 'checked_out') || property_exists($this, 'checked_out_time')) { $query->where( '(' . $this->_db->quoteName($checkedOutField) . ' = 0' . ' OR ' . $this->_db->quoteName($checkedOutField) . ' = ' . (int) $userId . ' OR ' . $this->_db->quoteName($checkedOutField) . ' IS NULL' . ')' ); $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 Table 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) { $this->$publishedField = $state; } } $this->setError(''); return true; } /** * Method to lock the database table for writing. * * @return boolean True on success. * * @since 1.7.0 * @throws \RuntimeException */ protected function _lock() { $this->_db->lockTable($this->_tbl); $this->_locked = true; return true; } /** * Method to return the real name of a "special" column such as ordering, hits, published * etc etc. In this way you are free to follow your db naming convention and use the * built in \Joomla functions. * * @param string $column Name of the "special" column (ie ordering, hits) * * @return string The string that identify the special * * @since 3.4 */ public function getColumnAlias($column) { // Get the column data if set if (isset($this->_columnAlias[$column])) { $return = $this->_columnAlias[$column]; } else { $return = $column; } // Sanitize the name $return = preg_replace('#[^A-Z0-9_]#i', '', $return); return $return; } /** * Method to register a column alias for a "special" column. * * @param string $column The "special" column (ie ordering) * @param string $columnAlias The real column name (ie foo_ordering) * * @return void * * @since 3.4 */ public function setColumnAlias($column, $columnAlias) { // Santize the column name alias $column = strtolower($column); $column = preg_replace('#[^A-Z0-9_]#i', '', $column); // Set the column alias internally $this->_columnAlias[$column] = $columnAlias; } /** * Method to unlock the database table for writing. * * @return boolean True on success. * * @since 1.7.0 */ protected function _unlock() { $this->_db->unlockTables(); $this->_locked = false; return true; } /** * Check if the record has a property (applying a column alias if it exists) * * @param string $key key to be checked * * @return boolean * * @since 3.9.11 */ public function hasField($key) { $key = $this->getColumnAlias($key); return property_exists($this, $key); } } PK �V�[��W� � TableInterface.phpnu �[��� <?php /** * Joomla! Content Management System * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Table; defined('JPATH_PLATFORM') or die; /** * Table class interface. * * @since 3.2 */ interface TableInterface { /** * Method to bind an associative array or object to the TableInterface 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 TableInterface instance. * @param mixed $ignore An optional array or space separated list of properties to ignore while binding. * * @return boolean True on success. * * @since 3.2 * @throws \UnexpectedValueException */ public function bind($src, $ignore = array()); /** * Method to perform sanity checks on the TableInterface instance properties to ensure they are safe to store in the database. * * Implementations of this interface should use this method to make sure the data they are storing in the database is safe and * as expected before storage. * * @return boolean True if the instance is sane and able to be stored in the database. * * @since 3.2 */ public function check(); /** * Method to delete a record. * * @param mixed $pk An optional primary key value to delete. If not set the instance property value is used. * * @return boolean True on success. * * @since 3.2 * @throws \UnexpectedValueException */ public function delete($pk = null); /** * Method to get the \JDatabaseDriver object. * * @return \JDatabaseDriver The internal database driver object. * * @since 3.2 */ public function getDbo(); /** * Method to get the primary key field name for the table. * * @return string The name of the primary key for the table. * * @since 3.2 */ public function getKeyName(); /** * Method to load a row from the database by primary key and bind the fields to the TableInterface 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. * * @since 3.2 * @throws \RuntimeException * @throws \UnexpectedValueException */ public function load($keys = null, $reset = true); /** * Method to reset class properties to the defaults set in the class definition. * * It will ignore the primary key as well as any private class properties. * * @return void * * @since 3.2 */ public function reset(); /** * Method to store a row in the database from the TableInterface instance properties. * * If a primary key value is set the row with that primary key value will be updated with the instance property values. * If no primary key value is set a new row will be inserted into the database with the properties from the TableInterface instance. * * @param boolean $updateNulls True to update fields even if they are null. * * @return boolean True on success. * * @since 3.2 */ public function store($updateNulls = false); } PK �V�[�4 4 Ucm.phpnu �[��� <?php /** * Joomla! Content Management System * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Table; defined('JPATH_PLATFORM') or die; /** * UCM map table * * @since 3.1 */ class Ucm extends Table { /** * Constructor * * @param \JDatabaseDriver $db A database connector object * * @since 3.1 */ public function __construct($db) { parent::__construct('#__ucm_base', 'ucm_id', $db); } } PK �V�[3 �X� � Update.phpnu �[��� <?php /** * Joomla! Content Management System * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Table; defined('JPATH_PLATFORM') or die; use Joomla\Registry\Registry; /** * Update table * Stores updates temporarily * * @since 1.7.0 */ class Update extends Table { /** * Constructor * * @param \JDatabaseDriver $db Database driver object. * * @since 1.7.0 */ public function __construct($db) { parent::__construct('#__updates', 'update_id', $db); } /** * Overloaded check function * * @return boolean True if the object is ok * * @see Table::check() * @since 1.7.0 */ public function check() { // Check for valid name if (trim($this->name) == '' || trim($this->element) == '') { $this->setError(\JText::_('JLIB_DATABASE_ERROR_MUSTCONTAIN_A_TITLE_EXTENSION')); return false; } if (!$this->update_id && !$this->data) { $this->data = ''; } return true; } /** * Overloaded bind function * * @param array $array Named array * @param mixed $ignore An optional array or space separated list of properties * to ignore while binding. * * @return mixed Null if operation was satisfactory, otherwise returns an error * * @see Table::bind() * @since 1.7.0 */ public function bind($array, $ignore = '') { if (isset($array['params']) && is_array($array['params'])) { $registry = new Registry($array['params']); $array['params'] = (string) $registry; } if (isset($array['control']) && is_array($array['control'])) { $registry = new Registry($array['control']); $array['control'] = (string) $registry; } return parent::bind($array, $ignore); } /** * Method to create and execute a SELECT WHERE query. * * @param array $options Array of options * * @return string Results of query * * @since 1.7.0 */ public function find($options = array()) { $where = array(); foreach ($options as $col => $val) { $where[] = $col . ' = ' . $this->_db->quote($val); } $query = $this->_db->getQuery(true) ->select($this->_db->quoteName($this->_tbl_key)) ->from($this->_db->quoteName($this->_tbl)) ->where(implode(' AND ', $where)); $this->_db->setQuery($query); return $this->_db->loadResult(); } } PK �V�[�Y�\� � UpdateSite.phpnu �[��� <?php /** * Joomla! Content Management System * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Table; defined('JPATH_PLATFORM') or die; /** * Update site table * Stores the update sites for extensions * * @since 3.4 */ class UpdateSite extends Table { /** * Constructor * * @param \JDatabaseDriver $db Database driver object. * * @since 3.4 */ public function __construct($db) { parent::__construct('#__update_sites', 'update_site_id', $db); } /** * Overloaded check function * * @return boolean True if the object is ok * * @see Table::check() * @since 3.4 */ public function check() { // Check for valid name if (trim($this->name) == '' || trim($this->location) == '') { $this->setError(\JText::_('JLIB_DATABASE_ERROR_MUSTCONTAIN_A_TITLE_EXTENSION')); return false; } return true; } } PK �V�[I|�3 �3 User.phpnu �[��� <?php /** * Joomla! Content Management System * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Table; defined('JPATH_PLATFORM') or die; use Joomla\Registry\Registry; use Joomla\String\StringHelper; use Joomla\Utilities\ArrayHelper; /** * Users table * * @since 1.7.0 */ class User extends Table { /** * Associative array of group ids => group ids for the user * * @var array * @since 1.7.0 */ public $groups; /** * Constructor * * @param \JDatabaseDriver $db Database driver object. * * @since 1.7.0 */ public function __construct($db) { parent::__construct('#__users', 'id', $db); // Initialise. $this->id = 0; $this->sendEmail = 0; } /** * Method to load a user, user groups, and any other necessary data * from the database so that it can be bound to the user object. * * @param integer $userId An optional user id. * @param boolean $reset False if row not found or on error * (internal error state set in that case). * * @return boolean True on success, false on failure. * * @since 1.7.0 */ public function load($userId = null, $reset = true) { // Get the id to load. if ($userId !== null) { $this->id = $userId; } else { $userId = $this->id; } // Check for a valid id to load. if ($userId === null) { return false; } // Reset the table. $this->reset(); // Load the user data. $query = $this->_db->getQuery(true) ->select('*') ->from($this->_db->quoteName('#__users')) ->where($this->_db->quoteName('id') . ' = ' . (int) $userId); $this->_db->setQuery($query); $data = (array) $this->_db->loadAssoc(); if (!count($data)) { return false; } // Convert email from punycode $data['email'] = \JStringPunycode::emailToUTF8($data['email']); // Bind the data to the table. $return = $this->bind($data); if ($return !== false) { // Load the user groups. $query->clear() ->select($this->_db->quoteName('g.id')) ->select($this->_db->quoteName('g.title')) ->from($this->_db->quoteName('#__usergroups') . ' AS g') ->join('INNER', $this->_db->quoteName('#__user_usergroup_map') . ' AS m ON m.group_id = g.id') ->where($this->_db->quoteName('m.user_id') . ' = ' . (int) $userId); $this->_db->setQuery($query); // Add the groups to the user data. $this->groups = $this->_db->loadAssocList('id', 'id'); } return $return; } /** * Method to bind the user, user groups, and any other necessary data. * * @param array $array The data to bind. * @param mixed $ignore An array or space separated list of fields to ignore. * * @return boolean True on success, false on failure. * * @since 1.7.0 */ public function bind($array, $ignore = '') { if (array_key_exists('params', $array) && is_array($array['params'])) { $registry = new Registry($array['params']); $array['params'] = (string) $registry; } // Attempt to bind the data. $return = parent::bind($array, $ignore); // Load the real group data based on the bound ids. if ($return && !empty($this->groups)) { // Set the group ids. $this->groups = ArrayHelper::toInteger($this->groups); // Get the titles for the user groups. $query = $this->_db->getQuery(true) ->select($this->_db->quoteName('id')) ->select($this->_db->quoteName('title')) ->from($this->_db->quoteName('#__usergroups')) ->where($this->_db->quoteName('id') . ' = ' . implode(' OR ' . $this->_db->quoteName('id') . ' = ', $this->groups)); $this->_db->setQuery($query); // Set the titles for the user groups. $this->groups = $this->_db->loadAssocList('id', 'id'); } return $return; } /** * Validation and filtering * * @return boolean True if satisfactory * * @since 1.7.0 */ public function check() { // Set user id to null istead of 0, if needed if ($this->id === 0) { $this->id = null; } $filterInput = \JFilterInput::getInstance(); // Validate user information if ($filterInput->clean($this->name, 'TRIM') == '') { $this->setError(\JText::_('JLIB_DATABASE_ERROR_PLEASE_ENTER_YOUR_NAME')); return false; } if ($filterInput->clean($this->username, 'TRIM') == '') { $this->setError(\JText::_('JLIB_DATABASE_ERROR_PLEASE_ENTER_A_USER_NAME')); return false; } if (preg_match('#[<>"\'%;()&\\\\]|\\.\\./#', $this->username) || StringHelper::strlen($this->username) < 2 || $filterInput->clean($this->username, 'TRIM') !== $this->username || StringHelper::strlen($this->username) > 150) { $this->setError(\JText::sprintf('JLIB_DATABASE_ERROR_VALID_AZ09', 2)); return false; } if (($filterInput->clean($this->email, 'TRIM') == '') || !\JMailHelper::isEmailAddress($this->email) || StringHelper::strlen($this->email) > 100) { $this->setError(\JText::_('JLIB_DATABASE_ERROR_VALID_MAIL')); return false; } // Convert email to punycode for storage $this->email = \JStringPunycode::emailToPunycode($this->email); // Set the registration timestamp if (empty($this->registerDate) || $this->registerDate == $this->_db->getNullDate()) { $this->registerDate = \JFactory::getDate()->toSql(); } // Set the lastvisitDate timestamp if (empty($this->lastvisitDate)) { $this->lastvisitDate = $this->_db->getNullDate(); } // Set the lastResetTime timestamp if (empty($this->lastResetTime)) { $this->lastResetTime = $this->_db->getNullDate(); } // Check for existing username $query = $this->_db->getQuery(true) ->select($this->_db->quoteName('id')) ->from($this->_db->quoteName('#__users')) ->where($this->_db->quoteName('username') . ' = ' . $this->_db->quote($this->username)) ->where($this->_db->quoteName('id') . ' != ' . (int) $this->id); $this->_db->setQuery($query); $xid = (int) $this->_db->loadResult(); if ($xid && $xid != (int) $this->id) { $this->setError(\JText::_('JLIB_DATABASE_ERROR_USERNAME_INUSE')); return false; } // Check for existing email $query->clear() ->select($this->_db->quoteName('id')) ->from($this->_db->quoteName('#__users')) ->where('LOWER(' . $this->_db->quoteName('email') . ') = LOWER(' . $this->_db->quote($this->email) . ')') ->where($this->_db->quoteName('id') . ' != ' . (int) $this->id); $this->_db->setQuery($query); $xid = (int) $this->_db->loadResult(); if ($xid && $xid != (int) $this->id) { $this->setError(\JText::_('JLIB_DATABASE_ERROR_EMAIL_INUSE')); return false; } // Check for root_user != username $config = \JFactory::getConfig(); $rootUser = $config->get('root_user'); if (!is_numeric($rootUser)) { $query->clear() ->select($this->_db->quoteName('id')) ->from($this->_db->quoteName('#__users')) ->where($this->_db->quoteName('username') . ' = ' . $this->_db->quote($rootUser)); $this->_db->setQuery($query); $xid = (int) $this->_db->loadResult(); if ($rootUser == $this->username && (!$xid || $xid && $xid != (int) $this->id) || $xid && $xid == (int) $this->id && $rootUser != $this->username) { $this->setError(\JText::_('JLIB_DATABASE_ERROR_USERNAME_CANNOT_CHANGE')); return false; } } return true; } /** * Method to store a row in the database from the Table instance properties. * * If a primary key value is set the row with that primary key value will be updated with the instance property values. * If no primary key value is set a new row will be inserted into the database with the properties from the Table instance. * * @param boolean $updateNulls True to update fields even if they are null. * * @return boolean True on success. * * @since 1.7.0 */ public function store($updateNulls = false) { // Get the table key and key value. $k = $this->_tbl_key; $key = $this->$k; // TODO: This is a dumb way to handle the groups. // Store groups locally so as to not update directly. $groups = $this->groups; unset($this->groups); // Insert or update the object based on presence of a key value. if ($key) { // Already have a table key, update the row. $this->_db->updateObject($this->_tbl, $this, $this->_tbl_key, $updateNulls); } else { // Don't have a table key, insert the row. $this->_db->insertObject($this->_tbl, $this, $this->_tbl_key); } // Reset groups to the local object. $this->groups = $groups; $query = $this->_db->getQuery(true); // Store the group data if the user data was saved. if (is_array($this->groups) && count($this->groups)) { // Grab all usergroup entries for the user $query -> clear() -> select($this->_db->quoteName('group_id')) -> from($this->_db->quoteName('#__user_usergroup_map')) -> where($this->_db->quoteName('user_id') . ' = ' . (int) $this->id); $this->_db->setQuery($query); $result = $this->_db->loadObjectList(); // Loop through them and check if database contains something $this->groups does not if (count($result)) { foreach ($result as $map) { if (array_key_exists($map->group_id, $this->groups)) { // It already exists, no action required unset($groups[$map->group_id]); } else { // It should be removed $query -> clear() -> delete($this->_db->quoteName('#__user_usergroup_map')) -> where($this->_db->quoteName('user_id') . ' = ' . (int) $this->id) -> where($this->_db->quoteName('group_id') . ' = ' . (int) $map->group_id); $this->_db->setQuery($query); $this->_db->execute(); } } } // If there is anything left in this->groups it needs to be inserted if (count($groups)) { // Set the new user group maps. $query->clear() ->insert($this->_db->quoteName('#__user_usergroup_map')) ->columns(array($this->_db->quoteName('user_id'), $this->_db->quoteName('group_id'))); // Have to break this up into individual queries for cross-database support. foreach ($groups as $group) { $query->clear('values') ->values($this->id . ', ' . $group); $this->_db->setQuery($query); $this->_db->execute(); } } unset($groups); } // If a user is blocked, delete the cookie login rows if ($this->block == (int) 1) { $query->clear() ->delete($this->_db->quoteName('#__user_keys')) ->where($this->_db->quoteName('user_id') . ' = ' . $this->_db->quote($this->username)); $this->_db->setQuery($query); $this->_db->execute(); } return true; } /** * Method to delete a user, user groups, and any other necessary data from the database. * * @param integer $userId An optional user id. * * @return boolean True on success, false on failure. * * @since 1.7.0 */ public function delete($userId = null) { // Set the primary key to delete. $k = $this->_tbl_key; if ($userId) { $this->$k = (int) $userId; } // Delete the user. $query = $this->_db->getQuery(true) ->delete($this->_db->quoteName($this->_tbl)) ->where($this->_db->quoteName($this->_tbl_key) . ' = ' . (int) $this->$k); $this->_db->setQuery($query); $this->_db->execute(); // Delete the user group maps. $query->clear() ->delete($this->_db->quoteName('#__user_usergroup_map')) ->where($this->_db->quoteName('user_id') . ' = ' . (int) $this->$k); $this->_db->setQuery($query); $this->_db->execute(); /* * Clean Up Related Data. */ $query->clear() ->delete($this->_db->quoteName('#__messages_cfg')) ->where($this->_db->quoteName('user_id') . ' = ' . (int) $this->$k); $this->_db->setQuery($query); $this->_db->execute(); $query->clear() ->delete($this->_db->quoteName('#__messages')) ->where($this->_db->quoteName('user_id_to') . ' = ' . (int) $this->$k); $this->_db->setQuery($query); $this->_db->execute(); $query->clear() ->delete($this->_db->quoteName('#__user_keys')) ->where($this->_db->quoteName('user_id') . ' = ' . $this->_db->quote($this->username)); $this->_db->setQuery($query); $this->_db->execute(); return true; } /** * Updates last visit time of user * * @param integer $timeStamp The timestamp, defaults to 'now'. * @param integer $userId The user id (optional). * * @return boolean False if an error occurs * * @since 1.7.0 */ public function setLastVisit($timeStamp = null, $userId = null) { // Check for User ID if (is_null($userId)) { if (isset($this)) { $userId = $this->id; } else { jexit('No userid in setLastVisit'); } } // If no timestamp value is passed to function, than current time is used. $date = \JFactory::getDate($timeStamp); // Update the database row for the user. $db = $this->_db; $query = $db->getQuery(true) ->update($db->quoteName($this->_tbl)) ->set($db->quoteName('lastvisitDate') . '=' . $db->quote($date->toSql())) ->where($db->quoteName('id') . '=' . (int) $userId); $db->setQuery($query); $db->execute(); return true; } } PK �V�[zy= = Usergroup.phpnu �[��� <?php /** * Joomla! Content Management System * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Table; defined('JPATH_PLATFORM') or die; /** * Usergroup table class. * * @since 1.7.0 */ class Usergroup extends Table { /** * Constructor * * @param \JDatabaseDriver $db Database driver object. * * @since 1.7.0 */ public function __construct($db) { parent::__construct('#__usergroups', 'id', $db); } /** * Method to check the current record to save * * @return boolean True on success * * @since 1.7.0 */ public function check() { // Validate the title. if ((trim($this->title)) == '') { $this->setError(\JText::_('JLIB_DATABASE_ERROR_USERGROUP_TITLE')); return false; } // The parent_id can not be equal to the current id if ($this->id === (int) $this->parent_id) { $this->setError(\JText::_('JLIB_DATABASE_ERROR_USERGROUP_PARENT_ID_NOT_VALID')); return false; } // Check for a duplicate parent_id, title. // There is a unique index on the (parent_id, title) field in the table. $db = $this->_db; $query = $db->getQuery(true) ->select('COUNT(title)') ->from($this->_tbl) ->where('title = ' . $db->quote(trim($this->title))) ->where('parent_id = ' . (int) $this->parent_id) ->where('id <> ' . (int) $this->id); $db->setQuery($query); if ($db->loadResult() > 0) { $this->setError(\JText::_('JLIB_DATABASE_ERROR_USERGROUP_TITLE_EXISTS')); return false; } // We do not allow to move non public to root and public to non-root if (!empty($this->id)) { $table = self::getInstance('Usergroup', 'JTable', array('dbo' => $this->getDbo())); $table->load($this->id); if ((!$table->parent_id && $this->parent_id) || ($table->parent_id && !$this->parent_id)) { $this->setError(\JText::_('JLIB_DATABASE_ERROR_USERGROUP_PARENT_ID_NOT_VALID')); return false; } } // New entry should always be greater 0 elseif (!$this->parent_id) { $this->setError(\JText::_('JLIB_DATABASE_ERROR_USERGROUP_PARENT_ID_NOT_VALID')); return false; } // The new parent_id has to be a valid group if ($this->parent_id) { $table = self::getInstance('Usergroup', 'JTable', array('dbo' => $this->getDbo())); $table->load($this->parent_id); if ($table->id != $this->parent_id) { $this->setError(\JText::_('JLIB_DATABASE_ERROR_USERGROUP_PARENT_ID_NOT_VALID')); return false; } } return true; } /** * Method to recursively rebuild the nested set tree. * * @param integer $parentId The root of the tree to rebuild. * @param integer $left The left id to start with in building the tree. * * @return boolean True on success * * @since 1.7.0 */ public function rebuild($parentId = 0, $left = 0) { // Get the database object $db = $this->_db; // Get all children of this node $db->setQuery('SELECT id FROM ' . $this->_tbl . ' WHERE parent_id=' . (int) $parentId . ' ORDER BY parent_id, title'); $children = $db->loadColumn(); // The right value of this node is the left value + 1 $right = $left + 1; // Execute this function recursively over all children for ($i = 0, $n = count($children); $i < $n; $i++) { // $right is the current right value, which is incremented on recursion return $right = $this->rebuild($children[$i], $right); // If there is an update failure, return false to break out of the recursion if ($right === false) { return false; } } // We've got the left value, and now that we've processed // the children of this node we also know the right value $db->setQuery('UPDATE ' . $this->_tbl . ' SET lft=' . (int) $left . ', rgt=' . (int) $right . ' WHERE id=' . (int) $parentId); // If there is an update failure, return false to break out of the recursion try { $db->execute(); } catch (\JDatabaseExceptionExecuting $e) { return false; } // Return the right value of this node + 1 return $right + 1; } /** * Inserts a new row if id is zero or updates an existing row in the database table * * @param boolean $updateNulls If false, null object variables are not updated * * @return boolean True if successful, false otherwise and an internal error message is set * * @since 1.7.0 */ public function store($updateNulls = false) { if ($result = parent::store($updateNulls)) { // Rebuild the nested set tree. $this->rebuild(); } return $result; } /** * Delete this object and its dependencies * * @param integer $oid The primary key of the user group to delete. * * @return mixed Boolean or Exception. * * @since 1.7.0 * @throws \RuntimeException on database error. * @throws \UnexpectedValueException on data error. */ public function delete($oid = null) { if ($oid) { $this->load($oid); } if ($this->id == 0) { throw new \UnexpectedValueException('Usergroup not found'); } if ($this->parent_id == 0) { throw new \UnexpectedValueException('Root usergroup cannot be deleted.'); } if ($this->lft == 0 || $this->rgt == 0) { throw new \UnexpectedValueException('Left-Right data inconsistency. Cannot delete usergroup.'); } $db = $this->_db; // Select the usergroup ID and its children $query = $db->getQuery(true) ->select($db->quoteName('c.id')) ->from($db->quoteName($this->_tbl) . 'AS c') ->where($db->quoteName('c.lft') . ' >= ' . (int) $this->lft) ->where($db->quoteName('c.rgt') . ' <= ' . (int) $this->rgt); $db->setQuery($query); $ids = $db->loadColumn(); if (empty($ids)) { throw new \UnexpectedValueException('Left-Right data inconsistency. Cannot delete usergroup.'); } // Delete the usergroup and its children $query->clear() ->delete($db->quoteName($this->_tbl)) ->where($db->quoteName('id') . ' IN (' . implode(',', $ids) . ')'); $db->setQuery($query); $db->execute(); // Rebuild the nested set tree. $this->rebuild(); // Delete the usergroup in view levels $replace = array(); foreach ($ids as $id) { $replace[] = ',' . $db->quote("[$id,") . ',' . $db->quote('[') . ')'; $replace[] = ',' . $db->quote(",$id,") . ',' . $db->quote(',') . ')'; $replace[] = ',' . $db->quote(",$id]") . ',' . $db->quote(']') . ')'; $replace[] = ',' . $db->quote("[$id]") . ',' . $db->quote('[]') . ')'; } $query->clear() ->select('id, rules') ->from('#__viewlevels'); $db->setQuery($query); $rules = $db->loadObjectList(); $match_ids = array(); foreach ($rules as $rule) { foreach ($ids as $id) { if (strstr($rule->rules, '[' . $id) || strstr($rule->rules, ',' . $id) || strstr($rule->rules, $id . ']')) { $match_ids[] = $rule->id; } } } if (!empty($match_ids)) { $query->clear() ->set('rules=' . str_repeat('replace(', 4 * count($ids)) . 'rules' . implode('', $replace)) ->update('#__viewlevels') ->where('id IN (' . implode(',', $match_ids) . ')'); $db->setQuery($query); $db->execute(); } // Delete the user to usergroup mappings for the group(s) from the database. $query->clear() ->delete($db->quoteName('#__user_usergroup_map')) ->where($db->quoteName('group_id') . ' IN (' . implode(',', $ids) . ')'); $db->setQuery($query); $db->execute(); return true; } } PK �V�[M���Z Z ViewLevel.phpnu �[��� <?php /** * Joomla! Content Management System * * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Table; defined('JPATH_PLATFORM') or die; /** * Viewlevels table class. * * @since 1.7.0 */ class ViewLevel extends Table { /** * Constructor * * @param \JDatabaseDriver $db Database driver object. * * @since 1.7.0 */ public function __construct($db) { parent::__construct('#__viewlevels', 'id', $db); } /** * Method to bind the data. * * @param array $array The data to bind. * @param mixed $ignore An array or space separated list of fields to ignore. * * @return boolean True on success, false on failure. * * @since 1.7.0 */ public function bind($array, $ignore = '') { // Bind the rules as appropriate. if (isset($array['rules'])) { if (is_array($array['rules'])) { $array['rules'] = json_encode($array['rules']); } } return parent::bind($array, $ignore); } /** * Method to check the current record to save * * @return boolean True on success * * @since 1.7.0 */ public function check() { // Validate the title. if ((trim($this->title)) == '') { $this->setError(\JText::_('JLIB_DATABASE_ERROR_VIEWLEVEL')); return false; } // Check for a duplicate title. $db = $this->_db; $query = $db->getQuery(true) ->select('COUNT(title)') ->from($db->quoteName('#__viewlevels')) ->where($db->quoteName('title') . ' = ' . $db->quote($this->title)) ->where($db->quoteName('id') . ' != ' . (int) $this->id); $db->setQuery($query); if ($db->loadResult() > 0) { $this->setError(\JText::sprintf('JLIB_DATABASE_ERROR_USERLEVEL_NAME_EXISTS', $this->title)); return false; } return true; } } PK �V�[��~a a Asset.phpnu �[��� PK �V�[L�fK � Category.phpnu �[��� PK �V�[;�1�# # �) Content.phpnu �[��� PK �V�[��%�_ _ CM ContentHistory.phpnu �[��� PK �V�[�g� �g ContentType.phpnu �[��� PK �V�[*��YH* H* +v CoreContent.phpnu �[��� PK �V�["��_b b �� Extension.phpnu �[��� PK �V�[���s Q� Language.phpnu �[��� PK �V�[��B� � �� Menu.phpnu �[��� PK �V�[Qr��� � y� MenuType.phpnu �[��� PK �V�[�f�F F Y Module.phpnu �[��� PK �V�[:��X� X� � Nested.phpnu �[��� PK �V�[{��Y Y k� Observer/AbstractObserver.phpnu �[��� PK �V�[lK�%L L � Observer/ContentHistory.phpnu �[��� PK �V�[:��� �� Observer/Tags.phpnu �[��� PK �V�[���� � � Table.phpnu �[��� PK �V�[��W� � A� TableInterface.phpnu �[��� PK �V�[�4 4 0� Ucm.phpnu �[��� PK �V�[3 �X� � �� Update.phpnu �[��� PK �V�[�Y�\� � j� UpdateSite.phpnu �[��� PK �V�[I|�3 �3 �� User.phpnu �[��� PK �V�[zy= = c� Usergroup.phpnu �[��� PK �V�[M���Z Z � ViewLevel.phpnu �[��� PK � t"
| ver. 1.4 |
Github
|
.
| PHP 8.1.33 | Генерация страницы: 0.34 |
proxy
|
phpinfo
|
Настройка