<nav class="contentHeaderNavigation">
<ul>
+ <li><a href="{$object->getEditLink()}" class="button"><span class="icon icon16 fa-pencil"></span> <span>{lang}wcf.global.button.edit{/lang}</span></a></li>
<li><a href="{$object->getLink()}" class="button"><span class="icon icon16 fa-arrow-right"></span> <span>{lang}wcf.edit.button.goToContent{/lang}</span></a></li>
{event name='contentHeaderNavigation'}
</tr>
{/foreach}
</tbody>
+
+ {js application='wcf' file='WCF.Message' bundle='WCF.Combined'}
<script data-relocate="true">
$(function () {
- /*
- TODO: this needs to be adjusted
- */
- new WCF.Message.EditHistory($('input[name=oldID]'), $('input[name=newID]'), '.jsEditRow');
+ new WCF.Message.EditHistory($('input[name=oldID]'), $('input[name=newID]'), '.jsEditRow', undefined, {
+ isVersionTracker: true,
+ versionTrackerObjectType: '{$objectType}',
+ versionTrackerObjectId: {@$objectID},
+ redirectUrl: '{$object->getEditLink()}'
+ });
});
</script>
</table>
* @param object newIDInputs
* @param string containerSelector
* @param string buttonSelector
+ * @param {Object} options
*/
- init: function(oldIDInputs, newIDInputs, containerSelector, buttonSelector) {
+ init: function(oldIDInputs, newIDInputs, containerSelector, buttonSelector, options) {
this._oldIDInputs = oldIDInputs;
this._newIDInputs = newIDInputs;
this._containerSelector = containerSelector;
this._buttonSelector = (buttonSelector) ? buttonSelector : '.jsRevertButton';
+ this._options = $.extend({
+ isVersionTracker: false,
+ versionTrackerObjectType: '',
+ versionTrackerObjectId: 0,
+ redirectUrl: ''
+ }, options);
this.proxy = new WCF.Action.Proxy({
success: $.proxy(this._success, this)
* @param jQuery object
*/
_sendRequest: function(object) {
- this.proxy.setOption('data', {
- actionName: 'revert',
- className: 'wcf\\data\\edit\\history\\entry\\EditHistoryEntryAction',
- objectIDs: [ $(object).data('objectID') ]
- });
+ if (this._options.isVersionTracker) {
+ //noinspection JSUnresolvedVariable
+ this.proxy.setOption('url', window.WSC_API_URL + 'index.php?ajax-invoke/&t=' + window.SECURITY_TOKEN);
+ this.proxy.setOption('data', {
+ actionName: 'revert',
+ className: 'wcf\\system\\version\\VersionTracker',
+ parameters: {
+ objectType: this._options.versionTrackerObjectType,
+ objectID: this._options.versionTrackerObjectId,
+ versionID: $(object).data('objectID')
+ }
+ });
+ }
+ else {
+ this.proxy.setOption('data', {
+ actionName: 'revert',
+ className: 'wcf\\data\\edit\\history\\entry\\EditHistoryEntryAction',
+ objectIDs: [$(object).data('objectID')]
+ });
+ }
this.proxy.sendRequest();
},
* @param object jqXHR
*/
_success: function(data, textStatus, jqXHR) {
- window.location.reload(true);
+ if (this._options.redirectUrl) {
+ new WCF.System.Notification().show((function () {
+ window.location = this._options.redirectUrl;
+ }).bind(this));
+ }
+ else {
+ window.location.reload(true);
+ }
}
});
* @package WoltLabSuite\Core\Data
*/
interface IVersionTrackerObject extends IUserContent {
+ /**
+ * Returns the link to the object's edit page.
+ *
+ * @return string
+ */
+ public function getEditLink();
+
/**
* Returns the object's unique id.
*
public function update() {
parent::update();
+ $isRevert = (!empty($this->parameters['isRevert']));
+
// update article content
if (!empty($this->parameters['content'])) {
foreach ($this->getObjects() as $article) {
'title' => $content['title'],
'teaser' => $content['teaser'],
'content' => $content['content'],
- 'imageID' => $content['imageID'],
- 'teaserImageID' => $content['teaserImageID']
+ 'imageID' => ($isRevert) ? $articleContent->imageID : $content['imageID'],
+ 'teaserImageID' => ($isRevert) ? $articleContent->teaserImageID : $content['teaserImageID']
]);
$versionData[] = $articleContent;
}
// delete tags
- if (empty($content['tags'])) {
+ if (!$isRevert && empty($content['tags'])) {
TagEngine::getInstance()->deleteObjectTags('com.woltlab.wcf.article', $articleContent->articleContentID, ($languageID ?: null));
}
}
'title' => $content['title'],
'teaser' => $content['teaser'],
'content' => $content['content'],
- 'imageID' => $content['imageID'],
- 'teaserImageID' => $content['teaserImageID']
+ 'imageID' => ($isRevert) ? null : $content['imageID'],
+ 'teaserImageID' => ($isRevert) ? null : $content['teaserImageID']
]);
$articleContentEditor = new ArticleContentEditor($articleContent);
}
// save tags
- if (!empty($content['tags'])) {
+ if (!$isRevert && !empty($content['tags'])) {
TagEngine::getInstance()->addObjectTags('com.woltlab.wcf.article', $articleContent->articleContentID, $content['tags'], ($languageID ?: LanguageFactory::getInstance()->getDefaultLanguageID()));
}
use wcf\data\article\content\ArticleContent;
use wcf\data\DatabaseObjectDecorator;
use wcf\data\IVersionTrackerObject;
+use wcf\system\request\LinkHandler;
/**
* Represents an article with version tracking.
public function getTitle() {
return $this->getDecoratedObject()->getTitle();
}
+
+ /**
+ * @inheritDoc
+ */
+ public function getEditLink() {
+ return LinkHandler::getInstance()->getLink('ArticleEdit', ['isACP' => true, 'id' => $this->getDecoratedObject()->articleID]);
+ }
}
<?php
namespace wcf\system\version;
use wcf\data\article\Article;
+use wcf\data\article\ArticleAction;
use wcf\data\article\ArticleList;
use wcf\data\article\ArticleVersionTracker;
use wcf\data\IVersionTrackerObject;
/** @var Article $object */
return $object->isMultilingual == 1;
}
+
+ /**
+ * @inheritDoc
+ */
+ public function revert(IVersionTrackerObject $object, VersionTrackerEntry $entry) {
+ /** @var ArticleVersionTracker $object */
+
+ // build the content data
+ $properties = $this->getTrackedProperties();
+ $content = [];
+ foreach ($object->getArticleContents() as $articleContent) {
+ $content[$articleContent->languageID ?: 0] = $entry->getPayloadForProperties($properties, $articleContent->languageID ?: 0);
+ }
+
+ $action = new ArticleAction([$object->getDecoratedObject()], 'update', ['content' => $content, 'isRevert' => true]);
+ $action->executeAction();
+ }
}
* @return boolean
*/
public function isI18n(IVersionTrackerObject $object);
+
+ /**
+ * Reverts an object to a previous version.
+ *
+ * @param IVersionTrackerObject $object target object
+ * @param VersionTrackerEntry $entry previous version
+ */
+ public function revert(IVersionTrackerObject $object, VersionTrackerEntry $entry);
}
use wcf\data\package\Package;
use wcf\data\package\PackageList;
use wcf\data\IVersionTrackerObject;
+use wcf\system\exception\PermissionDeniedException;
use wcf\system\exception\SystemException;
+use wcf\system\exception\UserInputException;
+use wcf\system\IAJAXInvokeAction;
+use wcf\system\request\RequestHandler;
use wcf\system\SingletonFactory;
use wcf\system\WCF;
+use wcf\util\StringUtil;
/**
* Represents objects that support some of their properties to be saved.
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
* @package WoltLabSuite\Core\System\Version
*/
-class VersionTracker extends SingletonFactory {
+class VersionTracker extends SingletonFactory implements IAJAXInvokeAction {
+ /**
+ * list of methods that may be invoked via ajax
+ * @var string[]
+ */
+ public static $allowInvoke = ['revert'];
+
/**
* list of available object types
* @var ObjectType[]
*/
protected $availableObjectTypes = [];
+ /**
+ * version tracker object used for the version revert process
+ * @var IVersionTrackerObject
+ */
+ protected $object;
+
+ /**
+ * object type processor object
+ * @var IVersionTrackerProvider
+ */
+ protected $processor;
+
+ /**
+ * version tracker entry used for the version revert process
+ * @var VersionTrackerEntry
+ */
+ protected $version;
+
/**
* @inheritDoc
*/
throw new \InvalidArgumentException("Unknown object type '".$name."' for definition 'com.woltlab.wcf.versionTracker.objectType'.");
}
+ /**
+ * Validates parameters to revert an object to a previous version.
+ *
+ * @throws PermissionDeniedException
+ * @throws UserInputException
+ */
+ public function validateRevert() {
+ if (!RequestHandler::getInstance()->isACPRequest()) {
+ throw new PermissionDeniedException();
+ }
+
+ if (!isset($_POST['parameters'])) {
+ throw new UserInputException('parameters');
+ }
+
+ $objectTypeName = (isset($_POST['parameters']['objectType'])) ? StringUtil::trim($_POST['parameters']['objectType']) : '';
+ $objectID = (isset($_POST['parameters']['objectID'])) ? intval($_POST['parameters']['objectID']) : 0;
+ $versionID = (isset($_POST['parameters']['versionID'])) ? intval($_POST['parameters']['versionID']) : 0;
+
+ $objectType = $this->getObjectType($objectTypeName);
+ /** @var IVersionTrackerProvider $processor */
+ $this->processor = $objectType->getProcessor();
+ if (!$this->processor->canAccess()) {
+ throw new PermissionDeniedException();
+ }
+
+ $this->object = $this->processor->getObjectByID($objectID);
+ if (!$this->object->getObjectID()) {
+ throw new UserInputException('objectID');
+ }
+
+ $this->version = $this->getVersion($objectTypeName, $versionID);
+ if (!$this->version->versionID) {
+ throw new UserInputException('versionID');
+ }
+ }
+
+ /**
+ * Reverts an object to a previous version.
+ */
+ public function revert() {
+ $this->processor->revert($this->object, $this->version);
+ }
+
/**
* Creates a database table for an object type unless it exists already.
*
return '';
}
+ /**
+ * Returns the stored values for all given properties. Unknown or missing
+ * properties will be set to an empty string.
+ *
+ * @param string[] $properties list of property names
+ * @param integer $languageID language id
+ * @return string[]
+ */
+ public function getPayloadForProperties(array $properties, $languageID) {
+ $payload = [];
+ foreach ($properties as $property) {
+ $payload[$property] = '';
+
+ if (isset($this->payload[$languageID]) && isset($this->payload[$languageID][$property])) {
+ $payload[$property] = $this->payload[$languageID][$property];
+ }
+ }
+
+ return $payload;
+ }
+
/**
* Returns the list of language ids.
*