primo commit
This commit is contained in:
		| @ -0,0 +1,26 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * @package     Joomla.Administrator | ||||
|  * @subpackage  com_contenthistory | ||||
|  * | ||||
|  * @copyright   (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> | ||||
|  * @license     GNU General Public License version 2 or later; see LICENSE.txt | ||||
|  */ | ||||
|  | ||||
| namespace Joomla\Component\Contenthistory\Administrator\Controller; | ||||
|  | ||||
| use Joomla\CMS\MVC\Controller\BaseController; | ||||
|  | ||||
| // phpcs:disable PSR1.Files.SideEffects | ||||
| \defined('_JEXEC') or die; | ||||
| // phpcs:enable PSR1.Files.SideEffects | ||||
|  | ||||
| /** | ||||
|  * Contenthistory Controller | ||||
|  * | ||||
|  * @since  3.2 | ||||
|  */ | ||||
| class DisplayController extends BaseController | ||||
| { | ||||
| } | ||||
| @ -0,0 +1,96 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * @package     Joomla.Administrator | ||||
|  * @subpackage  com_contenthistory | ||||
|  * | ||||
|  * @copyright   (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> | ||||
|  * @license     GNU General Public License version 2 or later; see LICENSE.txt | ||||
|  */ | ||||
|  | ||||
| namespace Joomla\Component\Contenthistory\Administrator\Controller; | ||||
|  | ||||
| use Joomla\CMS\Language\Text; | ||||
| use Joomla\CMS\MVC\Controller\AdminController; | ||||
| use Joomla\CMS\Router\Route; | ||||
| use Joomla\CMS\Session\Session; | ||||
|  | ||||
| // phpcs:disable PSR1.Files.SideEffects | ||||
| \defined('_JEXEC') or die; | ||||
| // phpcs:enable PSR1.Files.SideEffects | ||||
|  | ||||
| /** | ||||
|  * Contenthistory list controller class. | ||||
|  * | ||||
|  * @since  3.2 | ||||
|  */ | ||||
| class HistoryController extends AdminController | ||||
| { | ||||
|     /** | ||||
|      * Proxy for getModel. | ||||
|      * | ||||
|      * @param   string  $name    The name of the model | ||||
|      * @param   string  $prefix  The prefix for the model | ||||
|      * @param   array   $config  An additional array of parameters | ||||
|      * | ||||
|      * @return  \Joomla\CMS\MVC\Model\BaseDatabaseModel  The model | ||||
|      * | ||||
|      * @since   3.2 | ||||
|      */ | ||||
|     public function getModel($name = 'History', $prefix = 'Administrator', $config = ['ignore_request' => true]) | ||||
|     { | ||||
|         return parent::getModel($name, $prefix, $config); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Toggles the keep forever value for one or more history rows. If it was Yes, changes to No. If No, changes to Yes. | ||||
|      * | ||||
|      * @return  void | ||||
|      * | ||||
|      * @since   3.2 | ||||
|      */ | ||||
|     public function keep() | ||||
|     { | ||||
|         $this->checkToken(); | ||||
|  | ||||
|         // Get items to toggle keep forever from the request. | ||||
|         $cid = (array) $this->input->get('cid', [], 'int'); | ||||
|  | ||||
|         // Remove zero values resulting from input filter | ||||
|         $cid = array_filter($cid); | ||||
|  | ||||
|         if (empty($cid)) { | ||||
|             $this->app->enqueueMessage(Text::_('COM_CONTENTHISTORY_NO_ITEM_SELECTED'), 'warning'); | ||||
|         } else { | ||||
|             // Get the model. | ||||
|             $model = $this->getModel(); | ||||
|  | ||||
|             // Toggle keep forever status of the selected items. | ||||
|             if ($model->keep($cid)) { | ||||
|                 $this->setMessage(Text::plural('COM_CONTENTHISTORY_N_ITEMS_KEEP_TOGGLE', \count($cid))); | ||||
|             } else { | ||||
|                 $this->setMessage($model->getError(), 'error'); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $this->setRedirect( | ||||
|             Route::_( | ||||
|                 'index.php?option=com_contenthistory&view=history&layout=modal&tmpl=component&item_id=' | ||||
|                 . $this->input->getCmd('item_id') . '&' . Session::getFormToken() . '=1', | ||||
|                 false | ||||
|             ) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Gets the URL arguments to append to a list redirect. | ||||
|      * | ||||
|      * @return  string  The arguments to append to the redirect URL. | ||||
|      * | ||||
|      * @since   4.0.0 | ||||
|      */ | ||||
|     protected function getRedirectToListAppend() | ||||
|     { | ||||
|         return '&layout=modal&tmpl=component&item_id=' . $this->input->get('item_id') . '&' . Session::getFormToken() . '=1'; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,41 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * @package     Joomla.Administrator | ||||
|  * @subpackage  com_contenthistory | ||||
|  * | ||||
|  * @copyright   (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> | ||||
|  * @license     GNU General Public License version 2 or later; see LICENSE.txt | ||||
|  */ | ||||
|  | ||||
| namespace Joomla\Component\Contenthistory\Administrator\Controller; | ||||
|  | ||||
| use Joomla\CMS\MVC\Controller\BaseController; | ||||
|  | ||||
| // phpcs:disable PSR1.Files.SideEffects | ||||
| \defined('_JEXEC') or die; | ||||
| // phpcs:enable PSR1.Files.SideEffects | ||||
|  | ||||
| /** | ||||
|  * Contenthistory list controller class. | ||||
|  * | ||||
|  * @since  3.2 | ||||
|  */ | ||||
| class PreviewController extends BaseController | ||||
| { | ||||
|     /** | ||||
|      * Proxy for getModel. | ||||
|      * | ||||
|      * @param   string  $name    The name of the model | ||||
|      * @param   string  $prefix  The prefix for the model | ||||
|      * @param   array   $config  An additional array of parameters | ||||
|      * | ||||
|      * @return  \Joomla\CMS\MVC\Model\BaseDatabaseModel  The model | ||||
|      * | ||||
|      * @since   3.2 | ||||
|      */ | ||||
|     public function getModel($name = 'Preview', $prefix = 'Administrator', $config = ['ignore_request' => true]) | ||||
|     { | ||||
|         return parent::getModel($name, $prefix, $config); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,43 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * @package     Joomla.Administrator | ||||
|  * @subpackage  com_contenthistory | ||||
|  * | ||||
|  * @copyright   (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> | ||||
|  * @license     GNU General Public License version 2 or later; see LICENSE.txt | ||||
|  */ | ||||
|  | ||||
| namespace Joomla\Component\Contenthistory\Administrator\Dispatcher; | ||||
|  | ||||
| use Joomla\CMS\Access\Exception\NotAllowed; | ||||
| use Joomla\CMS\Dispatcher\ComponentDispatcher; | ||||
|  | ||||
| // phpcs:disable PSR1.Files.SideEffects | ||||
| \defined('_JEXEC') or die; | ||||
| // phpcs:enable PSR1.Files.SideEffects | ||||
|  | ||||
| /** | ||||
|  * ComponentDispatcher class for com_associations | ||||
|  * | ||||
|  * @since  4.0.0 | ||||
|  */ | ||||
| class Dispatcher extends ComponentDispatcher | ||||
| { | ||||
|     /** | ||||
|      * Method to check component access permission | ||||
|      * | ||||
|      * @since   4.0.0 | ||||
|      * | ||||
|      * @return  void | ||||
|      * | ||||
|      * @throws  \Exception|NotAllowed | ||||
|      */ | ||||
|     protected function checkAccess() | ||||
|     { | ||||
|         // Check the user has permission to access this component if in the backend | ||||
|         if ($this->app->getIdentity()->guest) { | ||||
|             throw new NotAllowed($this->app->getLanguage()->_('JERROR_ALERTNOAUTHOR'), 403); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,363 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * @package     Joomla.Administrator | ||||
|  * @subpackage  com_contenthistory | ||||
|  * | ||||
|  * @copyright   (C) 2017 Open Source Matters, Inc. <https://www.joomla.org> | ||||
|  * @license     GNU General Public License version 2 or later; see LICENSE.txt | ||||
|  */ | ||||
|  | ||||
| namespace Joomla\Component\Contenthistory\Administrator\Helper; | ||||
|  | ||||
| use Joomla\CMS\Factory; | ||||
| use Joomla\CMS\Filesystem\File; | ||||
| use Joomla\CMS\Language\Text; | ||||
| use Joomla\CMS\Table\ContentHistory; | ||||
| use Joomla\CMS\Table\ContentType; | ||||
| use Joomla\CMS\Table\Table; | ||||
| use Joomla\Database\ParameterType; | ||||
| use Joomla\Filesystem\Folder; | ||||
| use Joomla\Filesystem\Path; | ||||
|  | ||||
| // phpcs:disable PSR1.Files.SideEffects | ||||
| \defined('_JEXEC') or die; | ||||
| // phpcs:enable PSR1.Files.SideEffects | ||||
|  | ||||
| /** | ||||
|  * Categories helper. | ||||
|  * | ||||
|  * @since  3.2 | ||||
|  */ | ||||
| class ContenthistoryHelper | ||||
| { | ||||
|     /** | ||||
|      * Method to put all field names, including nested ones, in a single array for easy lookup. | ||||
|      * | ||||
|      * @param   \stdClass  $object  Standard class object that may contain one level of nested objects. | ||||
|      * | ||||
|      * @return  array  Associative array of all field names, including ones in a nested object. | ||||
|      * | ||||
|      * @since   3.2 | ||||
|      */ | ||||
|     public static function createObjectArray($object) | ||||
|     { | ||||
|         $result = []; | ||||
|  | ||||
|         if ($object === null) { | ||||
|             return $result; | ||||
|         } | ||||
|  | ||||
|         foreach ($object as $name => $value) { | ||||
|             $result[$name] = $value; | ||||
|  | ||||
|             if (\is_object($value)) { | ||||
|                 foreach ($value as $subName => $subValue) { | ||||
|                     $result[$subName] = $subValue; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return $result; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Method to decode JSON-encoded fields in a standard object. Used to unpack JSON strings in the content history data column. | ||||
|      * | ||||
|      * @param   string  $jsonString  JSON String to convert to an object. | ||||
|      * | ||||
|      * @return  \stdClass  Object with any JSON-encoded fields unpacked. | ||||
|      * | ||||
|      * @since   3.2 | ||||
|      */ | ||||
|     public static function decodeFields($jsonString) | ||||
|     { | ||||
|         $object = json_decode($jsonString); | ||||
|  | ||||
|         if (\is_object($object)) { | ||||
|             foreach ($object as $name => $value) { | ||||
|                 if (!\is_null($value) && $subObject = json_decode($value)) { | ||||
|                     $object->$name = $subObject; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return $object; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Method to get field labels for the fields in the JSON-encoded object. | ||||
|      * First we see if we can find translatable labels for the fields in the object. | ||||
|      * We translate any we can find and return an array in the format object->name => label. | ||||
|      * | ||||
|      * @param   \stdClass    $object      Standard class object in the format name->value. | ||||
|      * @param   ContentType  $typesTable  Table object with content history options. | ||||
|      * | ||||
|      * @return  \stdClass  Contains two associative arrays. | ||||
|      *                    $formValues->labels in the format name => label (for example, 'id' => 'Article ID'). | ||||
|      *                    $formValues->values in the format name => value (for example, 'state' => 'Published'. | ||||
|      *                    This translates the text from the selected option in the form. | ||||
|      * | ||||
|      * @since   3.2 | ||||
|      */ | ||||
|     public static function getFormValues($object, ContentType $typesTable) | ||||
|     { | ||||
|         $labels              = []; | ||||
|         $values              = []; | ||||
|         $expandedObjectArray = static::createObjectArray($object); | ||||
|         static::loadLanguageFiles($typesTable->type_alias); | ||||
|  | ||||
|         if ($formFile = static::getFormFile($typesTable)) { | ||||
|             if ($xml = simplexml_load_file($formFile)) { | ||||
|                 // Now we need to get all of the labels from the form | ||||
|                 $fieldArray = $xml->xpath('//field'); | ||||
|                 $fieldArray = array_merge($fieldArray, $xml->xpath('//fields')); | ||||
|  | ||||
|                 foreach ($fieldArray as $field) { | ||||
|                     if ($label = (string) $field->attributes()->label) { | ||||
|                         $labels[(string) $field->attributes()->name] = Text::_($label); | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 // Get values for any list type fields | ||||
|                 $listFieldArray = $xml->xpath('//field[@type="list" or @type="radio"]'); | ||||
|  | ||||
|                 foreach ($listFieldArray as $field) { | ||||
|                     $name = (string) $field->attributes()->name; | ||||
|  | ||||
|                     if (isset($expandedObjectArray[$name])) { | ||||
|                         $optionFieldArray = $field->xpath('option[@value="' . $expandedObjectArray[$name] . '"]'); | ||||
|  | ||||
|                         $valueText = null; | ||||
|  | ||||
|                         if (\is_array($optionFieldArray) && \count($optionFieldArray)) { | ||||
|                             $valueText = trim((string) $optionFieldArray[0]); | ||||
|                         } | ||||
|  | ||||
|                         $values[(string) $field->attributes()->name] = Text::_($valueText); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $result         = new \stdClass(); | ||||
|         $result->labels = $labels; | ||||
|         $result->values = $values; | ||||
|  | ||||
|         return $result; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Method to get the XML form file for this component. Used to get translated field names for history preview. | ||||
|      * | ||||
|      * @param   ContentType  $typesTable  Table object with content history options. | ||||
|      * | ||||
|      * @return  mixed  \JModel object if successful, false if no model found. | ||||
|      * | ||||
|      * @since   3.2 | ||||
|      */ | ||||
|     public static function getFormFile(ContentType $typesTable) | ||||
|     { | ||||
|         // First, see if we have a file name in the $typesTable | ||||
|         $options = json_decode($typesTable->content_history_options); | ||||
|  | ||||
|         if (\is_object($options) && isset($options->formFile) && is_file(JPATH_ROOT . '/' . $options->formFile)) { | ||||
|             $result = JPATH_ROOT . '/' . $options->formFile; | ||||
|         } else { | ||||
|             $aliasArray = explode('.', $typesTable->type_alias); | ||||
|             $component  = ($aliasArray[1] == 'category') ? 'com_categories' : $aliasArray[0]; | ||||
|             $path       = Folder::makeSafe(JPATH_ADMINISTRATOR . '/components/' . $component . '/models/forms/'); | ||||
|             array_shift($aliasArray); | ||||
|             $file   = File::makeSafe(implode('.', $aliasArray) . '.xml'); | ||||
|             $result = is_file($path . $file) ? $path . $file : false; | ||||
|         } | ||||
|  | ||||
|         return $result; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Method to query the database using values from lookup objects. | ||||
|      * | ||||
|      * @param   \stdClass  $lookup  The std object with the values needed to do the query. | ||||
|      * @param   mixed      $value   The value used to find the matching title or name. Typically the id. | ||||
|      * | ||||
|      * @return  mixed  Value from database (for example, name or title) on success, false on failure. | ||||
|      * | ||||
|      * @since   3.2 | ||||
|      */ | ||||
|     public static function getLookupValue($lookup, $value) | ||||
|     { | ||||
|         $result = false; | ||||
|  | ||||
|         if (isset($lookup->sourceColumn) && isset($lookup->targetTable) && isset($lookup->targetColumn) && isset($lookup->displayColumn)) { | ||||
|             $db    = Factory::getDbo(); | ||||
|             $value = (int) $value; | ||||
|             $query = $db->getQuery(true); | ||||
|             $query->select($db->quoteName($lookup->displayColumn)) | ||||
|                 ->from($db->quoteName($lookup->targetTable)) | ||||
|                 ->where($db->quoteName($lookup->targetColumn) . ' = :value') | ||||
|                 ->bind(':value', $value, ParameterType::INTEGER); | ||||
|             $db->setQuery($query); | ||||
|  | ||||
|             try { | ||||
|                 $result = $db->loadResult(); | ||||
|             } catch (\Exception $e) { | ||||
|                 // Ignore any errors and just return false | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return $result; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Method to remove fields from the object based on values entered in the #__content_types table. | ||||
|      * | ||||
|      * @param   \stdClass    $object     Object to be passed to view layout file. | ||||
|      * @param   ContentType  $typeTable  Table object with content history options. | ||||
|      * | ||||
|      * @return  \stdClass  object with hidden fields removed. | ||||
|      * | ||||
|      * @since   3.2 | ||||
|      */ | ||||
|     public static function hideFields($object, ContentType $typeTable) | ||||
|     { | ||||
|         if ($options = json_decode($typeTable->content_history_options)) { | ||||
|             if (isset($options->hideFields) && \is_array($options->hideFields)) { | ||||
|                 foreach ($options->hideFields as $field) { | ||||
|                     unset($object->$field); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return $object; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Method to load the language files for the component whose history is being viewed. | ||||
|      * | ||||
|      * @param   string  $typeAlias  The type alias, for example 'com_content.article'. | ||||
|      * | ||||
|      * @return  void | ||||
|      * | ||||
|      * @since   3.2 | ||||
|      */ | ||||
|     public static function loadLanguageFiles($typeAlias) | ||||
|     { | ||||
|         $aliasArray = explode('.', $typeAlias); | ||||
|  | ||||
|         if (\is_array($aliasArray) && \count($aliasArray) == 2) { | ||||
|             $component = ($aliasArray[1] == 'category') ? 'com_categories' : $aliasArray[0]; | ||||
|             $lang      = Factory::getLanguage(); | ||||
|  | ||||
|             /** | ||||
|              * Loading language file from the administrator/language directory then | ||||
|              * loading language file from the administrator/components/extension/language directory | ||||
|              */ | ||||
|             $lang->load($component, JPATH_ADMINISTRATOR) | ||||
|             || $lang->load($component, Path::clean(JPATH_ADMINISTRATOR . '/components/' . $component)); | ||||
|  | ||||
|             // Force loading of backend global language file | ||||
|             $lang->load('joomla', Path::clean(JPATH_ADMINISTRATOR)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Method to create object to pass to the layout. Format is as follows: | ||||
|      * field is std object with name, value. | ||||
|      * | ||||
|      * Value can be a std object with name, value pairs. | ||||
|      * | ||||
|      * @param   \stdClass  $object      The std object from the JSON string. Can be nested 1 level deep. | ||||
|      * @param   \stdClass  $formValues  Standard class of label and value in an associative array. | ||||
|      * | ||||
|      * @return  \stdClass  Object with translated labels where available | ||||
|      * | ||||
|      * @since   3.2 | ||||
|      */ | ||||
|     public static function mergeLabels($object, $formValues) | ||||
|     { | ||||
|         $result = new \stdClass(); | ||||
|  | ||||
|         if ($object === null) { | ||||
|             return $result; | ||||
|         } | ||||
|  | ||||
|         $labelsArray = $formValues->labels; | ||||
|         $valuesArray = $formValues->values; | ||||
|  | ||||
|         foreach ($object as $name => $value) { | ||||
|             $result->$name        = new \stdClass(); | ||||
|             $result->$name->name  = $name; | ||||
|             $result->$name->value = $valuesArray[$name] ?? $value; | ||||
|             $result->$name->label = $labelsArray[$name] ?? $name; | ||||
|  | ||||
|             if (\is_object($value)) { | ||||
|                 $subObject = new \stdClass(); | ||||
|  | ||||
|                 foreach ($value as $subName => $subValue) { | ||||
|                     $subObject->$subName        = new \stdClass(); | ||||
|                     $subObject->$subName->name  = $subName; | ||||
|                     $subObject->$subName->value = $valuesArray[$subName] ?? $subValue; | ||||
|                     $subObject->$subName->label = $labelsArray[$subName] ?? $subName; | ||||
|                     $result->$name->value       = $subObject; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return $result; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Method to prepare the object for the preview and compare views. | ||||
|      * | ||||
|      * @param   ContentHistory  $table  Table object loaded with data. | ||||
|      * | ||||
|      * @return  \stdClass  Object ready for the views. | ||||
|      * | ||||
|      * @since   3.2 | ||||
|      */ | ||||
|     public static function prepareData(ContentHistory $table) | ||||
|     { | ||||
|         $object     = static::decodeFields($table->version_data); | ||||
|         $typesTable = Table::getInstance('ContentType', 'Joomla\\CMS\\Table\\'); | ||||
|         $typeAlias  = explode('.', $table->item_id); | ||||
|         array_pop($typeAlias); | ||||
|         $typesTable->load(['type_alias' => implode('.', $typeAlias)]); | ||||
|         $formValues = static::getFormValues($object, $typesTable); | ||||
|         $object     = static::mergeLabels($object, $formValues); | ||||
|         $object     = static::hideFields($object, $typesTable); | ||||
|         $object     = static::processLookupFields($object, $typesTable); | ||||
|  | ||||
|         return $object; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Method to process any lookup values found in the content_history_options column for this table. | ||||
|      * This allows category title and user name to be displayed instead of the id column. | ||||
|      * | ||||
|      * @param   \stdClass    $object      The std object from the JSON string. Can be nested 1 level deep. | ||||
|      * @param   ContentType  $typesTable  Table object loaded with data. | ||||
|      * | ||||
|      * @return  \stdClass  Object with lookup values inserted. | ||||
|      * | ||||
|      * @since   3.2 | ||||
|      */ | ||||
|     public static function processLookupFields($object, ContentType $typesTable) | ||||
|     { | ||||
|         if ($options = json_decode($typesTable->content_history_options)) { | ||||
|             if (isset($options->displayLookup) && \is_array($options->displayLookup)) { | ||||
|                 foreach ($options->displayLookup as $lookup) { | ||||
|                     $sourceColumn = $lookup->sourceColumn ?? false; | ||||
|                     $sourceValue  = $object->$sourceColumn->value ?? false; | ||||
|  | ||||
|                     if ($sourceColumn && $sourceValue && ($lookupValue = static::getLookupValue($lookup, $sourceValue))) { | ||||
|                         $object->$sourceColumn->value = $lookupValue; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return $object; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,185 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * @package     Joomla.Administrator | ||||
|  * @subpackage  com_contenthistory | ||||
|  * | ||||
|  * @copyright   (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> | ||||
|  * @license     GNU General Public License version 2 or later; see LICENSE.txt | ||||
|  */ | ||||
|  | ||||
| namespace Joomla\Component\Contenthistory\Administrator\Model; | ||||
|  | ||||
| use Joomla\CMS\Access\Exception\NotAllowed; | ||||
| use Joomla\CMS\Factory; | ||||
| use Joomla\CMS\HTML\HTMLHelper; | ||||
| use Joomla\CMS\Language\Text; | ||||
| use Joomla\CMS\MVC\Model\ListModel; | ||||
| use Joomla\CMS\Table\ContentHistory; | ||||
| use Joomla\CMS\Table\ContentType; | ||||
| use Joomla\CMS\Table\Table; | ||||
| use Joomla\Component\Contenthistory\Administrator\Helper\ContenthistoryHelper; | ||||
|  | ||||
| // phpcs:disable PSR1.Files.SideEffects | ||||
| \defined('_JEXEC') or die; | ||||
| // phpcs:enable PSR1.Files.SideEffects | ||||
|  | ||||
| /** | ||||
|  * Methods supporting a list of contenthistory records. | ||||
|  * | ||||
|  * @since  3.2 | ||||
|  */ | ||||
| class CompareModel extends ListModel | ||||
| { | ||||
|     /** | ||||
|      * Method to get a version history row. | ||||
|      * | ||||
|      * @return  array|boolean    On success, array of populated tables. False on failure. | ||||
|      * | ||||
|      * @since   3.2 | ||||
|      * | ||||
|      * @throws  NotAllowed   Thrown if not authorised to edit an item | ||||
|      */ | ||||
|     public function getItems() | ||||
|     { | ||||
|         $input = Factory::getApplication()->getInput(); | ||||
|  | ||||
|         /** @var ContentHistory $table1 */ | ||||
|         $table1 = $this->getTable('ContentHistory'); | ||||
|  | ||||
|         /** @var ContentHistory $table2 */ | ||||
|         $table2 = $this->getTable('ContentHistory'); | ||||
|  | ||||
|         $id1 = $input->getInt('id1'); | ||||
|         $id2 = $input->getInt('id2'); | ||||
|  | ||||
|         if (!$id1 || \is_array($id1) || !$id2 || \is_array($id2)) { | ||||
|             $this->setError(Text::_('COM_CONTENTHISTORY_ERROR_INVALID_ID')); | ||||
|  | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         $result = []; | ||||
|  | ||||
|         if (!$table1->load($id1) || !$table2->load($id2)) { | ||||
|             $this->setError(Text::_('COM_CONTENTHISTORY_ERROR_VERSION_NOT_FOUND')); | ||||
|  | ||||
|             // Assume a failure to load the content means broken data, abort mission | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         // Get the first history record's content type record so we can check ACL | ||||
|         /** @var ContentType $contentTypeTable */ | ||||
|         $contentTypeTable = $this->getTable('ContentType'); | ||||
|         $typeAlias        = explode('.', $table1->item_id); | ||||
|         array_pop($typeAlias); | ||||
|         $typeAlias        = implode('.', $typeAlias); | ||||
|  | ||||
|         if (!$contentTypeTable->load(['type_alias' => $typeAlias])) { | ||||
|             $this->setError(Text::_('COM_CONTENTHISTORY_ERROR_FAILED_LOADING_CONTENT_TYPE')); | ||||
|  | ||||
|             // Assume a failure to load the content type means broken data, abort mission | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         $user = $this->getCurrentUser(); | ||||
|  | ||||
|         // Access check | ||||
|         if (!$user->authorise('core.edit', $table1->item_id) && !$this->canEdit($table1)) { | ||||
|             throw new NotAllowed(Text::_('JERROR_ALERTNOAUTHOR'), 403); | ||||
|         } | ||||
|  | ||||
|         $nullDate = $this->getDatabase()->getNullDate(); | ||||
|  | ||||
|         foreach ([$table1, $table2] as $table) { | ||||
|             $object               = new \stdClass(); | ||||
|             $object->data         = ContenthistoryHelper::prepareData($table); | ||||
|             $object->version_note = $table->version_note; | ||||
|  | ||||
|             // Let's use custom calendars when present | ||||
|             $object->save_date = HTMLHelper::_('date', $table->save_date, Text::_('DATE_FORMAT_LC6')); | ||||
|  | ||||
|             $dateProperties = [ | ||||
|                 'modified_time', | ||||
|                 'created_time', | ||||
|                 'modified', | ||||
|                 'created', | ||||
|                 'checked_out_time', | ||||
|                 'publish_up', | ||||
|                 'publish_down', | ||||
|             ]; | ||||
|  | ||||
|             foreach ($dateProperties as $dateProperty) { | ||||
|                 if ( | ||||
|                     property_exists($object->data, $dateProperty) | ||||
|                     && $object->data->$dateProperty->value !== null | ||||
|                     && $object->data->$dateProperty->value !== $nullDate | ||||
|                 ) { | ||||
|                     $object->data->$dateProperty->value = HTMLHelper::_( | ||||
|                         'date', | ||||
|                         $object->data->$dateProperty->value, | ||||
|                         Text::_('DATE_FORMAT_LC6') | ||||
|                     ); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             $result[] = $object; | ||||
|         } | ||||
|  | ||||
|         return $result; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Method to get a table object, load it if necessary. | ||||
|      * | ||||
|      * @param   string  $type    The table name. Optional. | ||||
|      * @param   string  $prefix  The class prefix. Optional. | ||||
|      * @param   array   $config  Configuration array for model. Optional. | ||||
|      * | ||||
|      * @return  Table   A Table object | ||||
|      * | ||||
|      * @since   3.2 | ||||
|      */ | ||||
|     public function getTable($type = 'Contenthistory', $prefix = 'Joomla\\CMS\\Table\\', $config = []) | ||||
|     { | ||||
|         return Table::getInstance($type, $prefix, $config); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Method to test whether a record is editable | ||||
|      * | ||||
|      * @param   ContentHistory  $record  A Table object. | ||||
|      * | ||||
|      * @return  boolean  True if allowed to edit the record. Defaults to the permission set in the component. | ||||
|      * | ||||
|      * @since   3.6 | ||||
|      */ | ||||
|     protected function canEdit($record) | ||||
|     { | ||||
|         $result = false; | ||||
|  | ||||
|         if (!empty($record->item_id)) { | ||||
|             /** | ||||
|              * Make sure user has edit privileges for this content item. Note that we use edit permissions | ||||
|              * for the content item, not delete permissions for the content history row. | ||||
|              */ | ||||
|             $user   = $this->getCurrentUser(); | ||||
|             $result = $user->authorise('core.edit', $record->item_id); | ||||
|  | ||||
|             // Finally try session (this catches edit.own case too) | ||||
|             if (!$result) { | ||||
|                 /** @var ContentType $contentTypeTable */ | ||||
|                 $contentTypeTable = $this->getTable('ContentType'); | ||||
|  | ||||
|                 $typeAlias        = explode('.', $record->item_id); | ||||
|                 $id               = array_pop($typeAlias); | ||||
|                 $typeAlias        = implode('.', $typeAlias); | ||||
|                 $contentTypeTable->load(['type_alias' => $typeAlias]); | ||||
|                 $typeEditables = (array) Factory::getApplication()->getUserState(str_replace('.', '.edit.', $contentTypeTable->type_alias) . '.id'); | ||||
|                 $result        = \in_array((int) $id, $typeEditables); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return $result; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,395 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * @package     Joomla.Administrator | ||||
|  * @subpackage  com_contenthistory | ||||
|  * | ||||
|  * @copyright   (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> | ||||
|  * @license     GNU General Public License version 2 or later; see LICENSE.txt | ||||
|  */ | ||||
|  | ||||
| namespace Joomla\Component\Contenthistory\Administrator\Model; | ||||
|  | ||||
| use Joomla\CMS\Access\Exception\NotAllowed; | ||||
| use Joomla\CMS\Component\ComponentHelper; | ||||
| use Joomla\CMS\Factory; | ||||
| use Joomla\CMS\Helper\CMSHelper; | ||||
| use Joomla\CMS\Language\Text; | ||||
| use Joomla\CMS\Log\Log; | ||||
| use Joomla\CMS\MVC\Factory\MVCFactoryInterface; | ||||
| use Joomla\CMS\MVC\Model\ListModel; | ||||
| use Joomla\CMS\Table\ContentHistory; | ||||
| use Joomla\CMS\Table\ContentType; | ||||
| use Joomla\CMS\Table\Table; | ||||
| use Joomla\Database\ParameterType; | ||||
| use Joomla\Database\QueryInterface; | ||||
|  | ||||
| // phpcs:disable PSR1.Files.SideEffects | ||||
| \defined('_JEXEC') or die; | ||||
| // phpcs:enable PSR1.Files.SideEffects | ||||
|  | ||||
| /** | ||||
|  * Methods supporting a list of contenthistory records. | ||||
|  * | ||||
|  * @since  3.2 | ||||
|  */ | ||||
| class HistoryModel extends ListModel | ||||
| { | ||||
|     /** | ||||
|      * Constructor. | ||||
|      * | ||||
|      * @param   array                 $config   An optional associative array of configuration settings. | ||||
|      * @param   ?MVCFactoryInterface  $factory  The factory. | ||||
|      * | ||||
|      * @see     \Joomla\CMS\MVC\Model\BaseDatabaseModel | ||||
|      * @since   3.2 | ||||
|      */ | ||||
|     public function __construct($config = [], ?MVCFactoryInterface $factory = null) | ||||
|     { | ||||
|         if (empty($config['filter_fields'])) { | ||||
|             $config['filter_fields'] = [ | ||||
|                 'version_id', | ||||
|                 'h.version_id', | ||||
|                 'version_note', | ||||
|                 'h.version_note', | ||||
|                 'save_date', | ||||
|                 'h.save_date', | ||||
|                 'editor_user_id', | ||||
|                 'h.editor_user_id', | ||||
|             ]; | ||||
|         } | ||||
|  | ||||
|         parent::__construct($config, $factory); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Method to test whether a record is editable | ||||
|      * | ||||
|      * @param   ContentHistory  $record  A Table object. | ||||
|      * | ||||
|      * @return  boolean  True if allowed to edit the record. Defaults to the permission set in the component. | ||||
|      * | ||||
|      * @since   3.2 | ||||
|      */ | ||||
|     protected function canEdit($record) | ||||
|     { | ||||
|         if (empty($record->item_id)) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * Make sure user has edit privileges for this content item. Note that we use edit permissions | ||||
|          * for the content item, not delete permissions for the content history row. | ||||
|          */ | ||||
|         $user   = $this->getCurrentUser(); | ||||
|  | ||||
|         if ($user->authorise('core.edit', $record->item_id)) { | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         // Finally try session (this catches edit.own case too) | ||||
|         /** @var ContentType $contentTypeTable */ | ||||
|         $contentTypeTable = $this->getTable('ContentType'); | ||||
|  | ||||
|         $typeAlias        = explode('.', $record->item_id); | ||||
|         $id               = array_pop($typeAlias); | ||||
|         $typeAlias        = implode('.', $typeAlias); | ||||
|         $contentTypeTable->load(['type_alias' => $typeAlias]); | ||||
|         $typeEditables = (array) Factory::getApplication()->getUserState(str_replace('.', '.edit.', $contentTypeTable->type_alias) . '.id'); | ||||
|         $result        = \in_array((int) $id, $typeEditables); | ||||
|  | ||||
|         return $result; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Method to test whether a history record can be deleted. Note that we check whether we have edit permissions | ||||
|      * for the content item row. | ||||
|      * | ||||
|      * @param   ContentHistory  $record  A Table object. | ||||
|      * | ||||
|      * @return  boolean  True if allowed to delete the record. Defaults to the permission set in the component. | ||||
|      * | ||||
|      * @since   3.6 | ||||
|      */ | ||||
|     protected function canDelete($record) | ||||
|     { | ||||
|         return $this->canEdit($record); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Method to delete one or more records from content history table. | ||||
|      * | ||||
|      * @param   array  $pks  An array of record primary keys. | ||||
|      * | ||||
|      * @return  boolean  True if successful, false if an error occurs. | ||||
|      * | ||||
|      * @since   3.2 | ||||
|      */ | ||||
|     public function delete(&$pks) | ||||
|     { | ||||
|         $pks   = (array) $pks; | ||||
|         $table = $this->getTable(); | ||||
|  | ||||
|         // Iterate the items to delete each one. | ||||
|         foreach ($pks as $i => $pk) { | ||||
|             if ($table->load($pk)) { | ||||
|                 if ((int) $table->keep_forever === 1) { | ||||
|                     unset($pks[$i]); | ||||
|                     continue; | ||||
|                 } | ||||
|  | ||||
|                 if ($this->canEdit($table)) { | ||||
|                     if (!$table->delete($pk)) { | ||||
|                         $this->setError($table->getError()); | ||||
|  | ||||
|                         return false; | ||||
|                     } | ||||
|                 } else { | ||||
|                     // Prune items that you can't change. | ||||
|                     unset($pks[$i]); | ||||
|                     $error = $this->getError(); | ||||
|  | ||||
|                     if ($error) { | ||||
|                         try { | ||||
|                             Log::add($error, Log::WARNING, 'jerror'); | ||||
|                         } catch (\RuntimeException $exception) { | ||||
|                             Factory::getApplication()->enqueueMessage($error, 'warning'); | ||||
|                         } | ||||
|  | ||||
|                         return false; | ||||
|                     } | ||||
|  | ||||
|                     try { | ||||
|                         Log::add(Text::_('JLIB_APPLICATION_ERROR_DELETE_NOT_PERMITTED'), Log::WARNING, 'jerror'); | ||||
|                     } catch (\RuntimeException $exception) { | ||||
|                         Factory::getApplication()->enqueueMessage(Text::_('JLIB_APPLICATION_ERROR_DELETE_NOT_PERMITTED'), 'warning'); | ||||
|                     } | ||||
|  | ||||
|                     return false; | ||||
|                 } | ||||
|             } else { | ||||
|                 $this->setError($table->getError()); | ||||
|  | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Clear the component's cache | ||||
|         $this->cleanCache(); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Method to get an array of data items. | ||||
|      * | ||||
|      * @return  mixed  An array of data items on success, false on failure. | ||||
|      * | ||||
|      * @since   3.4.5 | ||||
|      * | ||||
|      * @throws  NotAllowed   Thrown if not authorised to edit an item | ||||
|      */ | ||||
|     public function getItems() | ||||
|     { | ||||
|         $items = parent::getItems(); | ||||
|         $user  = $this->getCurrentUser(); | ||||
|  | ||||
|         if ($items === false) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         // This should be an array with at least one element | ||||
|         if (!\is_array($items) || !isset($items[0])) { | ||||
|             return $items; | ||||
|         } | ||||
|  | ||||
|         // Access check | ||||
|         if (!$user->authorise('core.edit', $items[0]->item_id) && !$this->canEdit($items[0])) { | ||||
|             throw new NotAllowed(Text::_('JERROR_ALERTNOAUTHOR'), 403); | ||||
|         } | ||||
|  | ||||
|         return $items; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Method to get a table object, load it if necessary. | ||||
|      * | ||||
|      * @param   string  $type    The table name. Optional. | ||||
|      * @param   string  $prefix  The class prefix. Optional. | ||||
|      * @param   array   $config  Configuration array for model. Optional. | ||||
|      * | ||||
|      * @return  Table   A Table object | ||||
|      * | ||||
|      * @since   3.2 | ||||
|      */ | ||||
|     public function getTable($type = 'ContentHistory', $prefix = 'Joomla\\CMS\\Table\\', $config = []) | ||||
|     { | ||||
|         return Table::getInstance($type, $prefix, $config); | ||||
|     } | ||||
|     /** | ||||
|      * Method to toggle on and off the keep forever value for one or more records from content history table. | ||||
|      * | ||||
|      * @param   array  $pks  An array of record primary keys. | ||||
|      * | ||||
|      * @return  boolean  True if successful, false if an error occurs. | ||||
|      * | ||||
|      * @since   3.2 | ||||
|      */ | ||||
|     public function keep(&$pks) | ||||
|     { | ||||
|         $pks   = (array) $pks; | ||||
|         $table = $this->getTable(); | ||||
|  | ||||
|         // Iterate the items to delete each one. | ||||
|         foreach ($pks as $i => $pk) { | ||||
|             if ($table->load($pk)) { | ||||
|                 if ($this->canEdit($table)) { | ||||
|                     $table->keep_forever = $table->keep_forever ? 0 : 1; | ||||
|  | ||||
|                     if (!$table->store()) { | ||||
|                         $this->setError($table->getError()); | ||||
|  | ||||
|                         return false; | ||||
|                     } | ||||
|                 } else { | ||||
|                     // Prune items that you can't change. | ||||
|                     unset($pks[$i]); | ||||
|                     $error = $this->getError(); | ||||
|  | ||||
|                     if ($error) { | ||||
|                         try { | ||||
|                             Log::add($error, Log::WARNING, 'jerror'); | ||||
|                         } catch (\RuntimeException $exception) { | ||||
|                             Factory::getApplication()->enqueueMessage($error, 'warning'); | ||||
|                         } | ||||
|  | ||||
|                         return false; | ||||
|                     } | ||||
|  | ||||
|                     try { | ||||
|                         Log::add(Text::_('COM_CONTENTHISTORY_ERROR_KEEP_NOT_PERMITTED'), Log::WARNING, 'jerror'); | ||||
|                     } catch (\RuntimeException $exception) { | ||||
|                         Factory::getApplication()->enqueueMessage(Text::_('COM_CONTENTHISTORY_ERROR_KEEP_NOT_PERMITTED'), 'warning'); | ||||
|                     } | ||||
|  | ||||
|                     return false; | ||||
|                 } | ||||
|             } else { | ||||
|                 $this->setError($table->getError()); | ||||
|  | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Clear the component's cache | ||||
|         $this->cleanCache(); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Method to auto-populate the model state. | ||||
|      * | ||||
|      * Note. Calling getState in this method will result in recursion. | ||||
|      * | ||||
|      * @param   string  $ordering   An optional ordering field. | ||||
|      * @param   string  $direction  An optional direction (asc|desc). | ||||
|      * | ||||
|      * @return  void | ||||
|      * | ||||
|      * @since   3.2 | ||||
|      */ | ||||
|     protected function populateState($ordering = 'h.save_date', $direction = 'DESC') | ||||
|     { | ||||
|         $input  = Factory::getApplication()->getInput(); | ||||
|         $itemId = $input->get('item_id', '', 'string'); | ||||
|  | ||||
|         $this->setState('item_id', $itemId); | ||||
|         $this->setState('sha1_hash', $this->getSha1Hash()); | ||||
|  | ||||
|         // Load the parameters. | ||||
|         $params = ComponentHelper::getParams('com_contenthistory'); | ||||
|         $this->setState('params', $params); | ||||
|  | ||||
|         // List state information. | ||||
|         parent::populateState($ordering, $direction); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Build an SQL query to load the list data. | ||||
|      * | ||||
|      * @return  QueryInterface | ||||
|      * | ||||
|      * @since   3.2 | ||||
|      */ | ||||
|     protected function getListQuery() | ||||
|     { | ||||
|         // Create a new query object. | ||||
|         $db     = $this->getDatabase(); | ||||
|         $query  = $db->getQuery(true); | ||||
|         $itemId = $this->getState('item_id'); | ||||
|  | ||||
|         // Select the required fields from the table. | ||||
|         $query->select( | ||||
|             $this->getState( | ||||
|                 'list.select', | ||||
|                 [ | ||||
|                     $db->quoteName('h.version_id'), | ||||
|                     $db->quoteName('h.item_id'), | ||||
|                     $db->quoteName('h.version_note'), | ||||
|                     $db->quoteName('h.save_date'), | ||||
|                     $db->quoteName('h.editor_user_id'), | ||||
|                     $db->quoteName('h.character_count'), | ||||
|                     $db->quoteName('h.sha1_hash'), | ||||
|                     $db->quoteName('h.version_data'), | ||||
|                     $db->quoteName('h.keep_forever'), | ||||
|                 ] | ||||
|             ) | ||||
|         ) | ||||
|             ->from($db->quoteName('#__history', 'h')) | ||||
|             ->where($db->quoteName('h.item_id') . ' = :itemid') | ||||
|             ->bind(':itemid', $itemId, ParameterType::STRING) | ||||
|  | ||||
|         // Join over the users for the editor | ||||
|             ->select($db->quoteName('uc.name', 'editor')) | ||||
|             ->join( | ||||
|                 'LEFT', | ||||
|                 $db->quoteName('#__users', 'uc'), | ||||
|                 $db->quoteName('uc.id') . ' = ' . $db->quoteName('h.editor_user_id') | ||||
|             ); | ||||
|  | ||||
|         // Add the list ordering clause. | ||||
|         $orderCol  = $this->state->get('list.ordering'); | ||||
|         $orderDirn = $this->state->get('list.direction'); | ||||
|         $query->order($db->quoteName($orderCol) . $orderDirn); | ||||
|  | ||||
|         return $query; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the sha1 hash value for the current item being edited. | ||||
|      * | ||||
|      * @return  string  sha1 hash of row data | ||||
|      * | ||||
|      * @since   3.2 | ||||
|      */ | ||||
|     protected function getSha1Hash() | ||||
|     { | ||||
|         $result    = false; | ||||
|         $item_id   = Factory::getApplication()->getInput()->getCmd('item_id', ''); | ||||
|         $typeAlias = explode('.', $item_id); | ||||
|         Table::addIncludePath(JPATH_ADMINISTRATOR . '/components/' . $typeAlias[0] . '/tables'); | ||||
|         $typeTable = $this->getTable('ContentType'); | ||||
|         $typeTable->load(['type_alias' => $typeAlias[0] . '.' . $typeAlias[1]]); | ||||
|         $contentTable = $typeTable->getContentTable(); | ||||
|  | ||||
|         if ($contentTable && $contentTable->load($typeAlias[2])) { | ||||
|             $helper = new CMSHelper(); | ||||
|  | ||||
|             $dataObject = $helper->getDataObject($contentTable); | ||||
|             $result     = $this->getTable('ContentHistory')->getSha1(json_encode($dataObject), $typeTable); | ||||
|         } | ||||
|  | ||||
|         return $result; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,150 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * @package     Joomla.Administrator | ||||
|  * @subpackage  com_contenthistory | ||||
|  * | ||||
|  * @copyright   (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> | ||||
|  * @license     GNU General Public License version 2 or later; see LICENSE.txt | ||||
|  */ | ||||
|  | ||||
| namespace Joomla\Component\Contenthistory\Administrator\Model; | ||||
|  | ||||
| use Joomla\CMS\Access\Exception\NotAllowed; | ||||
| use Joomla\CMS\Factory; | ||||
| use Joomla\CMS\HTML\HTMLHelper; | ||||
| use Joomla\CMS\Language\Text; | ||||
| use Joomla\CMS\MVC\Model\ItemModel; | ||||
| use Joomla\CMS\Table\ContentHistory; | ||||
| use Joomla\CMS\Table\ContentType; | ||||
| use Joomla\CMS\Table\Table; | ||||
| use Joomla\Component\Contenthistory\Administrator\Helper\ContenthistoryHelper; | ||||
|  | ||||
| // phpcs:disable PSR1.Files.SideEffects | ||||
| \defined('_JEXEC') or die; | ||||
| // phpcs:enable PSR1.Files.SideEffects | ||||
|  | ||||
| /** | ||||
|  * Methods supporting a list of contenthistory records. | ||||
|  * | ||||
|  * @since  3.2 | ||||
|  */ | ||||
| class PreviewModel extends ItemModel | ||||
| { | ||||
|     /** | ||||
|      * Method to get a version history row. | ||||
|      * | ||||
|      * @param   integer  $pk  The id of the item | ||||
|      * | ||||
|      * @return  \stdClass|boolean    On success, standard object with row data. False on failure. | ||||
|      * | ||||
|      * @since   3.2 | ||||
|      * | ||||
|      * @throws  NotAllowed   Thrown if not authorised to edit an item | ||||
|      */ | ||||
|     public function getItem($pk = null) | ||||
|     { | ||||
|         /** @var ContentHistory $table */ | ||||
|         $table     = $this->getTable('ContentHistory'); | ||||
|         $versionId = Factory::getApplication()->getInput()->getInt('version_id'); | ||||
|  | ||||
|         if (!$versionId || \is_array($versionId) || !$table->load($versionId)) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         $user = $this->getCurrentUser(); | ||||
|  | ||||
|         // Access check | ||||
|         if (!$user->authorise('core.edit', $table->item_id) && !$this->canEdit($table)) { | ||||
|             throw new NotAllowed(Text::_('JERROR_ALERTNOAUTHOR'), 403); | ||||
|         } | ||||
|  | ||||
|         $result               = new \stdClass(); | ||||
|         $result->version_note = $table->version_note; | ||||
|         $result->data         = ContenthistoryHelper::prepareData($table); | ||||
|  | ||||
|         // Let's use custom calendars when present | ||||
|         $result->save_date = HTMLHelper::_('date', $table->save_date, Text::_('DATE_FORMAT_LC6')); | ||||
|  | ||||
|         $dateProperties = [ | ||||
|             'modified_time', | ||||
|             'created_time', | ||||
|             'modified', | ||||
|             'created', | ||||
|             'checked_out_time', | ||||
|             'publish_up', | ||||
|             'publish_down', | ||||
|         ]; | ||||
|  | ||||
|         $nullDate = $this->getDatabase()->getNullDate(); | ||||
|  | ||||
|         foreach ($dateProperties as $dateProperty) { | ||||
|             if ( | ||||
|                 property_exists($result->data, $dateProperty) | ||||
|                 && $result->data->$dateProperty->value !== null | ||||
|                 && $result->data->$dateProperty->value !== $nullDate | ||||
|             ) { | ||||
|                 $result->data->$dateProperty->value = HTMLHelper::_( | ||||
|                     'date', | ||||
|                     $result->data->$dateProperty->value, | ||||
|                     Text::_('DATE_FORMAT_LC6') | ||||
|                 ); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return $result; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Method to get a table object, load it if necessary. | ||||
|      * | ||||
|      * @param   string  $type    The table name. Optional. | ||||
|      * @param   string  $prefix  The class prefix. Optional. | ||||
|      * @param   array   $config  Configuration array for model. Optional. | ||||
|      * | ||||
|      * @return  Table   A Table object | ||||
|      * | ||||
|      * @since   3.2 | ||||
|      */ | ||||
|     public function getTable($type = 'ContentHistory', $prefix = 'Joomla\\CMS\\Table\\', $config = []) | ||||
|     { | ||||
|         return Table::getInstance($type, $prefix, $config); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Method to test whether a record is editable | ||||
|      * | ||||
|      * @param   ContentHistory  $record  A Table object. | ||||
|      * | ||||
|      * @return  boolean  True if allowed to edit the record. Defaults to the permission set in the component. | ||||
|      * | ||||
|      * @since   3.6 | ||||
|      */ | ||||
|     protected function canEdit($record) | ||||
|     { | ||||
|         $result = false; | ||||
|  | ||||
|         if (!empty($record->item_id)) { | ||||
|             /** | ||||
|              * Make sure user has edit privileges for this content item. Note that we use edit permissions | ||||
|              * for the content item, not delete permissions for the content history row. | ||||
|              */ | ||||
|             $user   = $this->getCurrentUser(); | ||||
|             $result = $user->authorise('core.edit', $record->item_id); | ||||
|  | ||||
|             // Finally try session (this catches edit.own case too) | ||||
|             if (!$result) { | ||||
|                 /** @var ContentType $contentTypeTable */ | ||||
|                 $contentTypeTable = $this->getTable('ContentType'); | ||||
|  | ||||
|                 $typeAlias        = explode('.', $record->item_id); | ||||
|                 $id               = array_pop($typeAlias); | ||||
|                 $typeAlias        = implode('.', $typeAlias); | ||||
|                 $typeEditables    = (array) Factory::getApplication()->getUserState(str_replace('.', '.edit.', $contentTypeTable->type_alias) . '.id'); | ||||
|                 $result           = \in_array((int) $id, $typeEditables); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return $result; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,62 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * @package     Joomla.Administrator | ||||
|  * @subpackage  com_contenthistory | ||||
|  * | ||||
|  * @copyright   (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> | ||||
|  * @license     GNU General Public License version 2 or later; see LICENSE.txt | ||||
|  */ | ||||
|  | ||||
| namespace Joomla\Component\Contenthistory\Administrator\View\Compare; | ||||
|  | ||||
| use Joomla\CMS\MVC\View\GenericDataException; | ||||
| use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView; | ||||
|  | ||||
| // phpcs:disable PSR1.Files.SideEffects | ||||
| \defined('_JEXEC') or die; | ||||
| // phpcs:enable PSR1.Files.SideEffects | ||||
|  | ||||
| /** | ||||
|  * View class for a list of contenthistory. | ||||
|  * | ||||
|  * @since  3.2 | ||||
|  */ | ||||
| class HtmlView extends BaseHtmlView | ||||
| { | ||||
|     /** | ||||
|      * An array of items | ||||
|      * | ||||
|      * @var  array | ||||
|      */ | ||||
|     protected $items; | ||||
|  | ||||
|     /** | ||||
|      * The model state | ||||
|      * | ||||
|      * @var  \Joomla\Registry\Registry | ||||
|      */ | ||||
|     protected $state; | ||||
|  | ||||
|     /** | ||||
|      * Method to display the view. | ||||
|      * | ||||
|      * @param   string  $tpl  A template file to load. [optional] | ||||
|      * | ||||
|      * @return  void | ||||
|      * | ||||
|      * @since   3.2 | ||||
|      */ | ||||
|     public function display($tpl = null) | ||||
|     { | ||||
|         $this->state = $this->get('State'); | ||||
|         $this->items = $this->get('Items'); | ||||
|  | ||||
|         // Check for errors. | ||||
|         if (\count($errors = $this->get('Errors'))) { | ||||
|             throw new GenericDataException(implode("\n", $errors), 500); | ||||
|         } | ||||
|  | ||||
|         parent::display($tpl); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,148 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * @package     Joomla.Administrator | ||||
|  * @subpackage  com_contenthistory | ||||
|  * | ||||
|  * @copyright   (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> | ||||
|  * @license     GNU General Public License version 2 or later; see LICENSE.txt | ||||
|  */ | ||||
|  | ||||
| namespace Joomla\Component\Contenthistory\Administrator\View\History; | ||||
|  | ||||
| use Joomla\CMS\Factory; | ||||
| use Joomla\CMS\Filter\InputFilter; | ||||
| use Joomla\CMS\MVC\View\GenericDataException; | ||||
| use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView; | ||||
| use Joomla\CMS\Pagination\Pagination; | ||||
| use Joomla\CMS\Router\Route; | ||||
| use Joomla\CMS\Session\Session; | ||||
| use Joomla\CMS\Toolbar\Toolbar; | ||||
| use Joomla\CMS\Toolbar\ToolbarFactoryInterface; | ||||
|  | ||||
| // phpcs:disable PSR1.Files.SideEffects | ||||
| \defined('_JEXEC') or die; | ||||
| // phpcs:enable PSR1.Files.SideEffects | ||||
|  | ||||
| /** | ||||
|  * View class for a list of contenthistory. | ||||
|  * | ||||
|  * @since  3.2 | ||||
|  */ | ||||
| class HtmlView extends BaseHtmlView | ||||
| { | ||||
|     /** | ||||
|      * An array of items | ||||
|      * | ||||
|      * @var  array | ||||
|      */ | ||||
|     protected $items; | ||||
|  | ||||
|     /** | ||||
|      * The model state | ||||
|      * | ||||
|      * @var  Pagination | ||||
|      */ | ||||
|     protected $pagination; | ||||
|  | ||||
|     /** | ||||
|      * The model state | ||||
|      * | ||||
|      * @var  \Joomla\Registry\Registry | ||||
|      */ | ||||
|     protected $state; | ||||
|  | ||||
|     /** | ||||
|      * The toolbar for the history modal. Note this is rendered inside the modal rather than using the regular module | ||||
|      * | ||||
|      * @var  Toolbar | ||||
|      */ | ||||
|     protected $toolbar; | ||||
|  | ||||
|     /** | ||||
|      * Method to display the view. | ||||
|      * | ||||
|      * @param   string  $tpl  A template file to load. [optional] | ||||
|      * | ||||
|      * @return  void | ||||
|      * | ||||
|      * @since   3.2 | ||||
|      */ | ||||
|     public function display($tpl = null) | ||||
|     { | ||||
|         $this->state      = $this->get('State'); | ||||
|         $this->items      = $this->get('Items'); | ||||
|         $this->pagination = $this->get('Pagination'); | ||||
|  | ||||
|         // Check for errors. | ||||
|         if (\count($errors = $this->get('Errors'))) { | ||||
|             throw new GenericDataException(implode("\n", $errors), 500); | ||||
|         } | ||||
|  | ||||
|         $this->toolbar = $this->addToolbar(); | ||||
|  | ||||
|         parent::display($tpl); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Add the page toolbar. | ||||
|      * | ||||
|      * @return  Toolbar | ||||
|      * | ||||
|      * @since  4.0.0 | ||||
|      */ | ||||
|     protected function addToolbar(): Toolbar | ||||
|     { | ||||
|         /** @var Toolbar $toolbar */ | ||||
|         $toolbar = Factory::getContainer()->get(ToolbarFactoryInterface::class)->createToolbar('toolbar'); | ||||
|  | ||||
|         // Cache a session token for reuse throughout. | ||||
|         $token = Session::getFormToken(); | ||||
|  | ||||
|         // Clean up input to ensure a clean url. | ||||
|         $filter     = InputFilter::getInstance(); | ||||
|         $aliasArray = explode('.', $this->state->item_id); | ||||
|  | ||||
|         if ($aliasArray[1] === 'category') { | ||||
|             $option = 'com_categories'; | ||||
|             $append = '&extension=' . $filter->clean($aliasArray[0], 'cmd'); | ||||
|         } else { | ||||
|             $option = $aliasArray[0]; | ||||
|             $append = ''; | ||||
|         } | ||||
|  | ||||
|         $task       = $filter->clean($aliasArray[1], 'cmd') . '.loadhistory'; | ||||
|  | ||||
|         // Build the final urls. | ||||
|         $loadUrl    = Route::_('index.php?option=' . $filter->clean($option, 'cmd') . $append . '&task=' . $task . '&' . $token . '=1'); | ||||
|         $previewUrl = Route::_('index.php?option=com_contenthistory&view=preview&layout=preview&tmpl=component&' . $token . '=1'); | ||||
|         $compareUrl = Route::_('index.php?option=com_contenthistory&view=compare&layout=compare&tmpl=component&' . $token . '=1'); | ||||
|  | ||||
|         $toolbar->basicButton('load', 'COM_CONTENTHISTORY_BUTTON_LOAD') | ||||
|             ->attributes(['data-url' => $loadUrl]) | ||||
|             ->icon('icon-upload') | ||||
|             ->buttonClass('btn btn-success') | ||||
|             ->listCheck(true); | ||||
|  | ||||
|         $toolbar->basicButton('preview', 'COM_CONTENTHISTORY_BUTTON_PREVIEW') | ||||
|             ->attributes(['data-url' => $previewUrl]) | ||||
|             ->icon('icon-search') | ||||
|             ->listCheck(true); | ||||
|  | ||||
|         $toolbar->basicButton('compare', 'COM_CONTENTHISTORY_BUTTON_COMPARE') | ||||
|             ->attributes(['data-url' => $compareUrl]) | ||||
|             ->icon('icon-search-plus') | ||||
|             ->listCheck(true); | ||||
|  | ||||
|         $toolbar->basicButton('keep', 'COM_CONTENTHISTORY_BUTTON_KEEP', 'history.keep') | ||||
|             ->icon('icon-lock') | ||||
|             ->listCheck(true); | ||||
|  | ||||
|         $toolbar->basicButton('delete', 'COM_CONTENTHISTORY_BUTTON_DELETE', 'history.delete') | ||||
|             ->buttonClass('btn btn-danger') | ||||
|             ->icon('icon-times') | ||||
|             ->listCheck(true); | ||||
|  | ||||
|         return $toolbar; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,69 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * @package     Joomla.Administrator | ||||
|  * @subpackage  com_contenthistory | ||||
|  * | ||||
|  * @copyright   (C) 2013 Open Source Matters, Inc. <https://www.joomla.org> | ||||
|  * @license     GNU General Public License version 2 or later; see LICENSE.txt | ||||
|  */ | ||||
|  | ||||
| namespace Joomla\Component\Contenthistory\Administrator\View\Preview; | ||||
|  | ||||
| use Joomla\CMS\Language\Text; | ||||
| use Joomla\CMS\MVC\View\GenericDataException; | ||||
| use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView; | ||||
|  | ||||
| // phpcs:disable PSR1.Files.SideEffects | ||||
| \defined('_JEXEC') or die; | ||||
| // phpcs:enable PSR1.Files.SideEffects | ||||
|  | ||||
| /** | ||||
|  * View class for a list of contenthistory. | ||||
|  * | ||||
|  * @since  1.5 | ||||
|  */ | ||||
| class HtmlView extends BaseHtmlView | ||||
| { | ||||
|     /** | ||||
|      * An array of items | ||||
|      * | ||||
|      * @var  \stdClass|false | ||||
|      */ | ||||
|     protected $item; | ||||
|  | ||||
|     /** | ||||
|      * The model state | ||||
|      * | ||||
|      * @var  \Joomla\Registry\Registry | ||||
|      */ | ||||
|     protected $state; | ||||
|  | ||||
|     /** | ||||
|      * Method to display the view. | ||||
|      * | ||||
|      * @param   string  $tpl  A template file to load. [optional] | ||||
|      * | ||||
|      * @return  void | ||||
|      * | ||||
|      * @since   3.2 | ||||
|      */ | ||||
|     public function display($tpl = null) | ||||
|     { | ||||
|         $this->state = $this->get('State'); | ||||
|         $this->item  = $this->get('Item'); | ||||
|  | ||||
|         if (false === $this->item) { | ||||
|             $this->getLanguage()->load('com_content', JPATH_SITE, null, true); | ||||
|  | ||||
|             throw new \Exception(Text::_('COM_CONTENT_ERROR_ARTICLE_NOT_FOUND'), 404); | ||||
|         } | ||||
|  | ||||
|         // Check for errors. | ||||
|         if (\count($errors = $this->get('Errors'))) { | ||||
|             throw new GenericDataException(implode("\n", $errors), 500); | ||||
|         } | ||||
|  | ||||
|         parent::display($tpl); | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user