Spade
Mini Shell
contenthistory.php000064400000001252151165257750010367 0ustar00<?php
/**
* @package Joomla.Site
* @subpackage com_contenthistory
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('_JEXEC') or die;
// Load the com_contenthistory language files, default to the admin file
and fall back to site if one isn't found
$lang = JFactory::getLanguage();
$lang->load('com_contenthistory', JPATH_ADMINISTRATOR, null,
false, true)
|| $lang->load('com_contenthistory', JPATH_SITE, null, false,
true);
// Hand processing over to the admin base file
require_once JPATH_COMPONENT_ADMINISTRATOR .
'/contenthistory.php';
contenthistory.xml000064400000002204151165257750010376 0ustar00<?xml
version="1.0" encoding="utf-8"?>
<extension type="component" version="3.2"
method="upgrade">
<name>com_contenthistory</name>
<author>Joomla! Project</author>
<creationDate>May 2013</creationDate>
<copyright>(C) 2005 - 2020 Open Source Matters. All rights
reserved.</copyright>
<license>GNU General Public License version 2 or later; see
LICENSE.txt</license>
<authorEmail>admin@joomla.org</authorEmail>
<authorUrl>www.joomla.org</authorUrl>
<version>3.2.0</version>
<description>COM_CONTENTHISTORY_XML_DESCRIPTION</description>
<files folder="site">
<filename>contenthistory.php</filename>
<filename>index.html</filename>
</files>
<administration>
<files folder="admin">
<filename>controller.php</filename>
<filename>contenthistory.php</filename>
<folder>controllers</folder>
<folder>helpers</folder>
<folder>media</folder>
<folder>models</folder>
<folder>views</folder>
</files>
<languages folder="admin">
<language
tag="en-GB">language/en-GB.com_contenthistory.ini</language>
<language
tag="en-GB">language/en-GB.com_contenthistory.sys.ini</language>
</languages>
</administration>
</extension>
controller.php000064400000000621151165257750007455 0ustar00<?php
/**
* @package Joomla.Administrator
* @subpackage com_contenthistory
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('_JEXEC') or die;
/**
* Contenthistory Controller
*
* @since 3.2
*/
class ContenthistoryController extends JControllerLegacy
{
}
controllers/history.php000064400000006047151165257750011351
0ustar00<?php
/**
* @package Joomla.Administrator
* @subpackage com_contenthistory
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('_JEXEC') or die;
use Joomla\Utilities\ArrayHelper;
/**
* Contenthistory list controller class.
*
* @since 3.2
*/
class ContenthistoryControllerHistory extends JControllerAdmin
{
/**
* Deletes and returns correctly.
*
* @return void
*
* @since 3.2
*/
public function delete()
{
$this->checkToken();
// Get items to remove from the request.
$cid = $this->input->get('cid', array(),
'array');
if (!is_array($cid) || count($cid) < 1)
{
JError::raiseWarning(500,
JText::_('COM_CONTENTHISTORY_NO_ITEM_SELECTED'));
}
else
{
// Get the model.
$model = $this->getModel();
// Make sure the item ids are integers
$cid = ArrayHelper::toInteger($cid);
// Remove the items.
if ($model->delete($cid))
{
$this->setMessage(JText::plural('COM_CONTENTHISTORY_N_ITEMS_DELETED',
count($cid)));
}
else
{
$this->setMessage($model->getError());
}
}
$this->setRedirect(
JRoute::_(
'index.php?option=com_contenthistory&view=history&layout=modal&tmpl=component&item_id='
. $this->input->getInt('item_id') .
'&type_id=' . $this->input->getInt('type_id')
. '&type_alias=' .
$this->input->getCmd('type_alias') . '&' .
JSession::getFormToken() . '=1', false
)
);
}
/**
* 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 JModelLegacy The model
*
* @since 3.2
*/
public function getModel($name = 'History', $prefix =
'ContenthistoryModel', $config = array('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 remove from the request.
$cid = $this->input->get('cid', array(),
'array');
if (!is_array($cid) || count($cid) < 1)
{
JError::raiseWarning(500,
JText::_('COM_CONTENTHISTORY_NO_ITEM_SELECTED'));
}
else
{
// Get the model.
$model = $this->getModel();
// Make sure the item ids are integers
$cid = ArrayHelper::toInteger($cid);
// Remove the items.
if ($model->keep($cid))
{
$this->setMessage(JText::plural('COM_CONTENTHISTORY_N_ITEMS_KEEP_TOGGLE',
count($cid)));
}
else
{
$this->setMessage($model->getError());
}
}
$this->setRedirect(
JRoute::_(
'index.php?option=com_contenthistory&view=history&layout=modal&tmpl=component&item_id='
. $this->input->getInt('item_id') .
'&type_id=' . $this->input->getInt('type_id')
. '&type_alias=' .
$this->input->getCmd('type_alias') . '&' .
JSession::getFormToken() . '=1', false
)
);
}
}
controllers/preview.php000064400000001546151165257750011330
0ustar00<?php
/**
* @package Joomla.Administrator
* @subpackage com_contenthistory
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('_JEXEC') or die;
/**
* Contenthistory list controller class.
*
* @since 3.2
*/
class ContenthistoryControllerPreview extends JControllerLegacy
{
/**
* 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 JModelLegacy The model
*
* @since 3.2
*/
public function getModel($name = 'Preview', $prefix =
'ContenthistoryModel', $config = array('ignore_request'
=> true))
{
return parent::getModel($name, $prefix, $config);
}
}
helpers/contenthistory.php000064400000026035151165257750012037
0ustar00<?php
/**
* @package Joomla.Administrator
* @subpackage com_contenthistory
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('_JEXEC') or die;
/**
* 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 = array();
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 stdClass $jsonString Standard class object that may contain
one or more JSON-encoded fields.
*
* @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 ($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 JTableContenttype $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, JTableContenttype
$typesTable)
{
$labels = array();
$values = array();
$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] =
JText::_($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] =
JText::_($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 JTableContenttype $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(JTableContenttype $typesTable)
{
$result = false;
jimport('joomla.filesystem.file');
jimport('joomla.filesystem.folder');
// 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)
&& JFile::exists(JPATH_ROOT . '/' .
$options->formFile))
{
$result = JPATH_ROOT . '/' . $options->formFile;
}
else
{
$aliasArray = explode('.', $typesTable->type_alias);
if (count($aliasArray) == 2)
{
$component = ($aliasArray[1] == 'category') ?
'com_categories' : $aliasArray[0];
$path = JFolder::makeSafe(JPATH_ADMINISTRATOR .
'/components/' . $component . '/models/forms/');
$file = JFile::makeSafe($aliasArray[1] . '.xml');
$result = JFile::exists($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 = JFactory::getDbo();
$query = $db->getQuery(true);
$query->select($db->quoteName($lookup->displayColumn))
->from($db->quoteName($lookup->targetTable))
->where($db->quoteName($lookup->targetColumn) . ' =
' . $db->quote($value));
$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 JTableContenttype $typeTable Table object with content
history options.
*
* @return stdClass object with hidden fields removed.
*
* @since 3.2
*/
public static function hideFields($object, JTableContenttype $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 = JFactory::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, null, false, true)
|| $lang->load($component, JPath::clean(JPATH_ADMINISTRATOR .
'/components/' . $component), null, false, true);
// Force loading of backend global language file
$lang->load('joomla', JPath::clean(JPATH_ADMINISTRATOR),
null, false, true);
}
}
/**
* 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 = isset($valuesArray[$name]) ?
$valuesArray[$name] : $value;
$result->$name->label = isset($labelsArray[$name]) ?
$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 = isset($valuesArray[$subName]) ?
$valuesArray[$subName] : $subValue;
$subObject->$subName->label = isset($labelsArray[$subName]) ?
$labelsArray[$subName] : $subName;
$result->$name->value = $subObject;
}
}
}
return $result;
}
/**
* Method to prepare the object for the preview and compare views.
*
* @param JTableContenthistory $table Table object loaded with data.
*
* @return stdClass Object ready for the views.
*
* @since 3.2
*/
public static function prepareData(JTableContenthistory $table)
{
$object = static::decodeFields($table->version_data);
$typesTable = JTable::getInstance('Contenttype');
$typesTable->load(array('type_id' =>
$table->ucm_type_id));
$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 JTableContenttype $typesTable Table object loaded with data.
*
* @return stdClass Object with lookup values inserted.
*
* @since 3.2
*/
public static function processLookupFields($object, JTableContenttype
$typesTable)
{
if ($options = json_decode($typesTable->content_history_options))
{
if (isset($options->displayLookup) &&
is_array($options->displayLookup))
{
foreach ($options->displayLookup as $lookup)
{
$sourceColumn = isset($lookup->sourceColumn) ?
$lookup->sourceColumn : false;
$sourceValue = isset($object->$sourceColumn->value) ?
$object->$sourceColumn->value : false;
if ($sourceColumn && $sourceValue && ($lookupValue =
static::getLookupValue($lookup, $sourceValue)))
{
$object->$sourceColumn->value = $lookupValue;
}
}
}
}
return $object;
}
}
helpers/html/textdiff.php000064400000003233151165257750011517
0ustar00<?php
/**
* @package Joomla.Administrator
* @subpackage com_contenthistory
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('JPATH_PLATFORM') or die;
/**
* HTML utility class for creating text diffs using jQuery,
diff_patch_match.js and jquery.pretty-text-diff.js JavaScript libraries.
*
* @since 3.2
*
* @deprecated 4.0 No replacement
*/
abstract class JHtmlTextdiff
{
/**
* @var array Array containing information for loaded files
* @since 3.2
*/
protected static $loaded = array();
/**
* Method to load Javascript text diff
*
* @param string $containerId DOM id of the element where the diff
will be rendered
*
* @return void
*
* @since 3.2
*/
public static function textdiff($containerId)
{
// Only load once
if (isset(static::$loaded[__METHOD__]))
{
return;
}
// Depends on jQuery UI
JHtml::_('bootstrap.framework');
JHtml::_('script',
'com_contenthistory/diff_match_patch.js',
array('version' => 'auto', 'relative'
=> true));
JHtml::_('script',
'com_contenthistory/jquery.pretty-text-diff.min.js',
array('version' => 'auto', 'relative'
=> true));
JHtml::_('stylesheet',
'com_contenthistory/jquery.pretty-text-diff.css',
array('version' => 'auto', 'relative'
=> true));
// Attach diff to document
JFactory::getDocument()->addScriptDeclaration("
(function ($){
$(document).ready(function (){
$('#" . $containerId . " tr').prettyTextDiff();
});
})(jQuery);
"
);
// Set static array
static::$loaded[__METHOD__] = true;
return;
}
}
models/compare.php000064400000010652151165257750010210 0ustar00<?php
/**
* @package Joomla.Administrator
* @subpackage com_contenthistory
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('_JEXEC') or die;
JLoader::register('ContenthistoryHelper', JPATH_ADMINISTRATOR .
'/components/com_contenthistory/helpers/contenthistory.php');
/**
* Methods supporting a list of contenthistory records.
*
* @since 3.2
*/
class ContenthistoryModelCompare extends JModelItem
{
/**
* Method to get a version history row.
*
* @return array|boolean On success, array of populated tables. False
on failure.
*
* @since 3.2
*/
public function getItems()
{
$input = JFactory::getApplication()->input;
/** @var JTableContenthistory $table1 */
$table1 = JTable::getInstance('Contenthistory');
/** @var JTableContenthistory $table2 */
$table2 = JTable::getInstance('Contenthistory');
$id1 = $input->getInt('id1');
$id2 = $input->getInt('id2');
if (!$id1 || \is_array($id1) || !$id2 || \is_array($id2))
{
$this->setError(\JText::_('COM_CONTENTHISTORY_ERROR_INVALID_ID'));
return false;
}
$result = array();
if ($table1->load($id1) && $table2->load($id2))
{
// Get the first history record's content type record so we can
check ACL
/** @var JTableContenttype $contentTypeTable */
$contentTypeTable = JTable::getInstance('Contenttype');
$ucmTypeId = $table1->ucm_type_id;
if (!$contentTypeTable->load($ucmTypeId))
{
$this->setError(\JText::_('COM_CONTENTHISTORY_ERROR_FAILED_LOADING_CONTENT_TYPE'));
// Assume a failure to load the content type means broken data, abort
mission
return false;
}
$user = JFactory::getUser();
// Access check
if ($user->authorise('core.edit',
$contentTypeTable->type_alias . '.' . (int)
$table1->ucm_item_id) || $this->canEdit($table1))
{
$return = true;
}
else
{
$this->setError(JText::_('JERROR_ALERTNOAUTHOR'));
return false;
}
// All's well, process the records
if ($return == true)
{
foreach (array($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 = JHtml::_('date',
$table->save_date, JText::_('DATE_FORMAT_LC6'));
$dateProperties = array (
'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 != '0000-00-00
00:00:00')
{
$object->data->$dateProperty->value =
JHtml::_('date', $object->data->$dateProperty->value,
JText::_('DATE_FORMAT_LC6'));
}
}
$result[] = $object;
}
return $result;
}
}
$this->setError(\JText::_('COM_CONTENTHISTORY_ERROR_VERSION_NOT_FOUND'));
return false;
}
/**
* Method to test whether a record is editable
*
* @param JTableContenthistory $record A JTable 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->ucm_type_id))
{
// Check that the type id matches the type alias
$typeAlias =
JFactory::getApplication()->input->get('type_alias');
/** @var JTableContenttype $contentTypeTable */
$contentTypeTable = JTable::getInstance('Contenttype',
'JTable');
if ($contentTypeTable->getTypeId($typeAlias) ==
$record->ucm_type_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 = JFactory::getUser();
$result = $user->authorise('core.edit', $typeAlias .
'.' . (int) $record->ucm_item_id);
}
// Finally try session (this catches edit.own case too)
if (!$result)
{
$contentTypeTable->load($record->ucm_type_id);
$typeEditables = (array)
JFactory::getApplication()->getUserState(str_replace('.',
'.edit.', $contentTypeTable->type_alias) . '.id');
$result = in_array((int) $record->ucm_item_id, $typeEditables);
}
}
return $result;
}
}
models/history.php000064400000024221151165257750010260 0ustar00<?php
/**
* @package Joomla.Administrator
* @subpackage com_contenthistory
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('_JEXEC') or die;
/**
* Methods supporting a list of contenthistory records.
*
* @since 3.2
*/
class ContenthistoryModelHistory extends JModelList
{
/**
* Constructor.
*
* @param array $config An optional associative array of configuration
settings.
*
* @see JControllerLegacy
* @since 3.2
*/
public function __construct($config = array())
{
if (empty($config['filter_fields']))
{
$config['filter_fields'] = array(
'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);
}
/**
* Method to test whether a record is editable
*
* @param JTableContenthistory $record A JTable 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)
{
$result = false;
if (!empty($record->ucm_type_id))
{
// Check that the type id matches the type alias
$typeAlias =
JFactory::getApplication()->input->get('type_alias');
/** @var JTableContenttype $contentTypeTable */
$contentTypeTable = JTable::getInstance('Contenttype',
'JTable');
if ($contentTypeTable->getTypeId($typeAlias) ==
$record->ucm_type_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 = JFactory::getUser();
$result = $user->authorise('core.edit', $typeAlias .
'.' . (int) $record->ucm_item_id);
}
// Finally try session (this catches edit.own case too)
if (!$result)
{
$contentTypeTable->load($record->ucm_type_id);
$typeEditables = (array)
JFactory::getApplication()->getUserState(str_replace('.',
'.edit.', $contentTypeTable->type_alias) . '.id');
$result = in_array((int) $record->ucm_item_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 JTableContenthistory $record A JTable 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
{
JLog::add($error, JLog::WARNING, 'jerror');
}
catch (RuntimeException $exception)
{
JFactory::getApplication()->enqueueMessage($error,
'warning');
}
return false;
}
else
{
try
{
JLog::add(JText::_('JLIB_APPLICATION_ERROR_DELETE_NOT_PERMITTED'),
JLog::WARNING, 'jerror');
}
catch (RuntimeException $exception)
{
JFactory::getApplication()->enqueueMessage(JText::_('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
*/
public function getItems()
{
$items = parent::getItems();
$user = JFactory::getUser();
if ($items === false)
{
return false;
}
// This should be an array with at least one element
if (!is_array($items) || !isset($items[0]))
{
return $items;
}
// Get the content type's record so we can check ACL
/** @var JTableContenttype $contentTypeTable */
$contentTypeTable = JTable::getInstance('Contenttype');
$ucmTypeId = $items[0]->ucm_type_id;
if (!$contentTypeTable->load($ucmTypeId))
{
// Assume a failure to load the content type means broken data, abort
mission
return false;
}
// Access check
if ($user->authorise('core.edit',
$contentTypeTable->type_alias . '.' . (int)
$items[0]->ucm_item_id) || $this->canEdit($items[0]))
{
return $items;
}
else
{
$this->setError(JText::_('JERROR_ALERTNOAUTHOR'));
return false;
}
}
/**
* 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 JTable A JTable object
*
* @since 3.2
*/
public function getTable($type = 'Contenthistory', $prefix =
'JTable', $config = array())
{
return JTable::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
{
JLog::add($error, JLog::WARNING, 'jerror');
}
catch (RuntimeException $exception)
{
JFactory::getApplication()->enqueueMessage($error,
'warning');
}
return false;
}
else
{
try
{
JLog::add(JText::_('COM_CONTENTHISTORY_ERROR_KEEP_NOT_PERMITTED'),
JLog::WARNING, 'jerror');
}
catch (RuntimeException $exception)
{
JFactory::getApplication()->enqueueMessage(JText::_('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 = JFactory::getApplication()->input;
$itemId = $input->get('item_id', 0, 'integer');
$typeId = $input->get('type_id', 0, 'integer');
$typeAlias = $input->get('type_alias', '',
'string');
$this->setState('item_id', $itemId);
$this->setState('type_id', $typeId);
$this->setState('type_alias', $typeAlias);
$this->setState('sha1_hash', $this->getSha1Hash());
// Load the parameters.
$params = JComponentHelper::getParams('com_contenthistory');
$this->setState('params', $params);
// List state information.
parent::populateState($ordering, $direction);
}
/**
* Build an SQL query to load the list data.
*
* @return JDatabaseQuery
*
* @since 3.2
*/
protected function getListQuery()
{
// Create a new query object.
$db = $this->getDbo();
$query = $db->getQuery(true);
// Select the required fields from the table.
$query->select(
$this->getState(
'list.select',
'h.version_id, h.ucm_item_id, h.ucm_type_id, h.version_note,
h.save_date, h.editor_user_id,' .
'h.character_count, h.sha1_hash, h.version_data,
h.keep_forever'
)
)
->from($db->quoteName('#__ucm_history') . ' AS
h')
->where($db->quoteName('h.ucm_item_id') . ' =
' . (int) $this->getState('item_id'))
->where($db->quoteName('h.ucm_type_id') . ' =
' . (int) $this->getState('type_id'))
// Join over the users for the editor
->select('uc.name AS editor')
->join('LEFT', '#__users AS uc ON uc.id =
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;
$typeTable = JTable::getInstance('Contenttype',
'JTable');
$typeId =
JFactory::getApplication()->input->getInteger('type_id',
0);
$typeTable->load($typeId);
$typeAliasArray = explode('.', $typeTable->type_alias);
JTable::addIncludePath(JPATH_ADMINISTRATOR . '/components/' .
$typeAliasArray[0] . '/tables');
$contentTable = $typeTable->getContentTable();
$keyValue =
JFactory::getApplication()->input->getInteger('item_id',
0);
if ($contentTable && $contentTable->load($keyValue))
{
$helper = new JHelper;
$dataObject = $helper->getDataObject($contentTable);
$result = $this->getTable('Contenthistory',
'JTable')->getSha1(json_encode($dataObject), $typeTable);
}
return $result;
}
}
models/preview.php000064400000007361151165257750010246 0ustar00<?php
/**
* @package Joomla.Administrator
* @subpackage com_contenthistory
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('_JEXEC') or die;
JLoader::register('ContenthistoryHelper', JPATH_ADMINISTRATOR .
'/components/com_contenthistory/helpers/contenthistory.php');
/**
* Methods supporting a list of contenthistory records.
*
* @since 3.2
*/
class ContenthistoryModelPreview extends JModelItem
{
/**
* Method to get a version history row.
*
* @return stdClass|boolean On success, standard object with row data.
False on failure.
*
* @since 3.2
*/
public function getItem()
{
/** @var JTableContenthistory $table */
$table = JTable::getInstance('Contenthistory');
$versionId =
JFactory::getApplication()->input->getInt('version_id');
if (!$table->load($versionId))
{
return false;
}
// Get the content type's record so we can check ACL
/** @var JTableContenttype $contentTypeTable */
$contentTypeTable = JTable::getInstance('Contenttype');
if (!$contentTypeTable->load($table->ucm_type_id))
{
// Assume a failure to load the content type means broken data, abort
mission
return false;
}
$user = JFactory::getUser();
// Access check
if ($user->authorise('core.edit',
$contentTypeTable->type_alias . '.' . (int)
$table->ucm_item_id) || $this->canEdit($table))
{
$return = true;
}
else
{
$this->setError(JText::_('JERROR_ALERTNOAUTHOR'));
return false;
}
// Good to go, finish processing the data
if ($return == true)
{
$result = new stdClass;
$result->version_note = $table->version_note;
$result->data = ContenthistoryHelper::prepareData($table);
// Let's use custom calendars when present
$result->save_date = JHtml::_('date', $table->save_date,
JText::_('DATE_FORMAT_LC6'));
$dateProperties = array (
'modified_time',
'created_time',
'modified',
'created',
'checked_out_time',
'publish_up',
'publish_down',
);
foreach ($dateProperties as $dateProperty)
{
if (property_exists($result->data, $dateProperty) &&
$result->data->$dateProperty->value != '0000-00-00
00:00:00')
{
$result->data->$dateProperty->value =
JHtml::_('date', $result->data->$dateProperty->value,
JText::_('DATE_FORMAT_LC6'));
}
}
return $result;
}
}
/**
* Method to test whether a record is editable
*
* @param JTableContenthistory $record A JTable 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->ucm_type_id))
{
// Check that the type id matches the type alias
$typeAlias =
JFactory::getApplication()->input->get('type_alias');
/** @var JTableContenttype $contentTypeTable */
$contentTypeTable = JTable::getInstance('Contenttype',
'JTable');
if ($contentTypeTable->getTypeId($typeAlias) ==
$record->ucm_type_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 = JFactory::getUser();
$result = $user->authorise('core.edit', $typeAlias .
'.' . (int) $record->ucm_item_id);
}
// Finally try session (this catches edit.own case too)
if (!$result)
{
$contentTypeTable->load($record->ucm_type_id);
$typeEditables = (array)
JFactory::getApplication()->getUserState(str_replace('.',
'.edit.', $contentTypeTable->type_alias) . '.id');
$result = in_array((int) $record->ucm_item_id, $typeEditables);
}
}
return $result;
}
}
views/compare/tmpl/compare.php000064400000011417151165257750012464
0ustar00<?php
/**
* @package Joomla.Administrator
* @subpackage com_contenthistory
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('_JEXEC') or die;
JSession::checkToken('get') or
die(JText::_('JINVALID_TOKEN'));
$version2 = $this->items[0];
$version1 = $this->items[1];
$object1 = $version1->data;
$object2 = $version2->data;
JHtml::addIncludePath(JPATH_COMPONENT_ADMINISTRATOR .
'/helpers/html');
JHtml::_('textdiff.textdiff', 'diff');
JFactory::getDocument()->addScriptDeclaration("
(function ($){
$(document).ready(function (){
jQuery('.diffhtml, .diffhtml-header').hide();
});
})(jQuery);
"
);
?>
<fieldset>
<legend>
<?php echo JText::sprintf('COM_CONTENTHISTORY_COMPARE_TITLE');
?>
<div class="btn-group pull-right">
<button id="toolbar-all-rows" class="btn
hasTooltip" title="<?php echo
JText::_('COM_CONTENTHISTORY_BUTTON_COMPARE_ALL_ROWS_DESC');
?>"
onclick="jQuery('.items-equal').show();
jQuery('#toolbar-all-rows').hide();
jQuery('#toolbar-changed-rows').show()"
style="display:none" >
<?php echo
JText::_('COM_CONTENTHISTORY_BUTTON_COMPARE_ALL_ROWS');
?></button>
<button id="toolbar-changed-rows" class="btn
hasTooltip" title="<?php echo
JText::_('COM_CONTENTHISTORY_BUTTON_COMPARE_CHANGED_ROWS_DESC');
?>"
onclick="jQuery('.items-equal').hide();
jQuery('#toolbar-all-rows').show();
jQuery('#toolbar-changed-rows').hide()">
<?php echo
JText::_('COM_CONTENTHISTORY_BUTTON_COMPARE_CHANGED_ROWS');
?></button>
<button class="diff-header btn hasTooltip"
title="<?php echo
JText::_('COM_CONTENTHISTORY_BUTTON_COMPARE_HTML_DESC');
?>"
onclick="jQuery('.diffhtml, .diffhtml-header').show();
jQuery('.diff, .diff-header').hide()">
<span class="icon-wrench"
aria-hidden="true"></span> <?php echo
JText::_('COM_CONTENTHISTORY_BUTTON_COMPARE_HTML');
?></button>
<button class="diffhtml-header btn hasTooltip"
title="<?php echo
JText::_('COM_CONTENTHISTORY_BUTTON_COMPARE_TEXT_DESC');
?>"
onclick="jQuery('.diffhtml, .diffhtml-header').hide();
jQuery('.diff, .diff-header').show()">
<span class="icon-pencil"
aria-hidden="true"></span> <?php echo
JText::_('COM_CONTENTHISTORY_BUTTON_COMPARE_TEXT');
?></button>
</div>
</legend>
<table id="diff" class="table table-striped
table-condensed">
<thead><tr>
<th width="25%"><?php echo
JText::_('COM_CONTENTHISTORY_PREVIEW_FIELD'); ?></th>
<th style="display:none" />
<th style="display:none" />
<th><?php echo
JText::sprintf('COM_CONTENTHISTORY_COMPARE_VALUE1',
$version1->save_date, $version1->version_note); ?></th>
<th><?php echo
JText::sprintf('COM_CONTENTHISTORY_COMPARE_VALUE2',
$version2->save_date, $version2->version_note); ?></th>
<th class="diff-header"><?php echo
JText::_('COM_CONTENTHISTORY_COMPARE_DIFF'); ?></th>
<th class="diffhtml-header"><?php echo
JText::_('COM_CONTENTHISTORY_COMPARE_DIFF'); ?></th>
</tr></thead>
<tbody>
<?php foreach ($object1 as $name => $value) : ?>
<?php $rowClass = ($value->value == $object2->$name->value) ?
'items-equal' : 'items-not-equal'; ?>
<tr class="<?php echo $rowClass; ?>">
<?php if (is_object($value->value)) : ?>
<td><strong><?php echo $value->label;
?></strong></td>
<td /><td /><td />
<?php foreach ($value->value as $subName => $subValue) : ?>
<?php $newSubValue =
isset($object2->$name->value->$subName->value) ?
$object2->$name->value->$subName->value : ''; ?>
<?php if ($subValue->value || $newSubValue) : ?>
<?php $rowClass = ($subValue->value == $newSubValue) ?
'items-equal' : 'items-not-equal'; ?>
<tr class="<?php echo $rowClass; ?>">
<td><i> <?php echo
$subValue->label; ?></i></td>
<td class="originalhtml" style="display:none"
><?php echo htmlspecialchars($subValue->value, ENT_COMPAT,
'UTF-8'); ?></td>
<td class="changedhtml" style="display:none"
><?php echo htmlspecialchars($newSubValue, ENT_COMPAT,
'UTF-8'); ?></td>
<td class="original"><?php echo $subValue->value;
?></td>
<td class="changed"><?php echo $newSubValue;
?></td>
<td class="diff" />
<td class="diffhtml" />
</tr>
<?php endif; ?>
<?php endforeach; ?>
<?php else : ?>
<td><strong><?php echo $value->label;
?></strong></td>
<td class="originalhtml" style="display:none"
><?php echo htmlspecialchars($value->value); ?></td>
<?php $object2->$name->value =
is_object($object2->$name->value) ?
json_encode($object2->$name->value) : $object2->$name->value;
?>
<td class="changedhtml" style="display:none"
><?php echo htmlspecialchars($object2->$name->value,
ENT_COMPAT, 'UTF-8'); ?></td>
<td class="original"><?php echo $value->value;
?></td>
<td class="changed"><?php echo
$object2->$name->value; ?></td>
<td class="diff" />
<td class="diffhtml" />
<?php endif; ?>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</fieldset>
views/compare/view.html.php000064400000001637151165257750012002
0ustar00<?php
/**
* @package Joomla.Administrator
* @subpackage com_contenthistory
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('_JEXEC') or die;
/**
* View class for a list of contenthistory.
*
* @since 3.2
*/
class ContenthistoryViewCompare extends JViewLegacy
{
protected $items;
protected $state;
/**
* Method to display the view.
*
* @param string $tpl A template file to load. [optional]
*
* @return mixed Exception on failure, void on success.
*
* @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 Exception(implode("\n", $errors), 500);
}
return parent::display($tpl);
}
}
views/history/tmpl/modal.php000064400000022255151165257750012207
0ustar00<?php
/**
* @package Joomla.Administrator
* @subpackage com_contenthistory
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('_JEXEC') or die;
JSession::checkToken('get') or
die(JText::_('JINVALID_TOKEN'));
JHtml::addIncludePath(JPATH_COMPONENT . '/helpers/html');
JHtml::_('bootstrap.tooltip', '.hasTooltip',
array('placement' => 'bottom'));
JHtml::_('behavior.multiselect');
JHtml::_('jquery.framework');
$input = JFactory::getApplication()->input;
$field = $input->getCmd('field');
$function = 'jSelectContenthistory_' . $field;
$listOrder =
$this->escape($this->state->get('list.ordering'));
$listDirn =
$this->escape($this->state->get('list.direction'));
$message = JText::_('COM_CONTENTHISTORY_BUTTON_SELECT_ONE',
true);
$compareMessage =
JText::_('COM_CONTENTHISTORY_BUTTON_SELECT_TWO', true);
JText::script('JLIB_HTML_PLEASE_MAKE_A_SELECTION_FROM_THE_LIST');
$deleteMessage =
"alert(Joomla.JText._('JLIB_HTML_PLEASE_MAKE_A_SELECTION_FROM_THE_LIST'));";
$aliasArray = explode('.', $this->state->type_alias);
$option = (end($aliasArray) == 'category') ?
'com_categories&extension=' . implode('.',
array_slice($aliasArray, 0, count($aliasArray) - 1)) : $aliasArray[0];
$filter = JFilterInput::getInstance();
$task = $filter->clean(end($aliasArray)) . '.loadhistory';
$loadUrl = JRoute::_('index.php?option=' .
$filter->clean($option) . '&task=' . $task);
$deleteUrl =
JRoute::_('index.php?option=com_contenthistory&task=history.delete');
$hash = $this->state->get('sha1_hash');
$formUrl =
'index.php?option=com_contenthistory&view=history&layout=modal&tmpl=component&item_id='
. $this->state->get('item_id') . '&type_id='
. $this->state->get('type_id') .
'&type_alias=' .
$this->state->get('type_alias') . '&' .
JSession::getFormToken() . '=1';
JFactory::getDocument()->addScriptDeclaration("
(function ($){
$(document).ready(function (){
$('#toolbar-load').click(function() {
var ids = $('input[id*=\'cb\']:checked');
if (ids.length == 1) {
// Add version item id to URL
var url = $('#toolbar-load').attr('data-url') +
'&version_id=' + ids[0].value;
$('#content-url').attr('data-url', url);
if (window.parent) {
window.parent.location = url;
}
} else {
alert('" . $message . "');
}
});
$('#toolbar-preview').click(function() {
var windowSizeArray = ['width=800, height=600, resizable=yes,
scrollbars=yes'];
var ids = $('input[id*=\'cb\']:checked');
if (ids.length == 1) {
// Add version item id to URL
var url = $('#toolbar-preview').attr('data-url') +
'&version_id=' + ids[0].value;
$('#content-url').attr('data-url', url);
if (window.parent) {
window.open(url, '', windowSizeArray);
return false;
}
} else {
alert('" . $message . "');
}
});
$('#toolbar-compare').click(function() {
var windowSizeArray = ['width=1000, height=600, resizable=yes,
scrollbars=yes'];
var ids = $('input[id*=\'cb\']:checked');
if (ids.length == 2) {
// Add version item ids to URL
var url = $('#toolbar-compare').attr('data-url') +
'&id1=' + ids[0].value + '&id2=' +
ids[1].value;
$('#content-url').attr('data-url', url);
if (window.parent) {
window.open(url, '', windowSizeArray);
return false;
}
} else {
alert('" . $compareMessage . "');
}
});
});
})(jQuery);
"
);
?>
<div class="container-popup">
<div class="btn-group pull-right">
<button id="toolbar-load" type="submit"
class="btn hasTooltip" aria-label="<?php echo
JText::_('COM_CONTENTHISTORY_BUTTON_LOAD_DESC'); ?>"
title="<?php echo
JText::_('COM_CONTENTHISTORY_BUTTON_LOAD_DESC'); ?>"
data-url="<?php echo JRoute::_($loadUrl); ?>">
<span class="icon-upload"
aria-hidden="true"></span><span
class="hidden-phone"><?php echo
JText::_('COM_CONTENTHISTORY_BUTTON_LOAD');
?></span></button>
<button id="toolbar-preview" type="button"
class="btn hasTooltip" aria-label="<?php echo
JText::_('COM_CONTENTHISTORY_BUTTON_PREVIEW_DESC'); ?>"
title="<?php echo
JText::_('COM_CONTENTHISTORY_BUTTON_PREVIEW_DESC'); ?>"
data-url="<?php echo
JRoute::_('index.php?option=com_contenthistory&view=preview&layout=preview&tmpl=component&'
. JSession::getFormToken() . '=1'); ?>">
<span class="icon-search"
aria-hidden="true"></span><span
class="hidden-phone"><?php echo
JText::_('COM_CONTENTHISTORY_BUTTON_PREVIEW');
?></span></button>
<button id="toolbar-compare" type="button"
class="btn hasTooltip" aria-label="<?php echo
JText::_('COM_CONTENTHISTORY_BUTTON_COMPARE_DESC'); ?>"
title="<?php echo
JText::_('COM_CONTENTHISTORY_BUTTON_COMPARE_DESC'); ?>"
data-url="<?php echo
JRoute::_('index.php?option=com_contenthistory&view=compare&layout=compare&tmpl=component&'
. JSession::getFormToken() . '=1'); ?>">
<span class="icon-zoom-in"
aria-hidden="true"></span><span
class="hidden-phone"><?php echo
JText::_('COM_CONTENTHISTORY_BUTTON_COMPARE');
?></span></button>
<button onclick="if
(document.adminForm.boxchecked.value==0){<?php echo $deleteMessage;
?>}else{ Joomla.submitbutton('history.keep')}"
class="btn pointer hasTooltip" aria-label="<?php echo
JText::_('COM_CONTENTHISTORY_BUTTON_KEEP_DESC'); ?>"
title="<?php echo
JText::_('COM_CONTENTHISTORY_BUTTON_KEEP_DESC'); ?>">
<span class="icon-lock"
aria-hidden="true"></span><span
class="hidden-phone"><?php echo
JText::_('COM_CONTENTHISTORY_BUTTON_KEEP');
?></span></button>
<button onclick="if
(document.adminForm.boxchecked.value==0){<?php echo $deleteMessage;
?>}else{ Joomla.submitbutton('history.delete')}"
class="btn pointer hasTooltip" aria-label="<?php echo
JText::_('COM_CONTENTHISTORY_BUTTON_DELETE_DESC'); ?>"
title="<?php echo
JText::_('COM_CONTENTHISTORY_BUTTON_DELETE_DESC');
?>">
<span class="icon-delete"
aria-hidden="true"></span><span
class="hidden-phone"><?php echo
JText::_('COM_CONTENTHISTORY_BUTTON_DELETE');
?></span></button>
</div>
<div class="clearfix"></div>
<hr class="hr-condensed" />
<form action="<?php echo JRoute::_($formUrl); ?>"
method="post" name="adminForm"
id="adminForm">
<table class="table table-striped table-condensed">
<thead>
<tr>
<th width="1%" class="center">
<input type="checkbox" name="checkall-toggle"
value="" title="<?php echo
JText::_('JGLOBAL_CHECK_ALL'); ?>"
onclick="Joomla.checkAll(this)" />
</th>
<th width="15%">
<?php echo JText::_('JDATE'); ?>
</th>
<th width="15%" class="nowrap hidden-phone">
<?php echo JText::_('COM_CONTENTHISTORY_VERSION_NOTE');
?>
</th>
<th width="10%" class="nowrap">
<?php echo JText::_('COM_CONTENTHISTORY_KEEP_VERSION');
?>
</th>
<th width="15%" class="nowrap hidden-phone">
<?php echo JText::_('JAUTHOR'); ?>
</th>
<th width="10%" class="nowrap center">
<?php echo
JText::_('COM_CONTENTHISTORY_CHARACTER_COUNT'); ?>
</th>
</tr>
</thead>
<tfoot>
<tr>
<td colspan="15">
<?php echo $this->pagination->getListFooter(); ?>
</td>
</tr>
</tfoot>
<tbody>
<?php $i = 0; ?>
<?php foreach ($this->items as $item) : ?>
<tr class="row<?php echo $i % 2; ?>">
<td class="center">
<?php echo JHtml::_('grid.id', $i,
$item->version_id); ?>
</td>
<td>
<a class="save-date"
onclick="window.open(this.href,'win2','width=800,height=600,resizable=yes,scrollbars=yes');
return false;"
href="<?php echo
JRoute::_('index.php?option=com_contenthistory&view=preview&layout=preview&tmpl=component&'
. JSession::getFormToken() . '=1&version_id=' .
$item->version_id); ?>">
<?php echo JHtml::_('date', $item->save_date,
JText::_('DATE_FORMAT_LC6')); ?>
</a>
<?php if ($item->sha1_hash == $hash) : ?>
<span class="icon-featured"
aria-hidden="true"><span
class="element-invisible"><?php echo
JText::_('JFEATURED'); ?></span></span>
<?php endif; ?>
</td>
<td class="hidden-phone">
<?php echo htmlspecialchars($item->version_note); ?>
</td>
<td>
<?php if ($item->keep_forever) : ?>
<a class="btn btn-mini active" rel="tooltip"
href="javascript:void(0);"
onclick="return listItemTask('cb<?php echo $i;
?>','history.keep')"
data-original-title="<?php echo
JText::_('COM_CONTENTHISTORY_BUTTON_KEEP_TOGGLE_OFF');
?>">
<?php echo JText::_('JYES'); ?> <span
class="icon-lock" aria-hidden="true"></span>
</a>
<?php else : ?>
<a class="btn btn-mini active" rel="tooltip"
href="javascript:void(0);"
onclick="return listItemTask('cb<?php echo $i;
?>','history.keep')"
data-original-title="<?php echo
JText::_('COM_CONTENTHISTORY_BUTTON_KEEP_TOGGLE_ON');
?>">
<?php echo JText::_('JNO'); ?>
</a>
<?php endif; ?>
</td>
<td class="hidden-phone">
<?php echo htmlspecialchars($item->editor); ?>
</td>
<td class="center">
<?php echo number_format((int) $item->character_count, 0,
JText::_('DECIMALS_SEPARATOR'),
JText::_('THOUSANDS_SEPARATOR')); ?>
</td>
</tr>
<?php $i++; ?>
<?php endforeach; ?>
</tbody>
</table>
<input type="hidden" name="task"
value="" />
<input type="hidden" name="boxchecked"
value="0" />
<?php echo JHtml::_('form.token'); ?>
</form>
</div>
views/history/view.html.php000064400000001750151165257750012051
0ustar00<?php
/**
* @package Joomla.Administrator
* @subpackage com_contenthistory
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('_JEXEC') or die;
/**
* View class for a list of contenthistory.
*
* @since 3.2
*/
class ContenthistoryViewHistory extends JViewLegacy
{
protected $items;
protected $pagination;
protected $state;
/**
* Method to display the view.
*
* @param string $tpl A template file to load. [optional]
*
* @return mixed Exception on failure, void on success.
*
* @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 Exception(implode("\n", $errors), 500);
}
return parent::display($tpl);
}
}
views/preview/tmpl/preview.php000064400000002674151165257750012557
0ustar00<?php
/**
* @package Joomla.Administrator
* @subpackage com_contenthistory
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('_JEXEC') or die;
JSession::checkToken('get') or
die(JText::_('JINVALID_TOKEN'));
?>
<h3>
<?php echo
JText::sprintf('COM_CONTENTHISTORY_PREVIEW_SUBTITLE_DATE',
$this->item->save_date); ?>
<?php if ($this->item->version_note) : ?>
<?php echo
JText::sprintf('COM_CONTENTHISTORY_PREVIEW_SUBTITLE',
$this->item->version_note); ?>
<?php endif; ?>
</h3>
<table class="table table-striped" >
<thead><tr>
<th width="25%"><?php echo
JText::_('COM_CONTENTHISTORY_PREVIEW_FIELD'); ?></th>
<th><?php echo
JText::_('COM_CONTENTHISTORY_PREVIEW_VALUE'); ?></th>
</tr></thead>
<tbody>
<?php foreach ($this->item->data as $name => $value) : ?>
<tr>
<?php if (is_object($value->value)) : ?>
<td><strong><?php echo $value->label;
?></strong></td>
<td></td><tr>
<?php foreach ($value->value as $subName => $subValue) : ?>
<?php if ($subValue) : ?>
<tr>
<td><i> <?php echo
$subValue->label; ?></i></td>
<td><?php echo $subValue->value; ?></td>
</tr>
<?php endif; ?>
<?php endforeach; ?>
<?php else : ?>
<td><strong><?php echo $value->label;
?></strong></td>
<td><?php echo $value->value; ?></td>
<?php endif; ?>
</tr>
<?php endforeach; ?>
</tbody>
</table>
views/preview/view.html.php000064400000002155151165257750012031
0ustar00<?php
/**
* @package Joomla.Administrator
* @subpackage com_contenthistory
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('_JEXEC') or die;
/**
* View class for a list of contenthistory.
*
* @since 1.5
*/
class ContenthistoryViewPreview extends JViewLegacy
{
protected $items;
protected $state;
/**
* Method to display the view.
*
* @param string $tpl A template file to load. [optional]
*
* @return mixed Exception on failure, void on success.
*
* @since 3.2
*/
public function display($tpl = null)
{
$this->state = $this->get('State');
$this->item = $this->get('Item');
if (false === $this->item)
{
JFactory::getLanguage()->load('com_content', JPATH_SITE,
null, true);
JError::raiseError(404,
JText::_('COM_CONTENT_ERROR_ARTICLE_NOT_FOUND'));
return false;
}
// Check for errors.
if (count($errors = $this->get('Errors')))
{
throw new Exception(implode("\n", $errors), 500);
}
return parent::display($tpl);
}
}
src/Dispatcher/Dispatcher.php000064400000004044151165413700012225
0ustar00<?php
/**
* @package Joomla.Site
* @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\Site\Dispatcher;
use Joomla\CMS\Access\Exception\NotAllowed;
use Joomla\CMS\Dispatcher\ComponentDispatcher;
use Joomla\CMS\MVC\Controller\BaseController;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* ComponentDispatcher class for com_contenthistory
*
* @since 4.0.0
*/
class Dispatcher extends ComponentDispatcher
{
/**
* Load the language
*
* @since 4.0.0
*
* @return void
*/
protected function loadLanguage()
{
// Load common and local language files.
$this->app->getLanguage()->load($this->option,
JPATH_ADMINISTRATOR) ||
$this->app->getLanguage()->load($this->option,
JPATH_SITE);
}
/**
* 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);
}
}
/**
* Get a controller from the component
*
* @param string $name Controller name
* @param string $client Optional client (like Administrator, Site
etc.)
* @param array $config Optional controller config
*
* @return BaseController
*
* @since 4.0.0
*/
public function getController(string $name, string $client =
'', array $config = []): BaseController
{
$config['base_path'] = JPATH_COMPONENT_ADMINISTRATOR;
$client = 'Administrator';
return parent::getController($name, $client, $config);
}
}
src/Controller/DisplayController.php000064400000002455151165413700013651
0ustar00<?php
/**
* @package Joomla.Site
* @subpackage com_contenthistory
*
* @copyright (C) 2018 Open Source Matters, Inc.
<https://www.joomla.org>
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
namespace Joomla\Component\Contenthistory\Site\Controller;
use Joomla\CMS\Application\CMSApplication;
use Joomla\CMS\Input\Input;
use Joomla\CMS\MVC\Controller\BaseController;
use Joomla\CMS\MVC\Factory\MVCFactoryInterface;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* History manager display controller.
*
* @since 4.0.0
*/
class DisplayController extends BaseController
{
/**
* @param array $config An optional associative
array of configuration settings.
* @param MVCFactoryInterface|null $factory The factory.
* @param CMSApplication|null $app The Application for the
dispatcher
* @param ?Input $input The Input object for
the request
*
* @since 3.0
*/
public function __construct($config = [], MVCFactoryInterface $factory
= null, $app = null, $input = null)
{
$config['base_path'] = JPATH_COMPONENT_ADMINISTRATOR;
parent::__construct($config, $factory, $app, $input);
}
}
css/jquery.pretty-text-diff.css000064400000000214151165463030012604
0ustar00/* colors for pretty-text-diff*/
ins {
background-color: #c6ffc6;
text-decoration: none;
}
del {
background-color: #ffc6c6;
}
js/admin-history-versions.js000064400000000461151165463030012160
0ustar00/**
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
window.addEventListener('DOMContentLoaded', function() {
document.body.appendChild(document.getElementById('versionsModal'));
});
js/diff_match_patch.js000064400000045370151165463030010776
0ustar00(function(){function
diff_match_patch(){this.Diff_Timeout=1;this.Diff_EditCost=4;this.Match_Threshold=0.5;this.Match_Distance=1E3;this.Patch_DeleteThreshold=0.5;this.Patch_Margin=4;this.Match_MaxBits=32}
diff_match_patch.prototype.diff_main=function(a,b,c,d){"undefined"==typeof
d&&(d=0>=this.Diff_Timeout?Number.MAX_VALUE:(new
Date).getTime()+1E3*this.Diff_Timeout);if(null==a||null==b)throw
Error("Null input. (diff_main)");if(a==b)return
a?[[0,a]]:[];"undefined"==typeof c&&(c=!0);var
e=c,f=this.diff_commonPrefix(a,b);c=a.substring(0,f);a=a.substring(f);b=b.substring(f);var
f=this.diff_commonSuffix(a,b),g=a.substring(a.length-f);a=a.substring(0,a.length-f);b=b.substring(0,b.length-f);a=this.diff_compute_(a,
b,e,d);c&&a.unshift([0,c]);g&&a.push([0,g]);this.diff_cleanupMerge(a);return
a};
diff_match_patch.prototype.diff_compute_=function(a,b,c,d){if(!a)return[[1,b]];if(!b)return[[-1,a]];var
e=a.length>b.length?a:b,f=a.length>b.length?b:a,g=e.indexOf(f);return-1!=g?(c=[[1,e.substring(0,g)],[0,f],[1,e.substring(g+f.length)]],a.length>b.length&&(c[0][0]=c[2][0]=-1),c):1==f.length?[[-1,a],[1,b]]:(e=this.diff_halfMatch_(a,b))?(f=e[0],a=e[1],g=e[2],b=e[3],e=e[4],f=this.diff_main(f,g,c,d),c=this.diff_main(a,b,c,d),f.concat([[0,e]],c)):c&&100<a.length&&100<b.length?this.diff_lineMode_(a,b,
d):this.diff_bisect_(a,b,d)};
diff_match_patch.prototype.diff_lineMode_=function(a,b,c){var
d=this.diff_linesToChars_(a,b);a=d.chars1;b=d.chars2;d=d.lineArray;a=this.diff_main(a,b,!1,c);this.diff_charsToLines_(a,d);this.diff_cleanupSemantic(a);a.push([0,""]);for(var
e=d=b=0,f="",g="";b<a.length;){switch(a[b][0]){case
1:e++;g+=a[b][1];break;case -1:d++;f+=a[b][1];break;case
0:if(1<=d&&1<=e){a.splice(b-d-e,d+e);b=b-d-e;d=this.diff_main(f,g,!1,c);for(e=d.length-1;0<=e;e--)a.splice(b,0,d[e]);b+=d.length}d=e=0;g=f=""}b++}a.pop();return
a};
diff_match_patch.prototype.diff_bisect_=function(a,b,c){for(var
d=a.length,e=b.length,f=Math.ceil((d+e)/2),g=f,h=2*f,j=Array(h),i=Array(h),k=0;k<h;k++)j[k]=-1,i[k]=-1;j[g+1]=0;i[g+1]=0;for(var
k=d-e,q=0!=k%2,r=0,t=0,p=0,w=0,v=0;v<f&&!((new
Date).getTime()>c);v++){for(var n=-v+r;n<=v-t;n+=2){var
l=g+n,m;m=n==-v||n!=v&&j[l-1]<j[l+1]?j[l+1]:j[l-1]+1;for(var
s=m-n;m<d&&s<e&&a.charAt(m)==b.charAt(s);)m++,s++;j[l]=m;if(m>d)t+=2;else
if(s>e)r+=2;else
if(q&&(l=g+k-n,0<=l&&l<h&&-1!=i[l])){var
u=d-i[l];if(m>=
u)return
this.diff_bisectSplit_(a,b,m,s,c)}}for(n=-v+p;n<=v-w;n+=2){l=g+n;u=n==-v||n!=v&&i[l-1]<i[l+1]?i[l+1]:i[l-1]+1;for(m=u-n;u<d&&m<e&&a.charAt(d-u-1)==b.charAt(e-m-1);)u++,m++;i[l]=u;if(u>d)w+=2;else
if(m>e)p+=2;else
if(!q&&(l=g+k-n,0<=l&&(l<h&&-1!=j[l])&&(m=j[l],s=g+m-l,u=d-u,m>=u)))return
this.diff_bisectSplit_(a,b,m,s,c)}}return[[-1,a],[1,b]]};
diff_match_patch.prototype.diff_bisectSplit_=function(a,b,c,d,e){var
f=a.substring(0,c),g=b.substring(0,d);a=a.substring(c);b=b.substring(d);f=this.diff_main(f,g,!1,e);e=this.diff_main(a,b,!1,e);return
f.concat(e)};
diff_match_patch.prototype.diff_linesToChars_=function(a,b){function
c(a){for(var
b="",c=0,f=-1,g=d.length;f<a.length-1;){f=a.indexOf("\n",c);-1==f&&(f=a.length-1);var
r=a.substring(c,f+1),c=f+1;(e.hasOwnProperty?e.hasOwnProperty(r):void
0!==e[r])?b+=String.fromCharCode(e[r]):(b+=String.fromCharCode(g),e[r]=g,d[g++]=r)}return
b}var d=[],e={};d[0]="";var
f=c(a),g=c(b);return{chars1:f,chars2:g,lineArray:d}};
diff_match_patch.prototype.diff_charsToLines_=function(a,b){for(var
c=0;c<a.length;c++){for(var
d=a[c][1],e=[],f=0;f<d.length;f++)e[f]=b[d.charCodeAt(f)];a[c][1]=e.join("")}};diff_match_patch.prototype.diff_commonPrefix=function(a,b){if(!a||!b||a.charAt(0)!=b.charAt(0))return
0;for(var
c=0,d=Math.min(a.length,b.length),e=d,f=0;c<e;)a.substring(f,e)==b.substring(f,e)?f=c=e:d=e,e=Math.floor((d-c)/2+c);return
e};
diff_match_patch.prototype.diff_commonSuffix=function(a,b){if(!a||!b||a.charAt(a.length-1)!=b.charAt(b.length-1))return
0;for(var
c=0,d=Math.min(a.length,b.length),e=d,f=0;c<e;)a.substring(a.length-e,a.length-f)==b.substring(b.length-e,b.length-f)?f=c=e:d=e,e=Math.floor((d-c)/2+c);return
e};
diff_match_patch.prototype.diff_commonOverlap_=function(a,b){var
c=a.length,d=b.length;if(0==c||0==d)return
0;c>d?a=a.substring(c-d):c<d&&(b=b.substring(0,c));c=Math.min(c,d);if(a==b)return
c;for(var d=0,e=1;;){var f=a.substring(c-e),f=b.indexOf(f);if(-1==f)return
d;e+=f;if(0==f||a.substring(c-e)==b.substring(0,e))d=e,e++}};
diff_match_patch.prototype.diff_halfMatch_=function(a,b){function
c(a,b,c){for(var
d=a.substring(c,c+Math.floor(a.length/4)),e=-1,g="",h,j,n,l;-1!=(e=b.indexOf(d,e+1));){var
m=f.diff_commonPrefix(a.substring(c),b.substring(e)),s=f.diff_commonSuffix(a.substring(0,c),b.substring(0,e));g.length<s+m&&(g=b.substring(e-s,e)+b.substring(e,e+m),h=a.substring(0,c-s),j=a.substring(c+m),n=b.substring(0,e-s),l=b.substring(e+m))}return
2*g.length>=a.length?[h,j,n,l,g]:null}if(0>=this.Diff_Timeout)return
null;
var
d=a.length>b.length?a:b,e=a.length>b.length?b:a;if(4>d.length||2*e.length<d.length)return
null;var
f=this,g=c(d,e,Math.ceil(d.length/4)),d=c(d,e,Math.ceil(d.length/2)),h;if(!g&&!d)return
null;h=d?g?g[4].length>d[4].length?g:d:d:g;var
j;a.length>b.length?(g=h[0],d=h[1],e=h[2],j=h[3]):(e=h[0],j=h[1],g=h[2],d=h[3]);h=h[4];return[g,d,e,j,h]};
diff_match_patch.prototype.diff_cleanupSemantic=function(a){for(var
b=!1,c=[],d=0,e=null,f=0,g=0,h=0,j=0,i=0;f<a.length;)0==a[f][0]?(c[d++]=f,g=j,h=i,i=j=0,e=a[f][1]):(1==a[f][0]?j+=a[f][1].length:i+=a[f][1].length,e&&(e.length<=Math.max(g,h)&&e.length<=Math.max(j,i))&&(a.splice(c[d-1],0,[-1,e]),a[c[d-1]+1][0]=1,d--,d--,f=0<d?c[d-1]:-1,i=j=h=g=0,e=null,b=!0)),f++;b&&this.diff_cleanupMerge(a);this.diff_cleanupSemanticLossless(a);for(f=1;f<a.length;){if(-1==a[f-1][0]&&1==a[f][0]){b=a[f-1][1];c=a[f][1];
d=this.diff_commonOverlap_(b,c);e=this.diff_commonOverlap_(c,b);if(d>=e){if(d>=b.length/2||d>=c.length/2)a.splice(f,0,[0,c.substring(0,d)]),a[f-1][1]=b.substring(0,b.length-d),a[f+1][1]=c.substring(d),f++}else
if(e>=b.length/2||e>=c.length/2)a.splice(f,0,[0,b.substring(0,e)]),a[f-1][0]=1,a[f-1][1]=c.substring(0,c.length-e),a[f+1][0]=-1,a[f+1][1]=b.substring(e),f++;f++}f++}};
diff_match_patch.prototype.diff_cleanupSemanticLossless=function(a){function
b(a,b){if(!a||!b)return 6;var
c=a.charAt(a.length-1),d=b.charAt(0),e=c.match(diff_match_patch.nonAlphaNumericRegex_),f=d.match(diff_match_patch.nonAlphaNumericRegex_),g=e&&c.match(diff_match_patch.whitespaceRegex_),h=f&&d.match(diff_match_patch.whitespaceRegex_),c=g&&c.match(diff_match_patch.linebreakRegex_),d=h&&d.match(diff_match_patch.linebreakRegex_),i=c&&a.match(diff_match_patch.blanklineEndRegex_),j=d&&b.match(diff_match_patch.blanklineStartRegex_);
return i||j?5:c||d?4:e&&!g&&h?3:g||h?2:e||f?1:0}for(var
c=1;c<a.length-1;){if(0==a[c-1][0]&&0==a[c+1][0]){var
d=a[c-1][1],e=a[c][1],f=a[c+1][1],g=this.diff_commonSuffix(d,e);if(g)var
h=e.substring(e.length-g),d=d.substring(0,d.length-g),e=h+e.substring(0,e.length-g),f=h+f;for(var
g=d,h=e,j=f,i=b(d,e)+b(e,f);e.charAt(0)===f.charAt(0);){var
d=d+e.charAt(0),e=e.substring(1)+f.charAt(0),f=f.substring(1),k=b(d,e)+b(e,f);k>=i&&(i=k,g=d,h=e,j=f)}a[c-1][1]!=g&&(g?a[c-1][1]=g:(a.splice(c-1,1),c--),a[c][1]=
h,j?a[c+1][1]=j:(a.splice(c+1,1),c--))}c++}};diff_match_patch.nonAlphaNumericRegex_=/[^a-zA-Z0-9]/;diff_match_patch.whitespaceRegex_=/\s/;diff_match_patch.linebreakRegex_=/[\r\n]/;diff_match_patch.blanklineEndRegex_=/\n\r?\n$/;diff_match_patch.blanklineStartRegex_=/^\r?\n\r?\n/;
diff_match_patch.prototype.diff_cleanupEfficiency=function(a){for(var
b=!1,c=[],d=0,e=null,f=0,g=!1,h=!1,j=!1,i=!1;f<a.length;){if(0==a[f][0])a[f][1].length<this.Diff_EditCost&&(j||i)?(c[d++]=f,g=j,h=i,e=a[f][1]):(d=0,e=null),j=i=!1;else
if(-1==a[f][0]?i=!0:j=!0,e&&(g&&h&&j&&i||e.length<this.Diff_EditCost/2&&3==g+h+j+i))a.splice(c[d-1],0,[-1,e]),a[c[d-1]+1][0]=1,d--,e=null,g&&h?(j=i=!0,d=0):(d--,f=0<d?c[d-1]:-1,j=i=!1),b=!0;f++}b&&this.diff_cleanupMerge(a)};
diff_match_patch.prototype.diff_cleanupMerge=function(a){a.push([0,""]);for(var
b=0,c=0,d=0,e="",f="",g;b<a.length;)switch(a[b][0]){case
1:d++;f+=a[b][1];b++;break;case -1:c++;e+=a[b][1];b++;break;case
0:1<c+d?(0!==c&&0!==d&&(g=this.diff_commonPrefix(f,e),0!==g&&(0<b-c-d&&0==a[b-c-d-1][0]?a[b-c-d-1][1]+=f.substring(0,g):(a.splice(0,0,[0,f.substring(0,g)]),b++),f=f.substring(g),e=e.substring(g)),g=this.diff_commonSuffix(f,e),0!==g&&(a[b][1]=f.substring(f.length-g)+a[b][1],f=f.substring(0,f.length-
g),e=e.substring(0,e.length-g))),0===c?a.splice(b-d,c+d,[1,f]):0===d?a.splice(b-c,c+d,[-1,e]):a.splice(b-c-d,c+d,[-1,e],[1,f]),b=b-c-d+(c?1:0)+(d?1:0)+1):0!==b&&0==a[b-1][0]?(a[b-1][1]+=a[b][1],a.splice(b,1)):b++,c=d=0,f=e=""}""===a[a.length-1][1]&&a.pop();c=!1;for(b=1;b<a.length-1;)0==a[b-1][0]&&0==a[b+1][0]&&(a[b][1].substring(a[b][1].length-a[b-1][1].length)==a[b-1][1]?(a[b][1]=a[b-1][1]+a[b][1].substring(0,a[b][1].length-a[b-1][1].length),a[b+1][1]=a[b-1][1]+a[b+1][1],a.splice(b-1,1),c=!0):a[b][1].substring(0,
a[b+1][1].length)==a[b+1][1]&&(a[b-1][1]+=a[b+1][1],a[b][1]=a[b][1].substring(a[b+1][1].length)+a[b+1][1],a.splice(b+1,1),c=!0)),b++;c&&this.diff_cleanupMerge(a)};diff_match_patch.prototype.diff_xIndex=function(a,b){var
c=0,d=0,e=0,f=0,g;for(g=0;g<a.length;g++){1!==a[g][0]&&(c+=a[g][1].length);-1!==a[g][0]&&(d+=a[g][1].length);if(c>b)break;e=c;f=d}return
a.length!=g&&-1===a[g][0]?f:f+(b-e)};
diff_match_patch.prototype.diff_prettyHtml=function(a){for(var
b=[],c=/&/g,d=/</g,e=/>/g,f=/\n/g,g=0;g<a.length;g++){var
h=a[g][0],j=a[g][1],j=j.replace(c,"&").replace(d,"<").replace(e,">").replace(f,"¶<br>");switch(h){case
1:b[g]='<ins
style="background:#e6ffe6;">'+j+"</ins>";break;case
-1:b[g]='<del
style="background:#ffe6e6;">'+j+"</del>";break;case
0:b[g]="<span>"+j+"</span>"}}return
b.join("")};
diff_match_patch.prototype.diff_text1=function(a){for(var
b=[],c=0;c<a.length;c++)1!==a[c][0]&&(b[c]=a[c][1]);return
b.join("")};diff_match_patch.prototype.diff_text2=function(a){for(var
b=[],c=0;c<a.length;c++)-1!==a[c][0]&&(b[c]=a[c][1]);return
b.join("")};diff_match_patch.prototype.diff_levenshtein=function(a){for(var
b=0,c=0,d=0,e=0;e<a.length;e++){var f=a[e][0],g=a[e][1];switch(f){case
1:c+=g.length;break;case -1:d+=g.length;break;case
0:b+=Math.max(c,d),d=c=0}}return b+=Math.max(c,d)};
diff_match_patch.prototype.diff_toDelta=function(a){for(var
b=[],c=0;c<a.length;c++)switch(a[c][0]){case
1:b[c]="+"+encodeURI(a[c][1]);break;case
-1:b[c]="-"+a[c][1].length;break;case
0:b[c]="="+a[c][1].length}return
b.join("\t").replace(/%20/g," ")};
diff_match_patch.prototype.diff_fromDelta=function(a,b){for(var
c=[],d=0,e=0,f=b.split(/\t/g),g=0;g<f.length;g++){var
h=f[g].substring(1);switch(f[g].charAt(0)){case
"+":try{c[d++]=[1,decodeURI(h)]}catch(j){throw
Error("Illegal escape in diff_fromDelta: "+h);}break;case
"-":case "=":var
i=parseInt(h,10);if(isNaN(i)||0>i)throw Error("Invalid number in
diff_fromDelta:
"+h);h=a.substring(e,e+=i);"="==f[g].charAt(0)?c[d++]=[0,h]:c[d++]=[-1,h];break;default:if(f[g])throw
Error("Invalid diff operation in diff_fromDelta: "+
f[g]);}}if(e!=a.length)throw Error("Delta length ("+e+")
does not equal source text length ("+a.length+").");return
c};diff_match_patch.prototype.match_main=function(a,b,c){if(null==a||null==b||null==c)throw
Error("Null input.
(match_main)");c=Math.max(0,Math.min(c,a.length));return
a==b?0:a.length?a.substring(c,c+b.length)==b?c:this.match_bitap_(a,b,c):-1};
diff_match_patch.prototype.match_bitap_=function(a,b,c){function d(a,d){var
e=a/b.length,g=Math.abs(c-d);return!f.Match_Distance?g?1:e:e+g/f.Match_Distance}if(b.length>this.Match_MaxBits)throw
Error("Pattern too long for this browser.");var
e=this.match_alphabet_(b),f=this,g=this.Match_Threshold,h=a.indexOf(b,c);-1!=h&&(g=Math.min(d(0,h),g),h=a.lastIndexOf(b,c+b.length),-1!=h&&(g=Math.min(d(0,h),g)));for(var
j=1<<b.length-1,h=-1,i,k,q=b.length+a.length,r,t=0;t<b.length;t++){i=0;for(k=q;i<k;)d(t,c+
k)<=g?i=k:q=k,k=Math.floor((q-i)/2+i);q=k;i=Math.max(1,c-k+1);var
p=Math.min(c+k,a.length)+b.length;k=Array(p+2);for(k[p+1]=(1<<t)-1;p>=i;p--){var
w=e[a.charAt(p-1)];k[p]=0===t?(k[p+1]<<1|1)&w:(k[p+1]<<1|1)&w|((r[p+1]|r[p])<<1|1)|r[p+1];if(k[p]&j&&(w=d(t,p-1),w<=g))if(g=w,h=p-1,h>c)i=Math.max(1,2*c-h);else
break}if(d(t+1,c)>g)break;r=k}return h};
diff_match_patch.prototype.match_alphabet_=function(a){for(var
b={},c=0;c<a.length;c++)b[a.charAt(c)]=0;for(c=0;c<a.length;c++)b[a.charAt(c)]|=1<<a.length-c-1;return
b};
diff_match_patch.prototype.patch_addContext_=function(a,b){if(0!=b.length){for(var
c=b.substring(a.start2,a.start2+a.length1),d=0;b.indexOf(c)!=b.lastIndexOf(c)&&c.length<this.Match_MaxBits-this.Patch_Margin-this.Patch_Margin;)d+=this.Patch_Margin,c=b.substring(a.start2-d,a.start2+a.length1+d);d+=this.Patch_Margin;(c=b.substring(a.start2-d,a.start2))&&a.diffs.unshift([0,c]);(d=b.substring(a.start2+a.length1,a.start2+a.length1+d))&&a.diffs.push([0,d]);a.start1-=c.length;a.start2-=c.length;a.length1+=
c.length+d.length;a.length2+=c.length+d.length}};
diff_match_patch.prototype.patch_make=function(a,b,c){var
d;if("string"==typeof a&&"string"==typeof
b&&"undefined"==typeof
c)d=a,b=this.diff_main(d,b,!0),2<b.length&&(this.diff_cleanupSemantic(b),this.diff_cleanupEfficiency(b));else
if(a&&"object"==typeof
a&&"undefined"==typeof
b&&"undefined"==typeof c)b=a,d=this.diff_text1(b);else
if("string"==typeof
a&&b&&"object"==typeof
b&&"undefined"==typeof c)d=a;else
if("string"==typeof a&&"string"==typeof
b&&c&&"object"==typeof c)d=a,b=c;else throw
Error("Unknown call format to patch_make.");
if(0===b.length)return[];c=[];a=new diff_match_patch.patch_obj;for(var
e=0,f=0,g=0,h=d,j=0;j<b.length;j++){var
i=b[j][0],k=b[j][1];!e&&0!==i&&(a.start1=f,a.start2=g);switch(i){case
1:a.diffs[e++]=b[j];a.length2+=k.length;d=d.substring(0,g)+k+d.substring(g);break;case
-1:a.length1+=k.length;a.diffs[e++]=b[j];d=d.substring(0,g)+d.substring(g+k.length);break;case
0:k.length<=2*this.Patch_Margin&&e&&b.length!=j+1?(a.diffs[e++]=b[j],a.length1+=k.length,a.length2+=k.length):k.length>=2*this.Patch_Margin&&
e&&(this.patch_addContext_(a,h),c.push(a),a=new
diff_match_patch.patch_obj,e=0,h=d,f=g)}1!==i&&(f+=k.length);-1!==i&&(g+=k.length)}e&&(this.patch_addContext_(a,h),c.push(a));return
c};diff_match_patch.prototype.patch_deepCopy=function(a){for(var
b=[],c=0;c<a.length;c++){var d=a[c],e=new
diff_match_patch.patch_obj;e.diffs=[];for(var
f=0;f<d.diffs.length;f++)e.diffs[f]=d.diffs[f].slice();e.start1=d.start1;e.start2=d.start2;e.length1=d.length1;e.length2=d.length2;b[c]=e}return
b};
diff_match_patch.prototype.patch_apply=function(a,b){if(0==a.length)return[b,[]];a=this.patch_deepCopy(a);var
c=this.patch_addPadding(a);b=c+b+c;this.patch_splitMax(a);for(var
d=0,e=[],f=0;f<a.length;f++){var
g=a[f].start2+d,h=this.diff_text1(a[f].diffs),j,i=-1;if(h.length>this.Match_MaxBits){if(j=this.match_main(b,h.substring(0,this.Match_MaxBits),g),-1!=j&&(i=this.match_main(b,h.substring(h.length-this.Match_MaxBits),g+h.length-this.Match_MaxBits),-1==i||j>=i))j=-1}else
j=this.match_main(b,h,g);
if(-1==j)e[f]=!1,d-=a[f].length2-a[f].length1;else
if(e[f]=!0,d=j-g,g=-1==i?b.substring(j,j+h.length):b.substring(j,i+this.Match_MaxBits),h==g)b=b.substring(0,j)+this.diff_text2(a[f].diffs)+b.substring(j+h.length);else
if(g=this.diff_main(h,g,!1),h.length>this.Match_MaxBits&&this.diff_levenshtein(g)/h.length>this.Patch_DeleteThreshold)e[f]=!1;else{this.diff_cleanupSemanticLossless(g);for(var
h=0,k,i=0;i<a[f].diffs.length;i++){var
q=a[f].diffs[i];0!==q[0]&&(k=this.diff_xIndex(g,h));1===q[0]?b=b.substring(0,
j+k)+q[1]+b.substring(j+k):-1===q[0]&&(b=b.substring(0,j+k)+b.substring(j+this.diff_xIndex(g,h+q[1].length)));-1!==q[0]&&(h+=q[1].length)}}}b=b.substring(c.length,b.length-c.length);return[b,e]};
diff_match_patch.prototype.patch_addPadding=function(a){for(var
b=this.Patch_Margin,c="",d=1;d<=b;d++)c+=String.fromCharCode(d);for(d=0;d<a.length;d++)a[d].start1+=b,a[d].start2+=b;var
d=a[0],e=d.diffs;if(0==e.length||0!=e[0][0])e.unshift([0,c]),d.start1-=b,d.start2-=b,d.length1+=b,d.length2+=b;else
if(b>e[0][1].length){var
f=b-e[0][1].length;e[0][1]=c.substring(e[0][1].length)+e[0][1];d.start1-=f;d.start2-=f;d.length1+=f;d.length2+=f}d=a[a.length-1];e=d.diffs;0==e.length||0!=e[e.length-1][0]?(e.push([0,
c]),d.length1+=b,d.length2+=b):b>e[e.length-1][1].length&&(f=b-e[e.length-1][1].length,e[e.length-1][1]+=c.substring(0,f),d.length1+=f,d.length2+=f);return
c};
diff_match_patch.prototype.patch_splitMax=function(a){for(var
b=this.Match_MaxBits,c=0;c<a.length;c++)if(!(a[c].length1<=b)){var
d=a[c];a.splice(c--,1);for(var
e=d.start1,f=d.start2,g="";0!==d.diffs.length;){var h=new
diff_match_patch.patch_obj,j=!0;h.start1=e-g.length;h.start2=f-g.length;""!==g&&(h.length1=h.length2=g.length,h.diffs.push([0,g]));for(;0!==d.diffs.length&&h.length1<b-this.Patch_Margin;){var
g=d.diffs[0][0],i=d.diffs[0][1];1===g?(h.length2+=i.length,f+=i.length,h.diffs.push(d.diffs.shift()),
j=!1):-1===g&&1==h.diffs.length&&0==h.diffs[0][0]&&i.length>2*b?(h.length1+=i.length,e+=i.length,j=!1,h.diffs.push([g,i]),d.diffs.shift()):(i=i.substring(0,b-h.length1-this.Patch_Margin),h.length1+=i.length,e+=i.length,0===g?(h.length2+=i.length,f+=i.length):j=!1,h.diffs.push([g,i]),i==d.diffs[0][1]?d.diffs.shift():d.diffs[0][1]=d.diffs[0][1].substring(i.length))}g=this.diff_text2(h.diffs);g=g.substring(g.length-this.Patch_Margin);i=this.diff_text1(d.diffs).substring(0,this.Patch_Margin);""!==i&&
(h.length1+=i.length,h.length2+=i.length,0!==h.diffs.length&&0===h.diffs[h.diffs.length-1][0]?h.diffs[h.diffs.length-1][1]+=i:h.diffs.push([0,i]));j||a.splice(++c,0,h)}}};diff_match_patch.prototype.patch_toText=function(a){for(var
b=[],c=0;c<a.length;c++)b[c]=a[c];return b.join("")};
diff_match_patch.prototype.patch_fromText=function(a){var b=[];if(!a)return
b;a=a.split("\n");for(var c=0,d=/^@@ -(\d+),?(\d*) \+(\d+),?(\d*)
@@$/;c<a.length;){var e=a[c].match(d);if(!e)throw Error("Invalid
patch string: "+a[c]);var f=new
diff_match_patch.patch_obj;b.push(f);f.start1=parseInt(e[1],10);""===e[2]?(f.start1--,f.length1=1):"0"==e[2]?f.length1=0:(f.start1--,f.length1=parseInt(e[2],10));f.start2=parseInt(e[3],10);""===e[4]?(f.start2--,f.length2=1):"0"==e[4]?f.length2=0:(f.start2--,f.length2=
parseInt(e[4],10));for(c++;c<a.length;){e=a[c].charAt(0);try{var
g=decodeURI(a[c].substring(1))}catch(h){throw Error("Illegal escape in
patch_fromText: "+g);}if("-"==e)f.diffs.push([-1,g]);else
if("+"==e)f.diffs.push([1,g]);else if("
"==e)f.diffs.push([0,g]);else if("@"==e)break;else
if(""!==e)throw Error('Invalid patch mode
"'+e+'" in: '+g);c++}}return
b};diff_match_patch.patch_obj=function(){this.diffs=[];this.start2=this.start1=null;this.length2=this.length1=0};
diff_match_patch.patch_obj.prototype.toString=function(){var
a,b;a=0===this.length1?this.start1+",0":1==this.length1?this.start1+1:this.start1+1+","+this.length1;b=0===this.length2?this.start2+",0":1==this.length2?this.start2+1:this.start2+1+","+this.length2;a=["@@
-"+a+" +"+b+" @@\n"];var
c;for(b=0;b<this.diffs.length;b++){switch(this.diffs[b][0]){case
1:c="+";break;case -1:c="-";break;case 0:c="
"}a[b+1]=c+encodeURI(this.diffs[b][1])+"\n"}return
a.join("").replace(/%20/g," ")};
this.diff_match_patch=diff_match_patch;this.DIFF_DELETE=-1;this.DIFF_INSERT=1;this.DIFF_EQUAL=0;})()
js/jquery.pretty-text-diff.js000064400000005766151165463030012275
0ustar00// Generated by CoffeeScript 1.4.0
/*
@preserve jQuery.PrettyTextDiff 1.0.2
See https://github.com/arnab/jQuery.PrettyTextDiff/
Modified to show with and without HTML: Mark Dexter, Joomla Project.
*/
(function() {
var $;
$ = jQuery;
$.fn.extend({
prettyTextDiff: function(options) {
var dmp, settings;
settings = {
originalContainer: ".original",
changedContainer: ".changed",
diffContainer: ".diff",
originalHtmlContainer: ".originalhtml",
changedHtmlContainer: ".changedhtml",
diffHtmlContainer: ".diffhtml",
cleanup: true,
debug: false
};
settings = $.extend(settings, options);
$.fn.prettyTextDiff.debug("Options: ", settings, settings);
dmp = new diff_match_patch();
return this.each(function() {
var changed, diff_as_html, diffs, original, changedhtml,
originalhtml, diffshtml;
original = $(settings.originalContainer, this).text();
$.fn.prettyTextDiff.debug("Original text found: ",
original, settings);
changed = $(settings.changedContainer, this).text();
$.fn.prettyTextDiff.debug("Changed text found: ",
changed, settings);
originalhtml = $(settings.originalHtmlContainer, this).text();
$.fn.prettyTextDiff.debug("Original text found: ",
original, settings);
changedhtml = $(settings.changedHtmlContainer, this).text();
$.fn.prettyTextDiff.debug("Changed text found: ",
changed, settings);
diffs = dmp.diff_main(original, changed);
diffshtml = dmp.diff_main(originalhtml, changedhtml);
if (settings.cleanup) {
dmp.diff_cleanupSemantic(diffs);
dmp.diff_cleanupSemantic(diffshtml);
}
$.fn.prettyTextDiff.debug("Diffs: ", diffs, settings);
diff_as_html = diffs.map(function(diff) {
return $.fn.prettyTextDiff.createHTML(diff);
});
diffhtml_as_html = diffshtml.map(function(diff) {
return $.fn.prettyTextDiff.createHTML(diff);
});
$(settings.diffContainer,
this).html(diff_as_html.join(''));
$(settings.diffHtmlContainer,
this).html(diffhtml_as_html.join(''));
return this;
});
}
});
$.fn.prettyTextDiff.debug = function(message, object, settings) {
if (settings.debug) {
return console.log(message, object);
}
};
$.fn.prettyTextDiff.createHTML = function(diff) {
var data, html, operation, pattern_amp, pattern_gt, pattern_lt,
pattern_para, text;
html = [];
pattern_amp = /&/g;
pattern_lt = /</g;
pattern_gt = />/g;
pattern_para = /\n/g;
operation = diff[0], data = diff[1];
text = data.replace(pattern_amp,
'&').replace(pattern_lt,
'<').replace(pattern_gt,
'>').replace(pattern_para, '<br>');
switch (operation) {
case DIFF_INSERT:
return '<ins>' + text + '</ins>';
case DIFF_DELETE:
return '<del>' + text + '</del>';
case DIFF_EQUAL:
return '<span>' + text + '</span>';
}
};
}).call(this);
js/jquery.pretty-text-diff.min.js000064400000004237151165463030013047
0ustar00(function(){var
$;$=jQuery;$.fn.extend({prettyTextDiff:function(options){var
dmp,settings;settings={originalContainer:".original",changedContainer:".changed",diffContainer:".diff",originalHtmlContainer:".originalhtml",changedHtmlContainer:".changedhtml",diffHtmlContainer:".diffhtml",cleanup:true,debug:false};settings=$.extend(settings,options);$.fn.prettyTextDiff.debug("Options:
",settings,settings);dmp=new diff_match_patch;return
this.each(function(){var changed,diff_as_html,diffs,original,changedhtml,
originalhtml,diffshtml;original=$(settings.originalContainer,this).text();$.fn.prettyTextDiff.debug("Original
text found:
",original,settings);changed=$(settings.changedContainer,this).text();$.fn.prettyTextDiff.debug("Changed
text found:
",changed,settings);originalhtml=$(settings.originalHtmlContainer,this).text();$.fn.prettyTextDiff.debug("Original
text found:
",original,settings);changedhtml=$(settings.changedHtmlContainer,this).text();$.fn.prettyTextDiff.debug("Changed
text found: ",changed,
settings);diffs=dmp.diff_main(original,changed);diffshtml=dmp.diff_main(originalhtml,changedhtml);if(settings.cleanup){dmp.diff_cleanupSemantic(diffs);dmp.diff_cleanupSemantic(diffshtml)}$.fn.prettyTextDiff.debug("Diffs:
",diffs,settings);diff_as_html=diffs.map(function(diff){return
$.fn.prettyTextDiff.createHTML(diff)});diffhtml_as_html=diffshtml.map(function(diff){return
$.fn.prettyTextDiff.createHTML(diff)});$(settings.diffContainer,this).html(diff_as_html.join(""));$(settings.diffHtmlContainer,
this).html(diffhtml_as_html.join(""));return
this})}});$.fn.prettyTextDiff.debug=function(message,object,settings){if(settings.debug)return
console.log(message,object)};$.fn.prettyTextDiff.createHTML=function(diff){var
data,html,operation,pattern_amp,pattern_gt,pattern_lt,pattern_para,text;html=[];pattern_amp=/&/g;pattern_lt=/</g;pattern_gt=/>/g;pattern_para=/\n/g;operation=diff[0],data=diff[1];text=data.replace(pattern_amp,"&").replace(pattern_lt,"<").replace(pattern_gt,">").replace(pattern_para,
"<br>");switch(operation){case
DIFF_INSERT:return"<ins>"+text+"</ins>";case
DIFF_DELETE:return"<del>"+text+"</del>";case
DIFF_EQUAL:return"<span>"+text+"</span>"}}}).call(this);