[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/libraries/src/Table/ -> ContentHistory.php (source)

   1  <?php
   2  
   3  /**
   4   * Joomla! Content Management System
   5   *
   6   * @copyright  (C) 2013 Open Source Matters, Inc. <https://www.joomla.org>
   7   * @license    GNU General Public License version 2 or later; see LICENSE.txt
   8   */
   9  
  10  namespace Joomla\CMS\Table;
  11  
  12  use Joomla\CMS\Factory;
  13  use Joomla\Database\DatabaseDriver;
  14  use Joomla\Database\ParameterType;
  15  
  16  // phpcs:disable PSR1.Files.SideEffects
  17  \defined('JPATH_PLATFORM') or die;
  18  // phpcs:enable PSR1.Files.SideEffects
  19  
  20  /**
  21   * Content History table.
  22   *
  23   * @since  3.2
  24   */
  25  class ContentHistory extends Table
  26  {
  27      /**
  28       * Array of object fields to unset from the data object before calculating SHA1 hash. This allows us to detect a meaningful change
  29       * in the database row using the hash. This can be read from the #__content_types content_history_options column.
  30       *
  31       * @var    array
  32       * @since  3.2
  33       */
  34      public $ignoreChanges = array();
  35  
  36      /**
  37       * Array of object fields to convert to integers before calculating SHA1 hash. Some values are stored differently
  38       * when an item is created than when the item is changed and saved. This works around that issue.
  39       * This can be read from the #__content_types content_history_options column.
  40       *
  41       * @var    array
  42       * @since  3.2
  43       */
  44      public $convertToInt = array();
  45  
  46      /**
  47       * Constructor
  48       *
  49       * @param   DatabaseDriver  $db  A database connector object
  50       *
  51       * @since   3.1
  52       */
  53      public function __construct(DatabaseDriver $db)
  54      {
  55          parent::__construct('#__history', 'version_id', $db);
  56          $this->ignoreChanges = array(
  57              'modified_by',
  58              'modified_user_id',
  59              'modified',
  60              'modified_time',
  61              'checked_out',
  62              'checked_out_time',
  63              'version',
  64              'hits',
  65              'path',
  66          );
  67          $this->convertToInt  = array('publish_up', 'publish_down', 'ordering', 'featured');
  68      }
  69  
  70      /**
  71       * Overrides Table::store to set modified hash, user id, and save date.
  72       *
  73       * @param   boolean  $updateNulls  True to update fields even if they are null.
  74       *
  75       * @return  boolean  True on success.
  76       *
  77       * @since   3.2
  78       */
  79      public function store($updateNulls = false)
  80      {
  81          $this->set('character_count', \strlen($this->get('version_data')));
  82          $typeTable = Table::getInstance('ContentType', 'JTable', array('dbo' => $this->getDbo()));
  83          $typeAlias = explode('.', $this->item_id);
  84          array_pop($typeAlias);
  85          $typeTable->load(array('type_alias' => implode('.', $typeAlias)));
  86  
  87          if (!isset($this->sha1_hash)) {
  88              $this->set('sha1_hash', $this->getSha1($this->get('version_data'), $typeTable));
  89          }
  90  
  91          // Modify author and date only when not toggling Keep Forever
  92          if ($this->get('keep_forever') === null) {
  93              $this->set('editor_user_id', Factory::getUser()->id);
  94              $this->set('save_date', Factory::getDate()->toSql());
  95          }
  96  
  97          return parent::store($updateNulls);
  98      }
  99  
 100      /**
 101       * Utility method to get the hash after removing selected values. This lets us detect changes other than
 102       * modified date (which will change on every save).
 103       *
 104       * @param   mixed        $jsonData   Either an object or a string with json-encoded data
 105       * @param   ContentType  $typeTable  Table object with data for this content type
 106       *
 107       * @return  string  SHA1 hash on success. Empty string on failure.
 108       *
 109       * @since   3.2
 110       */
 111      public function getSha1($jsonData, ContentType $typeTable)
 112      {
 113          $object = \is_object($jsonData) ? $jsonData : json_decode($jsonData);
 114  
 115          if (isset($typeTable->content_history_options) && \is_object(json_decode($typeTable->content_history_options))) {
 116              $options = json_decode($typeTable->content_history_options);
 117              $this->ignoreChanges = $options->ignoreChanges ?? $this->ignoreChanges;
 118              $this->convertToInt = $options->convertToInt ?? $this->convertToInt;
 119          }
 120  
 121          foreach ($this->ignoreChanges as $remove) {
 122              if (property_exists($object, $remove)) {
 123                  unset($object->$remove);
 124              }
 125          }
 126  
 127          // Convert integers, booleans, and nulls to strings to get a consistent hash value
 128          foreach ($object as $name => $value) {
 129              if (\is_object($value)) {
 130                  // Go one level down for JSON column values
 131                  foreach ($value as $subName => $subValue) {
 132                      $object->$subName = \is_int($subValue) || \is_bool($subValue) || $subValue === null ? (string) $subValue : $subValue;
 133                  }
 134              } else {
 135                  $object->$name = \is_int($value) || \is_bool($value) || $value === null ? (string) $value : $value;
 136              }
 137          }
 138  
 139          // Work around empty values
 140          foreach ($this->convertToInt as $convert) {
 141              if (isset($object->$convert)) {
 142                  $object->$convert = (int) $object->$convert;
 143              }
 144          }
 145  
 146          if (isset($object->review_time)) {
 147              $object->review_time = (int) $object->review_time;
 148          }
 149  
 150          return sha1(json_encode($object));
 151      }
 152  
 153      /**
 154       * Utility method to get a matching row based on the hash value and id columns.
 155       * This lets us check to make sure we don't save duplicate versions.
 156       *
 157       * @return  string  SHA1 hash on success. Empty string on failure.
 158       *
 159       * @since   3.2
 160       */
 161      public function getHashMatch()
 162      {
 163          $db       = $this->_db;
 164          $itemId   = $this->get('item_id');
 165          $sha1Hash = $this->get('sha1_hash');
 166          $query    = $db->getQuery(true);
 167          $query->select('*')
 168              ->from($db->quoteName('#__history'))
 169              ->where($db->quoteName('item_id') . ' = :item_id')
 170              ->where($db->quoteName('sha1_hash') . ' = :sha1_hash')
 171              ->bind(':item_id', $itemId, ParameterType::STRING)
 172              ->bind(':sha1_hash', $sha1Hash);
 173  
 174          $query->setLimit(1);
 175          $db->setQuery($query);
 176  
 177          return $db->loadObject();
 178      }
 179  
 180      /**
 181       * Utility method to remove the oldest versions of an item, saving only the most recent versions.
 182       *
 183       * @param   integer  $maxVersions  The maximum number of versions to save. All others will be deleted.
 184       *
 185       * @return  boolean   true on success, false on failure.
 186       *
 187       * @since   3.2
 188       */
 189      public function deleteOldVersions($maxVersions)
 190      {
 191          $result = true;
 192  
 193          // Get the list of version_id values we want to save
 194          $db        = $this->_db;
 195          $itemId = $this->get('item_id');
 196          $query     = $db->getQuery(true);
 197          $query->select($db->quoteName('version_id'))
 198              ->from($db->quoteName('#__history'))
 199              ->where($db->quoteName('item_id') . ' = :item_id')
 200              ->where($db->quoteName('keep_forever') . ' != 1')
 201              ->bind(':item_id', $itemId, ParameterType::STRING)
 202              ->order($db->quoteName('save_date') . ' DESC ');
 203  
 204          $query->setLimit((int) $maxVersions);
 205          $db->setQuery($query);
 206          $idsToSave = $db->loadColumn(0);
 207  
 208          // Don't process delete query unless we have at least the maximum allowed versions
 209          if (\count($idsToSave) === (int) $maxVersions) {
 210              // Delete any rows not in our list and and not flagged to keep forever.
 211              $query = $db->getQuery(true);
 212              $query->delete($db->quoteName('#__history'))
 213                  ->where($db->quoteName('item_id') . ' = :item_id')
 214                  ->whereNotIn($db->quoteName('version_id'), $idsToSave)
 215                  ->where($db->quoteName('keep_forever') . ' != 1')
 216                  ->bind(':item_id', $itemId, ParameterType::STRING);
 217              $db->setQuery($query);
 218              $result = (bool) $db->execute();
 219          }
 220  
 221          return $result;
 222      }
 223  }


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer