Implemented history support for boxes and pages
authorAlexander Ebert <ebert@woltlab.com>
Wed, 29 Mar 2017 13:07:51 +0000 (15:07 +0200)
committerAlexander Ebert <ebert@woltlab.com>
Wed, 29 Mar 2017 13:08:03 +0000 (15:08 +0200)
See #2202

16 files changed:
com.woltlab.wcf/objectType.xml
com.woltlab.wcf/objectTypeDefinition.xml
wcfsetup/install/files/acp/templates/boxAdd.tpl
wcfsetup/install/files/acp/templates/pageAdd.tpl
wcfsetup/install/files/acp/templates/versionTrackerList.tpl
wcfsetup/install/files/lib/acp/form/BoxEditForm.class.php
wcfsetup/install/files/lib/acp/form/PageEditForm.class.php
wcfsetup/install/files/lib/data/box/BoxAction.class.php
wcfsetup/install/files/lib/data/box/BoxVersionTracker.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/data/page/PageAction.class.php
wcfsetup/install/files/lib/data/page/PageVersionTracker.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/version/AbstractVersionTrackerProvider.class.php
wcfsetup/install/files/lib/system/version/BoxVersionTrackerProvider.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/version/PageVersionTrackerProvider.class.php [new file with mode: 0644]
wcfsetup/install/lang/de.xml
wcfsetup/install/lang/en.xml

index 32e238a882295d574b82100405ddad786cfc527f..5132dfb6fe78dcf8d115e84ad5992b0f4f02c93b 100644 (file)
                        <name>com.woltlab.wcf.article.content</name>
                        <definitionname>com.woltlab.wcf.message</definitionname>
                </type>
-               
-               <type>
-                       <name>com.woltlab.wcf.article</name>
-                       <definitionname>com.woltlab.wcf.versionTracker.objectType</definitionname>
-                       <classname>wcf\data\article\ArticleVersionTrackerProvider</classname>
-                       <tableName>wcf1_article</tableName>
-                       <tablePrimaryKey>articleID</tablePrimaryKey>
-               </type>
                <!-- /articles -->
                
                <type>
                </type>
                <!-- /moderation type -->
                
+               <!-- version tracker -->
+               <type>
+                       <name>com.woltlab.wcf.article</name>
+                       <definitionname>com.woltlab.wcf.versionTracker.objectType</definitionname>
+                       <classname>wcf\system\version\ArticleVersionTrackerProvider</classname>
+                       <tableName>wcf1_article</tableName>
+                       <tablePrimaryKey>articleID</tablePrimaryKey>
+               </type>
+               <type>
+                       <name>com.woltlab.wcf.box</name>
+                       <definitionname>com.woltlab.wcf.versionTracker.objectType</definitionname>
+                       <classname>wcf\system\version\BoxVersionTrackerProvider</classname>
+                       <tableName>wcf1_box</tableName>
+                       <tablePrimaryKey>boxID</tablePrimaryKey>
+               </type>
+               <type>
+                       <name>com.woltlab.wcf.page</name>
+                       <definitionname>com.woltlab.wcf.versionTracker.objectType</definitionname>
+                       <classname>wcf\system\version\PageVersionTrackerProvider</classname>
+                       <tableName>wcf1_page</tableName>
+                       <tablePrimaryKey>pageID</tablePrimaryKey>
+               </type>
+               <!-- /version tracker -->
+               
                <!-- Visit Tracker -->
                <type>
                        <name>com.woltlab.wcf.moderation.queue</name>
index 47b3f7d90ac2e1eb9fc2ea3bebc6362dd3f007c1..a3319e251ad710127496780407707f164cb4602f 100644 (file)
@@ -91,7 +91,7 @@
                
                <definition>
                        <name>com.woltlab.wcf.versionTracker.objectType</name>
-                       <interfacename>wcf\system\version\IVersionTrackerObjectTypeProvider</interfacename>
+                       <interfacename>wcf\system\version\IVersionTrackerProvider</interfacename>
                </definition>
                
                <definition>
index debf0d40e0a541e5bef960aeede85b08de4765b4..22b4b75dabae2a445a140d9cb343f6a18c099190 100644 (file)
        <p class="success">{lang}wcf.global.success.{$action}{/lang}</p>
 {/if}
 
+{if $action == 'edit' && !$lastVersion|empty}
+       <p class="info">{lang}wcf.acp.box.lastVersion{/lang}</p>
+{/if}
+
 <form id="formContainer" method="post" action="{if $action == 'add'}{link controller='BoxAdd'}{/link}{else}{link controller='BoxEdit' id=$boxID}{/link}{/if}">
        <div class="section tabMenuContainer" data-active="{$activeTabMenuItem}" data-store="activeTabMenuItem" id="pageTabMenuContainer">
                <nav class="tabMenu">
index 98375e7861de98f0a58f15a0356c927fb7e3c64a..4efacf043582d13f5ed706fd320ea099629187ee 100644 (file)
        <p class="success">{lang}wcf.global.success.{$action}{/lang}</p>
 {/if}
 
+{if $action == 'edit' && !$lastVersion|empty}
+       <p class="info">{lang}wcf.acp.page.lastVersion{/lang}</p>
+{/if}
+
 <form method="post" action="{if $action == 'add'}{link controller='PageAdd'}{/link}{else}{link controller='PageEdit' id=$pageID}{/link}{/if}">
        <div class="section tabMenuContainer" data-active="{$activeTabMenuItem}" data-store="activeTabMenuItem" id="pageTabMenuContainer">
                <nav class="tabMenu">
index 248fc0a7c2a694946f09e9cdd0241aa164930198..52bf9e4ca5d982b75ec9e77f551712d4a9361858 100644 (file)
                                                {event name='rowButtons'}
                                        </td>
                                        <td class="columnID"><strong>{lang}wcf.edit.currentVersion{/lang}</strong></td>
-                                       <td class="columnText columnUser"><a href="{link controller='UserEdit' id=$object->getUserID()}{/link}">{$object->getUsername()}</a></td>
-                                       <td class="columnDate columnTime">{@$object->getTime()|time}</td>
+                                       <td class="columnText columnUser">{if $object->getUserID()}<a href="{link controller='UserEdit' id=$object->getUserID()}{/link}">{$object->getUsername()}{else}---{/if}</a></td>
+                                       <td class="columnDate columnTime">{if $object->getTime()}{@$object->getTime()|time}{else}---{/if}</td>
                                        
                                        {event name='columns'}
                                </tr>
index 9211a7a6e0278df9a6561a97a34b4077f06b3627..2b772d5c1b0bbac8c7ce35691fa8aa30b8b33da8 100644 (file)
@@ -9,6 +9,7 @@ use wcf\system\box\IConditionBoxController;
 use wcf\system\condition\ConditionHandler;
 use wcf\system\exception\IllegalLinkException;
 use wcf\system\language\LanguageFactory;
+use wcf\system\version\VersionTracker;
 use wcf\system\WCF;
 
 /**
@@ -213,7 +214,8 @@ class BoxEditForm extends BoxAddForm {
                WCF::getTPL()->assign([
                        'action' => 'edit',
                        'boxID' => $this->boxID,
-                       'box' => $this->box
+                       'box' => $this->box,
+                       'lastVersion' => VersionTracker::getInstance()->getLastVersion('com.woltlab.wcf.box', $this->boxID)
                ]);
        }
 }
index 9d2bbff2a0886dfaf6548146e8ea95a24403ae17..9dac1c229077c64253b0b36517bbc56f17385203 100644 (file)
@@ -7,6 +7,7 @@ use wcf\system\acl\simple\SimpleAclHandler;
 use wcf\system\exception\IllegalLinkException;
 use wcf\system\exception\UserInputException;
 use wcf\system\language\LanguageFactory;
+use wcf\system\version\VersionTracker;
 use wcf\system\WCF;
 
 /**
@@ -265,7 +266,8 @@ class PageEditForm extends PageAddForm {
                WCF::getTPL()->assign([
                        'action' => 'edit',
                        'pageID' => $this->pageID,
-                       'page' => $this->page
+                       'page' => $this->page,
+                       'lastVersion' => VersionTracker::getInstance()->getLastVersion('com.woltlab.wcf.page', $this->pageID)
                ]);
        }
 }
index dc0d4bbd0503d731548ec55bfbb1a0d69d31c8bb..47c18ed2635b2c544aa609c67e88a7914a733303 100644 (file)
@@ -10,6 +10,7 @@ use wcf\system\exception\PermissionDeniedException;
 use wcf\system\exception\UserInputException;
 use wcf\system\html\simple\HtmlSimpleParser;
 use wcf\system\message\embedded\object\MessageEmbeddedObjectManager;
+use wcf\system\version\VersionTracker;
 use wcf\system\WCF;
 
 /**
@@ -132,9 +133,14 @@ class BoxAction extends AbstractDatabaseObjectAction {
        public function update() {
                parent::update();
                
+               $isRevert = (!empty($this->parameters['isRevert']));
+               
                // update box content
                if (!empty($this->parameters['content'])) {
                        foreach ($this->getObjects() as $box) {
+                               $versionData = [];
+                               $hasChanges = false;
+                               
                                foreach ($this->parameters['content'] as $languageID => $content) {
                                        if (!empty($content['htmlInputProcessor'])) {
                                                /** @noinspection PhpUndefinedMethodInspection */
@@ -149,8 +155,14 @@ class BoxAction extends AbstractDatabaseObjectAction {
                                                $boxContentEditor->update([
                                                        'title' => $content['title'],
                                                        'content' => $content['content'],
-                                                       'imageID' => $content['imageID']
+                                                       'imageID' => ($isRevert) ? $boxContent->imageID : $content['imageID']
                                                ]);
+                                               
+                                               $versionData[] = $boxContent;
+                                               if ($boxContent->content != $content['content'] || $boxContent->title != $content['title']) {
+                                                       $hasChanges = true;
+                                               }
+                                               
                                                $boxContent = BoxContent::getBoxContent($box->boxID, ($languageID ?: null));
                                        }
                                        else {
@@ -160,9 +172,12 @@ class BoxAction extends AbstractDatabaseObjectAction {
                                                        'languageID' => $languageID ?: null,
                                                        'title' => $content['title'],
                                                        'content' => $content['content'],
-                                                       'imageID' => $content['imageID']
+                                                       'imageID' => ($isRevert) ? $content['imageID'] : null
                                                ]);
                                                $boxContentEditor = new BoxContentEditor($boxContent);
+                                               
+                                               $versionData[] = $boxContent;
+                                               $hasChanges = true;
                                        }
                                        
                                        // save embedded objects
@@ -186,6 +201,12 @@ class BoxAction extends AbstractDatabaseObjectAction {
                                                file_put_contents(WCF_DIR . 'templates/' . $box->getTplName(($languageID ?: null)) . '.tpl', $content['content']);
                                        }
                                }
+                               
+                               if ($hasChanges) {
+                                       $boxObj = new BoxVersionTracker($box->getDecoratedObject());
+                                       $boxObj->setContent($versionData);
+                                       VersionTracker::getInstance()->add('com.woltlab.wcf.box', $boxObj);
+                               }
                        }
                }
                
diff --git a/wcfsetup/install/files/lib/data/box/BoxVersionTracker.class.php b/wcfsetup/install/files/lib/data/box/BoxVersionTracker.class.php
new file mode 100644 (file)
index 0000000..e9a7e59
--- /dev/null
@@ -0,0 +1,107 @@
+<?php
+namespace wcf\data\box;
+use wcf\data\box\content\BoxContent;
+use wcf\data\DatabaseObjectDecorator;
+use wcf\data\IVersionTrackerObject;
+use wcf\system\request\LinkHandler;
+
+/**
+ * Represents a box with version tracking.
+ * 
+ * @author     Alexander Ebert
+ * @copyright  2001-2017 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    WoltLabSuite\Core\Data\Box
+ * @since      3.0
+ * 
+ * @method     Box     getDecoratedObject()
+ * @mixin      Box
+ */
+class BoxVersionTracker extends DatabaseObjectDecorator implements IVersionTrackerObject {
+       /**
+        * @inheritDoc
+        */
+       protected static $baseClass = Box::class;
+       
+       /**
+        * list of box content objects
+        * @var BoxContent[]
+        */
+       protected $content = [];
+       
+       /**
+        * @inheritDoc
+        */
+       public function getObjectID() {
+               return $this->getDecoratedObject()->boxID;
+       }
+       
+       /**
+        * Adds an box content object as child.
+        * 
+        * @param       BoxContent      $content        box content object
+        */
+       public function addContent(BoxContent $content) {
+               $this->content[] = $content;
+       }
+       
+       /**
+        * Sets the list of box content objects.
+        * 
+        * @param       BoxContent[]    $content        box content objects
+        */
+       public function setContent(array $content) {
+               $this->content = $content;
+       }
+       
+       /**
+        * Returns the list of stored box content objects.
+        * 
+        * @return      BoxContent[]    stored box content objects
+        */
+       public function getContent() {
+               return $this->content;
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function getLink() {
+               return $this->getDecoratedObject()->getLink();
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function getUsername() {
+               return '';
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function getUserID() {
+               return 0;
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function getTime() {
+               return 0;
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function getTitle() {
+               return $this->getDecoratedObject()->getTitle();
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function getEditLink() {
+               return LinkHandler::getInstance()->getLink('BoxEdit', ['isACP' => true, 'id' => $this->getDecoratedObject()->boxID]);
+       }
+}
index c6ce933f7e5e390fc365bbf7059a5bdc0b841ab7..b30a84d79d1f60f7c29e3b208280cd96774cd265 100644 (file)
@@ -11,6 +11,7 @@ use wcf\system\html\simple\HtmlSimpleParser;
 use wcf\system\message\embedded\object\MessageEmbeddedObjectManager;
 use wcf\system\page\handler\ILookupPageHandler;
 use wcf\system\search\SearchIndexManager;
+use wcf\system\version\VersionTracker;
 use wcf\system\WCF;
 
 /**
@@ -145,10 +146,13 @@ class PageAction extends AbstractDatabaseObjectAction implements ISearchAction,
         */
        public function update() {
                parent::update();
-       
+               
                // update page content
                if (!empty($this->parameters['content'])) {
                        foreach ($this->getObjects() as $page) {
+                               $versionData = [];
+                               $hasChanges = false;
+                               
                                foreach ($this->parameters['content'] as $languageID => $content) {
                                        if (!empty($content['htmlInputProcessor'])) {
                                                /** @noinspection PhpUndefinedMethodInspection */
@@ -167,6 +171,15 @@ class PageAction extends AbstractDatabaseObjectAction implements ISearchAction,
                                                        'metaKeywords' => $content['metaKeywords'],
                                                        'customURL' => $content['customURL']
                                                ]);
+                                               
+                                               $versionData[] = $pageContent;
+                                               foreach (['title', 'content', 'metaDescription', 'metaKeywords', 'customURL'] as $property) {
+                                                       if ($pageContent->{$property} != $content[$property]) {
+                                                               $hasChanges = true;
+                                                               break;
+                                                       }
+                                               }
+                                               
                                                $pageContent = PageContent::getPageContent($page->pageID, ($languageID ?: null));
                                        }
                                        else {
@@ -181,6 +194,9 @@ class PageAction extends AbstractDatabaseObjectAction implements ISearchAction,
                                                        'customURL' => $content['customURL']
                                                ]);
                                                $pageContentEditor = new PageContentEditor($pageContent);
+                                               
+                                               $versionData[] = $pageContent;
+                                               $hasChanges = true;
                                        }
                                        
                                        // update search index
@@ -216,6 +232,12 @@ class PageAction extends AbstractDatabaseObjectAction implements ISearchAction,
                                                file_put_contents(WCF_DIR . 'templates/' . $page->getTplName(($languageID ?: null)) . '.tpl', $content['content']);
                                        }
                                }
+                               
+                               if ($hasChanges) {
+                                       $pageObj = new PageVersionTracker($page->getDecoratedObject());
+                                       $pageObj->setContent($versionData);
+                                       VersionTracker::getInstance()->add('com.woltlab.wcf.page', $pageObj);
+                               }
                        }
                }
                
diff --git a/wcfsetup/install/files/lib/data/page/PageVersionTracker.class.php b/wcfsetup/install/files/lib/data/page/PageVersionTracker.class.php
new file mode 100644 (file)
index 0000000..6accc75
--- /dev/null
@@ -0,0 +1,107 @@
+<?php
+namespace wcf\data\page;
+use wcf\data\page\content\PageContent;
+use wcf\data\DatabaseObjectDecorator;
+use wcf\data\IVersionTrackerObject;
+use wcf\system\request\LinkHandler;
+
+/**
+ * Represents a page with version tracking.
+ * 
+ * @author     Alexander Ebert
+ * @copyright  2001-2017 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    WoltLabSuite\Core\Data\Page
+ * @since      3.0
+ * 
+ * @method     Page    getDecoratedObject()
+ * @mixin      Page
+ */
+class PageVersionTracker extends DatabaseObjectDecorator implements IVersionTrackerObject {
+       /**
+        * @inheritDoc
+        */
+       protected static $baseClass = Page::class;
+       
+       /**
+        * list of page content objects
+        * @var PageContent[]
+        */
+       protected $content = [];
+       
+       /**
+        * @inheritDoc
+        */
+       public function getObjectID() {
+               return $this->getDecoratedObject()->pageID;
+       }
+       
+       /**
+        * Adds an page content object as child.
+        * 
+        * @param       PageContent     $content        page content object
+        */
+       public function addContent(PageContent $content) {
+               $this->content[] = $content;
+       }
+       
+       /**
+        * Sets the list of page content objects.
+        * 
+        * @param       PageContent[]   $content        page content objects
+        */
+       public function setContent(array $content) {
+               $this->content = $content;
+       }
+       
+       /**
+        * Returns the list of stored page content objects.
+        * 
+        * @return      PageContent[]   stored page content objects
+        */
+       public function getContent() {
+               return $this->content;
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function getLink() {
+               return $this->getDecoratedObject()->getLink();
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function getUsername() {
+               return '';
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function getUserID() {
+               return 0;
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function getTime() {
+               return 0;
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function getTitle() {
+               return $this->getDecoratedObject()->getTitle();
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function getEditLink() {
+               return LinkHandler::getInstance()->getLink('PageEdit', ['isACP' => true, 'id' => $this->getDecoratedObject()->pageID]);
+       }
+}
index 650ab70aa65c6cf85b9e5ee69d01f5374d30d661..0a7f58ff8dec445df4bdca74fe9d6c902427b511 100644 (file)
@@ -20,7 +20,8 @@ abstract class AbstractVersionTrackerProvider extends AbstractObjectTypeProvider
        public static $defaultProperty = '';
        
        /**
-        * list of property names to their phrase
+        * list of property names to their phrase, the order in which properties
+        * appear is significant and is used as display orders when comparing changes
         * @var string[]
         */
        public static $propertyLabels = [];
diff --git a/wcfsetup/install/files/lib/system/version/BoxVersionTrackerProvider.class.php b/wcfsetup/install/files/lib/system/version/BoxVersionTrackerProvider.class.php
new file mode 100644 (file)
index 0000000..c35d7f0
--- /dev/null
@@ -0,0 +1,127 @@
+<?php
+namespace wcf\system\version;
+use wcf\data\box\Box;
+use wcf\data\box\BoxAction;
+use wcf\data\box\BoxList;
+use wcf\data\box\BoxVersionTracker;
+use wcf\data\IVersionTrackerObject;
+
+/**
+ * Version tracker object type provider implementation for boxes.
+ * 
+ * @author     Alexander Ebert
+ * @copyright  2001-2017 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    WoltLabSuite\Core\System\Version
+ */
+class BoxVersionTrackerProvider extends AbstractVersionTrackerProvider {
+       /**
+        * @inheritDoc
+        */
+       public $activeMenuItem = 'wcf.acp.menu.link.cms.box.list';
+       
+       /**
+        * @inheritDoc
+        */
+       public $className = Box::class;
+       
+       /**
+        * @inheritDoc
+        */
+       public $decoratorClassName = BoxVersionTracker::class;
+       
+       /**
+        * @inheritDoc
+        */
+       public $listClassName = BoxList::class;
+       
+       /**
+        * @inheritDoc
+        */
+       public $permissionCanAccess = 'admin.content.cms.canManageBox';
+       
+       /**
+        * @inheritDoc
+        */
+       public static $defaultProperty = 'content';
+       
+       /**
+        * @inheritDoc
+        */
+       public static $propertyLabels = [
+               'content' => 'wcf.acp.box.content',
+               'title' => 'wcf.global.title'
+       ];
+       
+       /**
+        * @inheritDoc
+        */
+       public static $trackedProperties = ['title', 'content'];
+       
+       /**
+        * @inheritDoc
+        */
+       public function getCurrentVersion(IVersionTrackerObject $object) {
+               $properties = $this->getTrackedProperties();
+               
+               /** @var Box $object */
+               $payload = [];
+               foreach ($object->getBoxContents() as $languageID => $boxContent) {
+                       $payload[$languageID] = [];
+                       foreach ($properties as $property) {
+                               $payload[$languageID][$property] = $boxContent->{$property};
+                       }
+               }
+               
+               return new VersionTrackerEntry(null, [
+                       'versionID' => 'current',
+                       'userID' => 0,
+                       'username' => '',
+                       'data' => $payload
+               ]);
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function getTrackedData(IVersionTrackerObject $object) {
+               $data = [];
+               
+               /** @var BoxVersionTracker $object */
+               foreach ($object->getContent() as $content) {
+                       $languageID = $content->languageID ?: 0;
+                       $data[$languageID] = [];
+                       
+                       foreach (static::$trackedProperties as $property) {
+                               $data[$languageID][$property] = $content->{$property};
+                       }
+               }
+               
+               return $data;
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function isI18n(IVersionTrackerObject $object) {
+               /** @var Box $object */
+               return $object->isMultilingual == 1;
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function revert(IVersionTrackerObject $object, VersionTrackerEntry $entry) {
+               /** @var BoxVersionTracker $object */
+               
+               // build the content data
+               $properties = $this->getTrackedProperties();
+               $content = [];
+               foreach ($object->getBoxContents() as $boxContent) {
+                       $content[$boxContent->languageID ?: 0] = $entry->getPayloadForProperties($properties, $boxContent->languageID ?: 0);
+               }
+               
+               $action = new BoxAction([$object->getDecoratedObject()], 'update', ['content' => $content, 'isRevert' => true]);
+               $action->executeAction();
+       }
+}
diff --git a/wcfsetup/install/files/lib/system/version/PageVersionTrackerProvider.class.php b/wcfsetup/install/files/lib/system/version/PageVersionTrackerProvider.class.php
new file mode 100644 (file)
index 0000000..eec3a99
--- /dev/null
@@ -0,0 +1,130 @@
+<?php
+namespace wcf\system\version;
+use wcf\data\page\Page;
+use wcf\data\page\PageAction;
+use wcf\data\page\PageList;
+use wcf\data\page\PageVersionTracker;
+use wcf\data\IVersionTrackerObject;
+
+/**
+ * Version tracker object type provider implementation for pages.
+ * 
+ * @author     Alexander Ebert
+ * @copyright  2001-2017 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    WoltLabSuite\Core\System\Version
+ */
+class PageVersionTrackerProvider extends AbstractVersionTrackerProvider {
+       /**
+        * @inheritDoc
+        */
+       public $activeMenuItem = 'wcf.acp.menu.link.cms.page.list';
+       
+       /**
+        * @inheritDoc
+        */
+       public $className = Page::class;
+       
+       /**
+        * @inheritDoc
+        */
+       public $decoratorClassName = PageVersionTracker::class;
+       
+       /**
+        * @inheritDoc
+        */
+       public $listClassName = PageList::class;
+       
+       /**
+        * @inheritDoc
+        */
+       public $permissionCanAccess = 'admin.content.cms.canManagePage';
+       
+       /**
+        * @inheritDoc
+        */
+       public static $defaultProperty = 'content';
+       
+       /**
+        * @inheritDoc
+        */
+       public static $propertyLabels = [
+               'content' => 'wcf.acp.page.content',
+               'customURL' => 'wcf.acp.page.customURL',
+               'metaDescription' => 'wcf.acp.page.metaDescription',
+               'metaKeywords' => 'wcf.acp.page.metaKeywords',
+               'title' => 'wcf.global.title'
+       ];
+       
+       /**
+        * @inheritDoc
+        */
+       public static $trackedProperties = ['title', 'content', 'metaDescription', 'metaKeywords', 'customURL'];
+       
+       /**
+        * @inheritDoc
+        */
+       public function getCurrentVersion(IVersionTrackerObject $object) {
+               $properties = $this->getTrackedProperties();
+               
+               /** @var Page $object */
+               $payload = [];
+               foreach ($object->getPageContents() as $languageID => $pageContent) {
+                       $payload[$languageID] = [];
+                       foreach ($properties as $property) {
+                               $payload[$languageID][$property] = $pageContent->{$property};
+                       }
+               }
+               
+               return new VersionTrackerEntry(null, [
+                       'versionID' => 'current',
+                       'userID' => 0,
+                       'username' => '',
+                       'data' => $payload
+               ]);
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function getTrackedData(IVersionTrackerObject $object) {
+               $data = [];
+               
+               /** @var PageVersionTracker $object */
+               foreach ($object->getContent() as $content) {
+                       $languageID = $content->languageID ?: 0;
+                       $data[$languageID] = [];
+                       
+                       foreach (static::$trackedProperties as $property) {
+                               $data[$languageID][$property] = $content->{$property};
+                       }
+               }
+               
+               return $data;
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function isI18n(IVersionTrackerObject $object) {
+               /** @var Page $object */
+               return $object->isMultilingual == 1;
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function revert(IVersionTrackerObject $object, VersionTrackerEntry $entry) {
+               /** @var PageVersionTracker $object */
+               
+               // build the content data
+               $properties = $this->getTrackedProperties();
+               $content = [];
+               foreach ($object->getPageContents() as $pageContent) {
+                       $content[$pageContent->languageID ?: 0] = $entry->getPayloadForProperties($properties, $pageContent->languageID ?: 0);
+               }
+               
+               $action = new PageAction([$object->getDecoratedObject()], 'update', ['content' => $content, 'isRevert' => true]);
+               $action->executeAction();
+       }
+}
index d4963f7f3c03351a6acfbdc96387024be29861fa..f707d8c0cb395204e365e6fcfd4a4c3ca6f1bbcf 100644 (file)
@@ -96,7 +96,7 @@
                <item name="wcf.acp.article.trash.confirmMessage"><![CDATA[{if LANGUAGE_USE_INFORMAL_VARIANT}Willst du{else}Wollen Sie{/if} {if $isArticleEdit|empty}den Artikel <span class="confirmationObject">{$article->getTitle()}</span>{else}diesen Artikel{/if} wirklich in den Papierkorb verschieben?]]></item>
                <item name="wcf.acp.article.trash.notice"><![CDATA[Dieser Artikel befindet sich im Papierkorb und wird gegenwärtig nicht angezeigt.]]></item>
                <item name="wcf.acp.article.views"><![CDATA[Zugriffe]]></item>
-               <item name="wcf.acp.article.lastVersion"><![CDATA[Es gibt <a href="{link controller='VersionTrackerList' objectType='com.woltlab.wcf.article' objectID=$article->articleID}{/link}">vorherige Versionen</a> dieses Artikels, die <a href="{link controller='VersionTrackerCompare' id=$lastVersion->versionID objectType='com.woltlab.wcf.article'}{/link}">letzte Änderung</a> erfolgte durch <a href="{link controller='UserEdit' id=$lastVersion->userID}{/link}">{$lastVersion->username}</a> ({@$lastVersion->time|time}).]]></item>
+               <item name="wcf.acp.article.lastVersion"><![CDATA[Es gibt <a href="{link controller='VersionTrackerList' objectType='com.woltlab.wcf.article' objectID=$article->articleID}{/link}">vorherige Versionen</a> dieses Artikels, die letzte Änderung erfolgte durch <a href="{link controller='UserEdit' id=$lastVersion->userID}{/link}">{$lastVersion->username}</a> ({@$lastVersion->time|time}).]]></item>
        </category>
        
        <category name="wcf.acp.attachment">
                <item name="wcf.acp.box.boxController.com.woltlab.wcf.userOnlineList"><![CDATA[Benutzer online]]></item>
                <item name="wcf.acp.box.boxController.com.woltlab.wcf.whoWasOnline"><![CDATA[Wer war online]]></item>
                <item name="wcf.acp.box.linkPageObjectID.error.invalid"><![CDATA[Die eingetragene ID ist ungültig.]]></item>
+               <item name="wcf.acp.box.lastVersion"><![CDATA[Es gibt <a href="{link controller='VersionTrackerList' objectType='com.woltlab.wcf.box' objectID=$box->boxID}{/link}">vorherige Versionen</a> dieser Box, die letzte Änderung erfolgte durch <a href="{link controller='UserEdit' id=$lastVersion->userID}{/link}">{$lastVersion->username}</a> ({@$lastVersion->time|time}).]]></item>
        </category>
        
        <category name="wcf.acp.cache">
@@ -1433,6 +1434,7 @@ GmbH=Gesellschaft mit beschränkter Haftung]]></item>
                <item name="wcf.acp.page.availableDuringOfflineMode"><![CDATA[Seite ist im Wartungsmodus aufrufbar]]></item>
                <item name="wcf.acp.page.allowSpidersToIndex"><![CDATA[Seite darf von Suchmaschinen-Robotern indiziert werden]]></item>
                <item name="wcf.acp.page.addPageToMainMenu"><![CDATA[Seite automatisch in das Hauptmenü aufnehmen]]></item>
+               <item name="wcf.acp.page.lastVersion"><![CDATA[Es gibt <a href="{link controller='VersionTrackerList' objectType='com.woltlab.wcf.page' objectID=$page->pageID}{/link}">vorherige Versionen</a> dieser Seite, die letzte Änderung erfolgte durch <a href="{link controller='UserEdit' id=$lastVersion->userID}{/link}">{$lastVersion->username}</a> ({@$lastVersion->time|time}).]]></item>
        </category>
        
        <category name="wcf.acp.paidSubscription">
@@ -2410,7 +2412,7 @@ Fehler sind beispielsweise:
                <item name="wcf.edit.headline.comparison"><![CDATA[Vergleich]]></item>
                <item name="wcf.edit.headline.old"><![CDATA[{if $oldID == 'current'}Aktuelle {/if}Version vom {@$old->time|plainTime} ({$old->username})]]></item>
                <item name="wcf.edit.headline.new"><![CDATA[{if $newID == 'current'}Aktuelle {/if}Version vom {@$new->time|plainTime} ({$new->username})]]></item>
-               <item name="wcf.edit.headline.newOrCurrent"><![CDATA[{if $newID == 'current'}Aktuelle Version{else}Version vom {@$new->time|plainTime}{/if} ({$new->username})]]></item>
+               <item name="wcf.edit.headline.newOrCurrent"><![CDATA[{if $newID == 'current'}Aktuelle Version{else}Version vom {@$new->time|plainTime}{/if}{if $new->username} ({$new->username}){/if}]]></item>
        </category>
        
        <category name="wcf.editor">
index ce114247063e261522493275a6b950efabe556c9..506d8bde80d33dd838263faf9e93680969bc0637 100644 (file)
@@ -96,6 +96,7 @@
                <item name="wcf.acp.article.trash.confirmMessage"><![CDATA[Do you really want to move {if $isArticleEdit|empty}the article <span class="confirmationObject">{$article->getTitle()}</span>{else}this article{/if} to the trash bin?]]></item>
                <item name="wcf.acp.article.trash.notice"><![CDATA[This article has been moved to the trash bin and is currently hidden from view.]]></item>
                <item name="wcf.acp.article.views"><![CDATA[Views]]></item>
+               <item name="wcf.acp.article.lastVersion"><![CDATA[There are <a href="{link controller='VersionTrackerList' objectType='com.woltlab.wcf.article' objectID=$article->articleID}{/link}">previous versions</a> of this article, the last change was by <a href="{link controller='UserEdit' id=$lastVersion->userID}{/link}">{$lastVersion->username}</a> ({@$lastVersion->time|time}).]]></item>
        </category>
        
        <category name="wcf.acp.attachment">
@@ -218,6 +219,7 @@ Examples for medium ID detection:
                <item name="wcf.acp.box.boxController.com.woltlab.wcf.userOnlineList"><![CDATA[Users Online]]></item>
                <item name="wcf.acp.box.boxController.com.woltlab.wcf.whoWasOnline"><![CDATA[Who Was Online]]></item>
                <item name="wcf.acp.box.linkPageObjectID.error.invalid"><![CDATA[ID is invalid.]]></item>
+               <item name="wcf.acp.box.lastVersion"><![CDATA[There are <a href="{link controller='VersionTrackerList' objectType='com.woltlab.wcf.box' objectID=$box->boxID}{/link}">previous versions</a> of this box, the last change was by <a href="{link controller='UserEdit' id=$lastVersion->userID}{/link}">{$lastVersion->username}</a> ({@$lastVersion->time|time}).]]></item>
        </category>
        
        <category name="wcf.acp.cache">
@@ -2361,7 +2363,7 @@ Errors are:
                <item name="wcf.edit.headline.comparison"><![CDATA[Comparison]]></item>
                <item name="wcf.edit.headline.old"><![CDATA[{if $oldID == 'current'}Current version{else}Version{/if} as of {@$old->time|plainTime} ({$old->username})]]></item>
                <item name="wcf.edit.headline.new"><![CDATA[{if $newID == 'current'}Current version{else}Version{/if} as of {@$new->time|plainTime} ({$new->username})]]></item>
-               <item name="wcf.edit.headline.newOrCurrent"><![CDATA[{if $newID == 'current'}Current version{else}Version as of {@$new->time|plainTime}{/if} ({$new->username})]]></item>
+               <item name="wcf.edit.headline.newOrCurrent"><![CDATA[{if $newID == 'current'}Current version{else}Version as of {@$new->time|plainTime}{/if}{if $new->username} ({$new->username}){/if}]]></item>
        </category>
        
        <category name="wcf.editor">
@@ -2914,6 +2916,7 @@ Errors are:
                <item name="wcf.acp.page.availableDuringOfflineMode"><![CDATA[Page is available during maintenance mode]]></item>
                <item name="wcf.acp.page.allowSpidersToIndex"><![CDATA[Allow search spiders to index this page]]></item>
                <item name="wcf.acp.page.addPageToMainMenu"><![CDATA[Add page to main menu]]></item>
+               <item name="wcf.acp.page.lastVersion"><![CDATA[There are <a href="{link controller='VersionTrackerList' objectType='com.woltlab.wcf.page' objectID=$page->pageID}{/link}">previous versions</a> of this page, the last change was by <a href="{link controller='UserEdit' id=$lastVersion->userID}{/link}">{$lastVersion->username}</a> ({@$lastVersion->time|time}).]]></item>
        </category>
        
        <category name="wcf.paidSubscription">