Global modification log list
authorAlexander Ebert <ebert@woltlab.com>
Thu, 12 Jul 2018 09:42:49 +0000 (11:42 +0200)
committerAlexander Ebert <ebert@woltlab.com>
Thu, 12 Jul 2018 09:42:49 +0000 (11:42 +0200)
See #2597

com.woltlab.wcf/acpMenu.xml
com.woltlab.wcf/objectTypeDefinition.xml
wcfsetup/install/files/acp/templates/modificationLogList.tpl [new file with mode: 0644]
wcfsetup/install/files/lib/acp/page/ModificationLogListPage.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/data/modification/log/IViewableModificationLog.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/data/object/type/ObjectType.class.php
wcfsetup/install/files/lib/system/log/modification/AbstractExtendedModificationLogHandler.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/log/modification/AbstractModificationLogHandler.class.php
wcfsetup/install/files/lib/system/log/modification/IExtendedModificationLogHandler.class.php [new file with mode: 0644]
wcfsetup/install/lang/de.xml
wcfsetup/install/lang/en.xml

index 027a420f036381cdbb06f3c0e914a72261d48c3b..c02b2c97159b4435e84125dbb3ecf720a371148c 100644 (file)
                                <permissions>admin.management.canViewLog</permissions>
                                <options>enable_user_authentication_failure</options>
                        </acpmenuitem>
+               
+                       <acpmenuitem name="wcf.acp.menu.link.log.modification">
+                               <controller>wcf\acp\page\ModificationLogListPage</controller>
+                               <parent>wcf.acp.menu.link.log</parent>
+                               <permissions>admin.management.canViewLog</permissions>
+                       </acpmenuitem>
                        <!-- /log -->
                
                <!-- /management -->
index 61a6f0227bd383818efd1008c83fca84b04913a4..59cb5051d42e50848955db4fb647f9499b86e6f9 100644 (file)
@@ -24,6 +24,7 @@
                
                <definition>
                        <name>com.woltlab.wcf.modifiableContent</name>
+                       <interfacename>wcf\system\log\modification\IExtendedModificationLogHandler</interfacename>
                </definition>
                
                <definition>
diff --git a/wcfsetup/install/files/acp/templates/modificationLogList.tpl b/wcfsetup/install/files/acp/templates/modificationLogList.tpl
new file mode 100644 (file)
index 0000000..9360f8b
--- /dev/null
@@ -0,0 +1,161 @@
+{include file='header' pageTitle='wcf.acp.modificationLog.list'}
+
+<header class="contentHeader">
+       <div class="contentHeaderTitle">
+               <h1 class="contentTitle">{lang}wcf.acp.modificationLog.list{/lang} <span class="badge badgeInverse">{#$items}</span></h1>
+       </div>
+       
+       {hascontent}
+               <nav class="contentHeaderNavigation">
+                       <ul>
+                               {content}{event name='contentHeaderNavigation'}{/content}
+                       </ul>
+               </nav>
+       {/hascontent}
+</header>
+
+{if !$unsupportedObjectTypes|empty}
+       <div class="warning">
+               <p>{lang}wcf.acp.modificationLog.unsupportedObjectTypes{/lang}</p>
+               <ul class="nativeList">
+                       {foreach from=$unsupportedObjectTypes item=unsupportedObjectType}
+                               <li><kbd>{$unsupportedObjectType->objectType}</kbd> ({$unsupportedObjectType->getPackage()})</li>
+                       {/foreach}
+               </ul>
+       </div>
+{/if}
+
+<form method="post" action="{link controller='ModificationLogList'}{/link}">
+       <section class="section">
+               <h2 class="sectionTitle">{lang}wcf.global.filter{/lang}</h2>
+               
+               <div class="row rowColGap formGrid">
+                       <dl class="col-xs-12 col-md-4">
+                               <dt></dt>
+                               <dd>
+                                       <input type="text" id="username" name="username" value="" placeholder="{lang}wcf.user.username{/lang}" class="long">
+                               </dd>
+                       </dl>
+                       
+                       <dl class="col-xs-12 col-md-4">
+                               <dt></dt>
+                               <dd>
+                                       <select name="packageID" id="packageID">
+                                               <option value="0"{if $packageID === 0} selected{/if}>{lang}wcf.acp.modificationLog.package.all{/lang}</option>
+                                               {foreach from=$packages item=package}
+                                                       <option value="{@$package->packageID}"{if $packageID == $package->packageID} selected{/if}>{$package}</option>
+                                               {/foreach}
+                                       </select>
+                               </dd>
+                       </dl>
+                       
+                       <dl class="col-xs-12 col-md-4">
+                               <dt></dt>
+                               <dd>
+                                       <select name="action" id="action">
+                                               <option value=""{if $action === ''} selected{/if}>{lang}wcf.acp.modificationLog.action.all{/lang}</option>
+                                               {if !$actions|empty}<option disabled>-----</option>{/if}
+                                               
+                                               {foreach from=$actions key=_packageID item=$availableActions}
+                                                       {assign var=_package value=$packages[$_packageID]}
+                                                       
+                                                       <optgroup label="{$_package}" data-package-id="{@$_package->packageID}">
+                                                               {foreach from=$availableActions key=actionName item=actionLabel}
+                                                                       <option value="{$actionName}"{if $action === $actionName} selected{/if}>{$actionLabel}</option>
+                                                               {/foreach}
+                                                       </optgroup>
+                                               {/foreach}
+                                       </select>
+                               </dd>
+                       </dl>
+                       
+                       <dl class="col-xs-12 col-md-4">
+                               <dt></dt>
+                               <dd>
+                                       <input type="date" name="afterDate" id="afterDate" value="{$afterDate}" placeholder="{lang}wcf.acp.modificationLog.time.afterDate{/lang}">
+                               </dd>
+                       </dl>
+                       
+                       <dl class="col-xs-12 col-md-4">
+                               <dt></dt>
+                               <dd>
+                                       <input type="date" name="beforeDate" id="beforeDate" value="{$beforeDate}" placeholder="{lang}wcf.acp.modificationLog.time.beforeDate{/lang}">
+                               </dd>
+                       </dl>
+                       
+                       {event name='filterFields'}
+               </div>
+               
+               <div class="formSubmit">
+                       <input type="submit" value="{lang}wcf.global.button.submit{/lang}" accesskey="s">
+                       {@SECURITY_TOKEN_INPUT_TAG}
+               </div>
+       </section>
+</form>
+
+{capture assign=pageParameters}{if $username}&username={$username}{/if}{if $packageID}&packageID={@$packageID}{/if}{if $action}&action={$action}{/if}{if $afterDate}&afterDate={$afterDate}{/if}{if $beforeDate}&beforeDate={$beforeDate}{/if}{/capture}
+{hascontent}
+       <div class="paginationTop">
+               {content}{pages print=true assign=pagesLinks controller="ModificationLogList" link="pageNo=%d&sortField=$sortField&sortOrder=$sortOrder$pageParameters"}{/content}
+       </div>
+{/hascontent}
+
+{if $logItems|count}
+       <div class="section tabularBox">
+               <table class="table">
+                       <thead>
+                               <tr>
+                                       <th class="columnLogID{if $sortField == 'logID'} active {@$sortOrder}{/if}"><a href="{link controller='ModificationLogList'}pageNo={@$pageNo}&sortField=logID&sortOrder={if $sortField == 'logID' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{$pageParameters}{/link}">{lang}wcf.global.objectID{/lang}</a></th>
+                                       <th class="columnText columnUsername{if $sortField == 'username'} active {@$sortOrder}{/if}"><a href="{link controller='ModificationLogList'}pageNo={@$pageNo}&sortField=username&sortOrder={if $sortField == 'username' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{$pageParameters}{/link}">{lang}wcf.user.username{/lang}</a></th>
+                                       <th class="columnText columnAction">{lang}wcf.acp.modificationLog.action{/lang}</th>
+                                       <th class="columnText columnAffectedObject">{lang}wcf.acp.modificationLog.affectedObject{/lang}</th>
+                                       <th class="columnDate columnTime{if $sortField == 'time'} active {@$sortOrder}{/if}"><a href="{link controller='ModificationLogList'}pageNo={@$pageNo}&sortField=time&sortOrder={if $sortField == 'time' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{$pageParameters}{/link}">{lang}wcf.global.date{/lang}</a></th>
+                                       
+                                       {event name='columnHeads'}
+                               </tr>
+                       </thead>
+                       
+                       <tbody>
+                               {foreach from=$logItems item=modificationLog}
+                                       {assign var=_objectType value=$objectTypes[$modificationLog->objectTypeID]}
+                                       
+                                       <tr>
+                                               <td class="columnID columnLogID">{@$modificationLog->logID}</td>
+                                               <td class="columnText columnUsername">{if $modificationLog->userID}<a href="{link controller='User' id=$modificationLog->userID}{/link}">{$modificationLog->username}</a>{else}{$modificationLog->username}{/if}</td>
+                                               <td class="columnText columnAction">{lang}wcf.acp.modificationLog.{$_objectType->objectType}.{$modificationLog->action}{/lang}</td>
+                                               <td class="columnText columnAffectedObject" title="{lang}wcf.acp.modificationLog.affectedObject.id{/lang}">
+                                                       {if $modificationLog->getAffectedObject()}
+                                                               <a href="{$modificationLog->getAffectedObject()->getLink()}">{$modificationLog->getAffectedObject()->getTitle()}</a>
+                                                       {else}
+                                                               <small>{lang}wcf.acp.modificationLog.affectedObject.unknown{/lang}</small>
+                                                       {/if}
+                                               </td>
+                                               <td class="columnDate columnTime">{@$modificationLog->time|time}</td>
+                                               
+                                               {event name='columns'}
+                                       </tr>
+                               {/foreach}
+                       </tbody>
+               </table>
+       </div>
+       
+       <footer class="contentFooter">
+               {hascontent}
+                       <div class="paginationBottom">
+                               {content}{@$pagesLinks}{/content}
+                       </div>
+               {/hascontent}
+               
+               {hascontent}
+                       <nav class="contentFooterNavigation">
+                               <ul>
+                                       {content}{event name='contentFooterNavigation'}{/content}
+                               </ul>
+                       </nav>
+               {/hascontent}
+       </footer>
+{else}
+       <p class="info">{lang}wcf.global.noItems{/lang}</p>
+{/if}
+
+{include file='footer'}
diff --git a/wcfsetup/install/files/lib/acp/page/ModificationLogListPage.class.php b/wcfsetup/install/files/lib/acp/page/ModificationLogListPage.class.php
new file mode 100644 (file)
index 0000000..82555b9
--- /dev/null
@@ -0,0 +1,316 @@
+<?php
+declare(strict_types=1);
+namespace wcf\acp\page;
+use wcf\data\modification\log\IViewableModificationLog;
+use wcf\data\modification\log\ModificationLogList;
+use wcf\data\object\type\ObjectType;
+use wcf\data\object\type\ObjectTypeCache;
+use wcf\data\package\Package;
+use wcf\page\SortablePage;
+use wcf\system\log\modification\IExtendedModificationLogHandler;
+use wcf\system\WCF;
+use wcf\util\StringUtil;
+
+/**
+ * Shows a list of modification log items.
+ *
+ * @author      Alexander Ebert
+ * @copyright   2001-2018 WoltLab GmbH
+ * @license     GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package     WoltLabSuite\Core\Acp\Page
+ *
+ * @property    ModificationLogList $objectList
+ * @since       3.2
+ */
+class ModificationLogListPage extends SortablePage {
+       /**
+        * filter by action
+        *
+        * @var string
+        */
+       public $action = '';
+       
+       /**
+        * list of available actions per package
+        *
+        * @var string[][]
+        */
+       public $actions = [];
+       
+       /**
+        * @inheritDoc
+        */
+       public $activeMenuItem = 'wcf.acp.menu.link.log.modification';
+       
+       /**
+        * filter by time
+        *
+        * @var string
+        */
+       public $afterDate = '';
+       
+       /**
+        * @var int[]
+        */
+       public $availableObjectTypeIDs = [];
+       
+       /**
+        * filter by time
+        *
+        * @var string
+        */
+       public $beforeDate = '';
+       
+       /**
+        * @inheritDoc
+        */
+       public $defaultSortField = 'time';
+       
+       /**
+        * @inheritDoc
+        */
+       public $defaultSortOrder = 'DESC';
+       
+       /**
+        * @inheritDoc
+        */
+       public $objectListClassName = ModificationLogList::class;
+       
+       /**
+        * @var IViewableModificationLog[]
+        */
+       public $logItems = [];
+       
+       /**
+        * @inheritDoc
+        */
+       public $neededPermissions = ['admin.management.canViewLog'];
+       
+       /**
+        * @var ObjectType[]
+        */
+       public $objectTypes = [];
+       
+       /**
+        * @var Package[]
+        */
+       public $packages = [];
+       
+       /**
+        * filter by package id
+        *
+        * @var int
+        */
+       public $packageID = 0;
+       
+       /**
+        * list of object types that are not implementing the new API
+        *
+        * @var ObjectType
+        */
+       public $unsupportedObjectTypes = [];
+       
+       /**
+        * filter by username
+        *
+        * @var string
+        */
+       public $username = '';
+       
+       /**
+        * @inheritDoc
+        */
+       public $validSortFields = [
+               'logID',
+               'username',
+               'time'
+       ];
+       
+       /**
+        * @inheritDoc
+        */
+       public function readParameters() {
+               parent::readParameters();
+               
+               $this->initObjectTypes();
+               
+               if (!empty($_REQUEST['action'])) {
+                       $this->action = StringUtil::trim($_REQUEST['action']);
+               }
+               if (!empty($_REQUEST['afterDate'])) {
+                       $this->afterDate = StringUtil::trim($_REQUEST['afterDate']);
+               }
+               if (!empty($_REQUEST['beforeDate'])) {
+                       $this->beforeDate = StringUtil::trim($_REQUEST['beforeDate']);
+               }
+               if (!empty($_REQUEST['packageID'])) {
+                       $this->packageID = intval($_REQUEST['packageID']);
+               }
+               if (!empty($_REQUEST['username'])) {
+                       $this->username = StringUtil::trim($_REQUEST['username']);
+               }
+       }
+       
+       protected function initObjectTypes() {
+               foreach (ObjectTypeCache::getInstance()->getObjectTypes('com.woltlab.wcf.modifiableContent') as $objectType) {
+                       /** @noinspection PhpUndefinedFieldInspection */
+                       if ($objectType->excludeFromLogList) {
+                               continue;
+                       }
+                       
+                       $this->objectTypes[$objectType->objectTypeID] = $objectType;
+                       
+                       /** @var IExtendedModificationLogHandler $processor */
+                       $processor = $objectType->getProcessor();
+                       if ($processor === null) {
+                               $this->unsupportedObjectTypes[] = $objectType;
+                       }
+                       else {
+                               $this->availableObjectTypeIDs[] = $objectType->objectTypeID;
+                               if (!isset($this->packages[$objectType->packageID])) {
+                                       $this->actions[$objectType->packageID] = [];
+                                       $this->packages[$objectType->packageID] = $objectType->getPackage();
+                               }
+                               
+                               foreach ($processor->getAvailableActions() as $action) {
+                                       $this->actions[$objectType->packageID]["{$objectType->objectType}-{$action}"] = WCF::getLanguage()->get("wcf.acp.modificationLog.{$objectType->objectType}.{$action}");
+                               }
+                       }
+               }
+               
+               foreach ($this->actions as &$actions) {
+                       asort($actions, SORT_NATURAL);
+               }
+               unset($actions);
+               
+               uasort($this->packages, function (Package $a, Package $b) {
+                       return strnatcasecmp($a->package, $b->package);
+               });
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function initObjectList() {
+               parent::initObjectList();
+               
+               if (!empty($this->availableObjectTypeIDs)) {
+                       $action = '';
+                       $objectTypeID = 0;
+                       if (preg_match('~^(?P<objectType>.+)\-(?P<action>[^\-]+)$~', $this->action, $matches)) {
+                               foreach ($this->objectTypes as $objectType) {
+                                       if ($objectType->objectType === $matches['objectType']) {
+                                               /** @var IExtendedModificationLogHandler $processor */
+                                               $processor = $objectType->getProcessor();
+                                               if ($processor !== null && in_array($matches['action'], $processor->getAvailableActions())) {
+                                                       $action = $matches['action'];
+                                                       $objectTypeID = $objectType->objectTypeID;
+                                               }
+                                               
+                                               break;
+                                       }
+                               }
+                       }
+                       
+                       if ($objectTypeID) {
+                               $this->objectList->getConditionBuilder()->add('modification_log.objectTypeID = ?', [$objectTypeID]);
+                               $this->objectList->getConditionBuilder()->add('modification_log.action = ?', [$action]);
+                       }
+                       else {
+                               if (isset($this->packages[$this->packageID])) {
+                                       $objectTypeIDs = [];
+                                       foreach ($this->objectTypes as $objectType) {
+                                               if ($objectType->packageID == $this->packageID) {
+                                                       $objectTypeIDs[] = $objectType->objectTypeID;
+                                               }
+                                       }
+                                       
+                                       $this->objectList->getConditionBuilder()->add('modification_log.objectTypeID IN (?)', [$objectTypeIDs]);
+                               }
+                               else {
+                                       $this->objectList->getConditionBuilder()->add('modification_log.objectTypeID IN (?)', [$this->availableObjectTypeIDs]);
+                               }
+                       }
+                       
+                       if (!empty($this->username)) {
+                               $this->objectList->getConditionBuilder()->add('modification_log.username LIKE ?', [addcslashes($this->username, '%') . '%']);
+                       }
+                       
+                       $afterDate = $beforeDate = 0;
+                       if (!empty($this->afterDate)) {
+                               $afterDate = intval(@strtotime($this->afterDate));
+                       }
+                       if (!empty($this->beforeDate)) {
+                               $beforeDate = intval(@strtotime($this->beforeDate));
+                       }
+                       
+                       if ($afterDate && $beforeDate) {
+                               $this->objectList->getConditionBuilder()->add('modification_log.time BETWEEN ? AND ?', [
+                                       $afterDate,
+                                       $beforeDate
+                               ]);
+                       }
+                       else {
+                               if ($afterDate) {
+                                       $this->objectList->getConditionBuilder()->add('modification_log.time > ?', [$afterDate]);
+                               }
+                               else if ($beforeDate) {
+                                       $this->objectList->getConditionBuilder()->add('modification_log.time < ?', [$beforeDate]);
+                               }
+                       }
+               }
+               else {
+                       $this->objectList->getConditionBuilder()->add('1=0');
+               }
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function readData() {
+               parent::readData();
+               
+               $itemsPerType = [];
+               foreach ($this->objectList as $modificationLog) {
+                       if (!isset($itemsPerType[$modificationLog->objectTypeID])) {
+                               $itemsPerType[$modificationLog->objectTypeID] = [];
+                       }
+                       
+                       $itemsPerType[$modificationLog->objectTypeID][] = $modificationLog;
+               }
+               
+               if (!empty($itemsPerType)) {
+                       foreach ($this->objectTypes as $objectType) {
+                               /** @var IExtendedModificationLogHandler $processor */
+                               $processor = $objectType->getProcessor();
+                               if ($processor === null) {
+                                       continue;
+                               }
+                               
+                               if (isset($itemsPerType[$objectType->objectTypeID])) {
+                                       $this->logItems = array_merge($this->logItems, $processor->processItems($itemsPerType[$objectType->objectTypeID]));
+                               }
+                       }
+               }
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function assignVariables() {
+               parent::assignVariables();
+               
+               WCF::getTPL()->assign([
+                       'action' => $this->action,
+                       'actions' => $this->actions,
+                       'afterDate' => $this->afterDate,
+                       'beforeDate' => $this->beforeDate,
+                       'logItems' => $this->logItems,
+                       'objectTypes' => $this->objectTypes,
+                       'packageID' => $this->packageID,
+                       'packages' => $this->packages,
+                       'unsupportedObjectTypes' => $this->unsupportedObjectTypes,
+                       'username' => $this->username,
+               ]);
+       }
+}
diff --git a/wcfsetup/install/files/lib/data/modification/log/IViewableModificationLog.class.php b/wcfsetup/install/files/lib/data/modification/log/IViewableModificationLog.class.php
new file mode 100644 (file)
index 0000000..3df40b5
--- /dev/null
@@ -0,0 +1,25 @@
+<?php
+declare(strict_types=1);
+namespace wcf\data\modification\log;
+use wcf\data\ITitledLinkObject;
+
+/**
+ * Common interface for modification log handlers that support item processing for
+ * display in the global modification log.
+ *
+ * @author      Alexander Ebert
+ * @copyright   2001-2018 WoltLab GmbH
+ * @license     GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package     WoltLabSuite\Core\System\Log\Modification
+ * @since       3.2
+ */
+interface IViewableModificationLog {
+       /**
+        * Returns the title of the affected object. If the object does not exist
+        * anymore, this method should return an empty string instead. (nullable
+        * requires PHP 7.1)
+        * 
+        * @return ITitledLinkObject|null
+        */
+       public function getAffectedObject();
+}
index 86cfe732b3327145f2c952f5e62e8fdb816f4cb9..dd3e5038abc98627db2837dc60008ecdc1194e4d 100644 (file)
@@ -2,6 +2,8 @@
 declare(strict_types=1);
 namespace wcf\data\object\type;
 use wcf\data\object\type\definition\ObjectTypeDefinition;
+use wcf\data\package\Package;
+use wcf\data\package\PackageCache;
 use wcf\data\ProcessibleDatabaseObject;
 use wcf\data\TDatabaseObjectOptions;
 use wcf\data\TDatabaseObjectPermissions;
@@ -114,4 +116,14 @@ class ObjectType extends ProcessibleDatabaseObject {
        public function getDefinition() {
                return ObjectTypeCache::getInstance()->getDefinition($this->definitionID);
        }
+       
+       /**
+        * Returns the package that this object type belongs to.
+        * 
+        * @return      Package
+        * @since       3.2
+        */
+       public function getPackage(): Package {
+               return PackageCache::getInstance()->getPackage($this->packageID);
+       }
 }
diff --git a/wcfsetup/install/files/lib/system/log/modification/AbstractExtendedModificationLogHandler.class.php b/wcfsetup/install/files/lib/system/log/modification/AbstractExtendedModificationLogHandler.class.php
new file mode 100644 (file)
index 0000000..978cf01
--- /dev/null
@@ -0,0 +1,16 @@
+<?php
+declare(strict_types=1);
+namespace wcf\system\log\modification;
+
+/**
+ * Abstract implementation of a modification log handler that can provide readable outputs
+ * for the global modification log in the ACP.
+ *
+ * @author      Alexander Ebert
+ * @copyright   2001-2018 WoltLab GmbH
+ * @license     GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package     WoltLabSuite\Core\System\Log\Modification
+ * @since       3.2
+ */
+abstract class AbstractExtendedModificationLogHandler extends AbstractModificationLogHandler implements IExtendedModificationLogHandler {
+}
index 3bd4561c65deb9b059c9d9d5d6a5e5ca33b06f2d..a7bc4ab13a1642ba1158db1082ba3cbb0b2c0379 100644 (file)
@@ -24,7 +24,7 @@ abstract class AbstractModificationLogHandler extends SingletonFactory {
         * modifiable content object type
         * @var ObjectType
         */
-       protected $objectType = null;
+       protected $objectType;
        
        /**
         * name of the modifiable content object type
diff --git a/wcfsetup/install/files/lib/system/log/modification/IExtendedModificationLogHandler.class.php b/wcfsetup/install/files/lib/system/log/modification/IExtendedModificationLogHandler.class.php
new file mode 100644 (file)
index 0000000..35eaf48
--- /dev/null
@@ -0,0 +1,33 @@
+<?php
+declare(strict_types=1);
+namespace wcf\system\log\modification;
+use wcf\data\modification\log\IViewableModificationLog;
+use wcf\data\modification\log\ModificationLog;
+
+/**
+ * Common interface for modification log handlers that support item processing for
+ * display in the global modification log.
+ *
+ * @author      Alexander Ebert
+ * @copyright   2001-2018 WoltLab GmbH
+ * @license     GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package     WoltLabSuite\Core\System\Log\Modification
+ * @since       3.2
+ */
+interface IExtendedModificationLogHandler {
+       /**
+        * Returns the list of possible actions for this object type.
+        *
+        * @return string[]
+        */
+       public function getAvailableActions(): array;
+       
+       /**
+        * Processes a list of items by converting them into IViewableModificationLog
+        * instances and pre-loading their data.
+        *
+        * @param ModificationLog[] $items
+        * @return IViewableModificationLog[]
+        */
+       public function processItems(array $items): array;
+}
index 27671b6a73a6e6419ed7cdcb2bba51a7eb8ad09d..07c41f895dac8ebb746b26714674355c1d82f680 100644 (file)
                <item name="wcf.acp.menu.link.devtools.project.add"><![CDATA[Projekt hinzufügen]]></item>
                <item name="wcf.acp.menu.link.devtools.project.list"><![CDATA[Projekte]]></item>
                <item name="wcf.acp.menu.link.devtools.notificationTest"><![CDATA[Benachrichtigungstest]]></item>
+               <item name="wcf.acp.menu.link.log.modification"><![CDATA[Globales Veränderungsprotokoll]]></item>
+       </category>
+       
+       <category name="wcf.acp.modificationLog">
+               <item name="wcf.acp.modificationLog.list"><![CDATA[Globales Veränderungsprotokoll]]></item>
+               <item name="wcf.acp.modificationLog.action"><![CDATA[Aktion]]></item>
+               <item name="wcf.acp.modificationLog.action.all"><![CDATA[Alle Aktion]]></item>
+               <item name="wcf.acp.modificationLog.affectedObject"><![CDATA[Betroffenes Objekt]]></item>
+               <item name="wcf.acp.modificationLog.affectedObject.id"><![CDATA[ID: {@$modificationLog->objectID}]]></item>
+               <item name="wcf.acp.modificationLog.affectedObject.unknown"><![CDATA[(Existiert nicht mehr)]]></item>
+               <item name="wcf.acp.modificationLog.package.all"><![CDATA[Alle Quellen]]></item>
+               <item name="wcf.acp.modificationLog.time.afterDate"><![CDATA[Ab dem Zeitpunkt]]></item>
+               <item name="wcf.acp.modificationLog.time.beforeDate"><![CDATA[Vor dem Zeitpunkt]]></item>
+               <item name="wcf.acp.modificationLog.unsupportedObjectTypes"><![CDATA[Einige Typen werden aktuell nicht unterstützt und müssen zuvor vom Hersteller angepasst werden.]]></item>
        </category>
        
        <category name="wcf.acp.notice">
index 96712a256238da94cda772635c503966c4ac9833..af2ee72c66ee4c7c80c76681b24cb6ce620ebbd1 100644 (file)
                <item name="wcf.acp.menu.link.devtools.project.add"><![CDATA[Add Project]]></item>
                <item name="wcf.acp.menu.link.devtools.project.list"><![CDATA[Projects]]></item>
                <item name="wcf.acp.menu.link.devtools.notificationTest"><![CDATA[Notification Test]]></item>
+               <item name="wcf.acp.menu.link.log.modification"><![CDATA[Global Modification Log]]></item>
+       </category>
+       
+       <category name="wcf.acp.modificationLog">
+               <item name="wcf.acp.modificationLog.list"><![CDATA[Global Modification Log]]></item>
+               <item name="wcf.acp.modificationLog.action"><![CDATA[Action]]></item>
+               <item name="wcf.acp.modificationLog.action.all"><![CDATA[All Actions]]></item>
+               <item name="wcf.acp.modificationLog.affectedObject"><![CDATA[Affected Object]]></item>
+               <item name="wcf.acp.modificationLog.affectedObject.id"><![CDATA[ID: {@$modificationLog->objectID}]]></item>
+               <item name="wcf.acp.modificationLog.affectedObject.unknown"><![CDATA[(Does not exist anymore)]]></item>
+               <item name="wcf.acp.modificationLog.package.all"><![CDATA[All Sources]]></item>
+               <item name="wcf.acp.modificationLog.time.afterDate"><![CDATA[After the date]]></item>
+               <item name="wcf.acp.modificationLog.time.beforeDate"><![CDATA[Before the date]]></item>
+               <item name="wcf.acp.modificationLog.unsupportedObjectTypes"><![CDATA[Some types are currently not supported and require changes to be made by the vendor.]]></item>
        </category>
        
        <category name="wcf.acp.notice">