<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>
<definition>
<name>com.woltlab.wcf.versionTracker.objectType</name>
- <interfacename>wcf\system\version\IVersionTrackerObjectTypeProvider</interfacename>
+ <interfacename>wcf\system\version\IVersionTrackerProvider</interfacename>
</definition>
<definition>
<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">
<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">
{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>
use wcf\system\condition\ConditionHandler;
use wcf\system\exception\IllegalLinkException;
use wcf\system\language\LanguageFactory;
+use wcf\system\version\VersionTracker;
use wcf\system\WCF;
/**
WCF::getTPL()->assign([
'action' => 'edit',
'boxID' => $this->boxID,
- 'box' => $this->box
+ 'box' => $this->box,
+ 'lastVersion' => VersionTracker::getInstance()->getLastVersion('com.woltlab.wcf.box', $this->boxID)
]);
}
}
use wcf\system\exception\IllegalLinkException;
use wcf\system\exception\UserInputException;
use wcf\system\language\LanguageFactory;
+use wcf\system\version\VersionTracker;
use wcf\system\WCF;
/**
WCF::getTPL()->assign([
'action' => 'edit',
'pageID' => $this->pageID,
- 'page' => $this->page
+ 'page' => $this->page,
+ 'lastVersion' => VersionTracker::getInstance()->getLastVersion('com.woltlab.wcf.page', $this->pageID)
]);
}
}
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;
/**
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 */
$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 {
'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
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);
+ }
}
}
--- /dev/null
+<?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]);
+ }
+}
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;
/**
*/
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 */
'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 {
'customURL' => $content['customURL']
]);
$pageContentEditor = new PageContentEditor($pageContent);
+
+ $versionData[] = $pageContent;
+ $hasChanges = true;
}
// update search index
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);
+ }
}
}
--- /dev/null
+<?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]);
+ }
+}
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 = [];
--- /dev/null
+<?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();
+ }
+}
--- /dev/null
+<?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();
+ }
+}
<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">
<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">
<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">
<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">
<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">
<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">
<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">