Migrate modification log to grid view
authorMarcel Werk <burntime@woltlab.com>
Tue, 10 Dec 2024 15:33:37 +0000 (16:33 +0100)
committerMarcel Werk <burntime@woltlab.com>
Tue, 10 Dec 2024 15:33:37 +0000 (16:33 +0100)
wcfsetup/install/files/acp/templates/modificationLogList.tpl
wcfsetup/install/files/lib/acp/page/ModificationLogListPage.class.php
wcfsetup/install/files/lib/event/gridView/ModificationLogGridViewInitialized.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/gridView/ModificationLogGridView.class.php [new file with mode: 0644]
wcfsetup/install/lang/de.xml
wcfsetup/install/lang/en.xml

index 55321fdf810fbf946e7528e86d689d8b4148ba81..b139caf6eb3c86dc543d62f76cf9a6c9f3b71408 100644 (file)
@@ -2,7 +2,7 @@
 
 <header class="contentHeader">
        <div class="contentHeaderTitle">
-               <h1 class="contentTitle">{lang}wcf.acp.modificationLog.list{/lang} <span class="badge badgeInverse">{#$items}</span></h1>
+               <h1 class="contentTitle">{lang}wcf.acp.modificationLog.list{/lang} <span class="badge badgeInverse">{#$gridView->countRows()}</span></h1>
        </div>
        
        {hascontent}
        {/hascontent}
 </header>
 
-{if !$unsupportedObjectTypes|empty}
-       <woltlab-core-notice type="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>
-       </woltlab-core-notice>
-{/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-8">
-                               <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]}
-                                                       
-                                                       <option value="{$_package->packageID}"{if $action == $_package->packageID} selected{/if}>{lang package=$_package}wcf.acp.modificationLog.action.allPackageActions{/lang}</option>
-                                                       {foreach from=$availableActions key=actionName item=actionLabel}
-                                                               <option value="{$actionName}"{if $action === $actionName} selected{/if}>{@'&nbsp;'|str_repeat:4}{$actionLabel}</option>
-                                                       {/foreach}
-                                               {/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">
-                       {csrfToken}
-               </div>
-       </section>
-</form>
-
-{capture assign=pageParameters}{if $username}&username={$username}{/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 title=$modificationLog->username forceFrontend=true}{/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}
-       <woltlab-core-notice type="info">{lang}wcf.global.noItems{/lang}</woltlab-core-notice>
-{/if}
+<div class="section">
+       {unsafe:$gridView->render()}
+</div>
 
 {include file='footer'}
index d9ec34631c46f3bbd842f17e41eae888c3b92bd2..4f6bf9e6ba84068c87427ef9b7695233c7843774 100644 (file)
@@ -2,16 +2,9 @@
 
 namespace wcf\acp\page;
 
-use wcf\data\DatabaseObject;
-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;
+use wcf\page\AbstractGridViewPage;
+use wcf\system\gridView\AbstractGridView;
+use wcf\system\gridView\ModificationLogGridView;
 
 /**
  * Shows a list of modification log items.
@@ -20,308 +13,24 @@ use wcf\util\StringUtil;
  * @copyright   2001-2019 WoltLab GmbH
  * @license     GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
  *
- * @property    ModificationLogList $objectList
+ * @property    ModificationLogGridView    $gridView
  * @since       5.2
  */
-class ModificationLogListPage extends SortablePage
+class ModificationLogListPage extends AbstractGridViewPage
 {
-    /**
-     * 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['username'])) {
-            $this->username = StringUtil::trim($_REQUEST['username']);
-        }
-    }
-
-    protected function initObjectTypes()
-    {
-        foreach (ObjectTypeCache::getInstance()->getObjectTypes('com.woltlab.wcf.modifiableContent') as $objectType) {
-            $this->objectTypes[$objectType->objectTypeID] = $objectType;
-
-            /** @var IExtendedModificationLogHandler $processor */
-            $processor = $objectType->getProcessor();
-            if ($processor === null) {
-                $this->unsupportedObjectTypes[] = $objectType;
-            } elseif ($processor->includeInLogList()) {
-                $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, static function (Package $a, Package $b) {
-            return \strnatcasecmp($a->package, $b->package);
-        });
-    }
-
-    /**
-     * @inheritDoc
-     */
-    public function initObjectList()
-    {
-        parent::initObjectList();
-
-        if (!empty($this->availableObjectTypeIDs)) {
-            $action = '';
-            $objectTypeID = 0;
-            $packageID = 0;
-
-            // an integer signals all actions from the package with the relevant id
-            if (\preg_match('/^[0-9]+$/', $this->action)) {
-                $packageID = $this->action;
-            } elseif (\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[$packageID])) {
-                    $objectTypeIDs = [];
-                    foreach ($this->objectTypes as $objectType) {
-                        if ($objectType->packageID == $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]);
-                } elseif ($beforeDate) {
-                    $this->objectList->getConditionBuilder()->add('modification_log.time < ?', [$beforeDate]);
-                }
-            }
-        } else {
-            $this->objectList->getConditionBuilder()->add('1=0');
-        }
-    }
-
-    /**
-     * @inheritDoc
-     */
-    public function readData()
+    #[\Override]
+    protected function createGridViewController(): AbstractGridView
     {
-        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])
-                    );
-                }
-            }
-        }
-
-        DatabaseObject::sort($this->logItems, $this->sortField, $this->sortOrder);
-    }
-
-    /**
-     * @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,
-            'packages' => $this->packages,
-            'unsupportedObjectTypes' => $this->unsupportedObjectTypes,
-            'username' => $this->username,
-        ]);
+        return new ModificationLogGridView();
     }
 }
diff --git a/wcfsetup/install/files/lib/event/gridView/ModificationLogGridViewInitialized.class.php b/wcfsetup/install/files/lib/event/gridView/ModificationLogGridViewInitialized.class.php
new file mode 100644 (file)
index 0000000..5333140
--- /dev/null
@@ -0,0 +1,19 @@
+<?php
+
+namespace wcf\event\gridView;
+
+use wcf\event\IPsr14Event;
+use wcf\system\gridView\ModificationLogGridView;
+
+/**
+ * Indicates that the modification log grid view has been initialized.
+ *
+ * @author      Marcel Werk
+ * @copyright   2001-2024 WoltLab GmbH
+ * @license     GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @since       6.2
+ */
+final class ModificationLogGridViewInitialized implements IPsr14Event
+{
+    public function __construct(public readonly ModificationLogGridView $gridView) {}
+}
diff --git a/wcfsetup/install/files/lib/system/gridView/ModificationLogGridView.class.php b/wcfsetup/install/files/lib/system/gridView/ModificationLogGridView.class.php
new file mode 100644 (file)
index 0000000..82e1227
--- /dev/null
@@ -0,0 +1,269 @@
+<?php
+
+namespace wcf\system\gridView;
+
+use LogicException;
+use wcf\data\DatabaseObject;
+use wcf\data\DatabaseObjectDecorator;
+use wcf\data\DatabaseObjectList;
+use wcf\data\modification\log\IViewableModificationLog;
+use wcf\data\modification\log\ModificationLog;
+use wcf\data\modification\log\ModificationLogList;
+use wcf\data\object\type\ObjectTypeCache;
+use wcf\data\package\Package;
+use wcf\event\gridView\ModificationLogGridViewInitialized;
+use wcf\event\IPsr14Event;
+use wcf\system\form\builder\field\AbstractFormField;
+use wcf\system\form\builder\field\SelectFormField;
+use wcf\system\gridView\filter\IGridViewFilter;
+use wcf\system\gridView\filter\TextFilter;
+use wcf\system\gridView\filter\TimeFilter;
+use wcf\system\gridView\renderer\DefaultColumnRenderer;
+use wcf\system\gridView\renderer\ILinkColumnRenderer;
+use wcf\system\gridView\renderer\ObjectIdColumnRenderer;
+use wcf\system\gridView\renderer\TimeColumnRenderer;
+use wcf\system\gridView\renderer\UserLinkColumnRenderer;
+use wcf\system\log\modification\IExtendedModificationLogHandler;
+use wcf\system\WCF;
+use wcf\util\StringUtil;
+
+/**
+ * Grid view for the list of all modification log items.
+ *
+ * @author      Marcel Werk
+ * @copyright   2001-2024 WoltLab GmbH
+ * @license     GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @since       6.2
+ */
+final class ModificationLogGridView extends DatabaseObjectListGridView
+{
+    /**
+     * @var IViewableModificationLog[]
+     */
+    private array $logItems;
+
+    public function __construct()
+    {
+        $this->addColumns([
+            GridViewColumn::for('logID')
+                ->label('wcf.global.objectID')
+                ->renderer(new ObjectIdColumnRenderer())
+                ->sortable(),
+            GridViewColumn::for('userID')
+                ->label('wcf.user.username')
+                ->sortable()
+                ->sortById('username')
+                ->renderer(new UserLinkColumnRenderer())
+                ->filter(new TextFilter('username')),
+            GridViewColumn::for('action')
+                ->label('wcf.acp.modificationLog.action')
+                ->renderer([
+                    new class extends DefaultColumnRenderer {
+                        #[\Override]
+                        public function render(mixed $value, mixed $context = null): string
+                        {
+                            \assert($context instanceof DatabaseObjectDecorator);
+                            $log = $context->getDecoratedObject();
+                            \assert($log instanceof ModificationLog);
+                            $objectType = ObjectTypeCache::getInstance()->getObjectType($log->objectTypeID);
+                            if (!$objectType) {
+                                return '';
+                            }
+
+                            return WCF::getLanguage()->get(
+                                'wcf.acp.modificationLog.' . $objectType->objectType . '.' . $log->action
+                            );
+                        }
+                    },
+                ])
+                ->filter(
+                    new class($this->getAvailableActions()) implements IGridViewFilter {
+                        public function __construct(private readonly array $options) {}
+
+                        #[\Override]
+                        public function getFormField(string $id, string $label): AbstractFormField
+                        {
+                            return SelectFormField::create($id)
+                                ->label($label)
+                                ->options($this->options, false, false);
+                        }
+
+                        #[\Override]
+                        public function applyFilter(DatabaseObjectList $list, string $id, string $value): void
+                        {
+                            if (\is_numeric($value)) {
+                                $list->getConditionBuilder()->add(
+                                    "objectTypeID IN (SELECT objectTypeID FROM wcf1_object_type WHERE packageID = ?)",
+                                    [$value]
+                                );
+                            } else if (\preg_match('~^(?P<objectType>.+)\-(?P<action>[^\-]+)$~', $value, $matches)) {
+                                $objectType = ObjectTypeCache::getInstance()->getObjectTypeByName(
+                                    'com.woltlab.wcf.modifiableContent',
+                                    $matches['objectType']
+                                );
+                                if (!$objectType) {
+                                    return;
+                                }
+
+                                $list->getConditionBuilder()->add(
+                                    "objectTypeID = ? AND action = ?",
+                                    [$objectType->objectTypeID, $matches['action']]
+                                );
+                            }
+                        }
+
+                        #[\Override]
+                        public function matches(string $filterValue, string $rowValue): bool
+                        {
+                            throw new LogicException('unreachable');
+                        }
+
+                        #[\Override]
+                        public function renderValue(string $value): string
+                        {
+                            if (\is_numeric($value)) {
+                                return WCF::getLanguage()->get($this->options[$value]);
+                            }
+
+                            return \substr(WCF::getLanguage()->get($this->options[$value]), 24);
+                        }
+                    }
+                ),
+            GridViewColumn::for('affectedObject')
+                ->label('wcf.acp.modificationLog.affectedObject')
+                ->renderer([
+                    new class extends DefaultColumnRenderer implements ILinkColumnRenderer {
+                        #[\Override]
+                        public function render(mixed $value, mixed $context = null): string
+                        {
+                            \assert($context instanceof IViewableModificationLog);
+                            if ($context->getAffectedObject() === null) {
+                                return WCF::getLanguage()->get('wcf.acp.modificationLog.affectedObject.unknown');
+                            }
+
+                            return \sprintf(
+                                '<a href="%s">%s</a>',
+                                StringUtil::encodeHTML($context->getAffectedObject()->getLink()),
+                                StringUtil::encodeHTML($context->getAffectedObject()->getTitle())
+                            );
+                        }
+                    },
+                ]),
+            GridViewColumn::for('time')
+                ->label('wcf.global.date')
+                ->sortable()
+                ->renderer(new TimeColumnRenderer())
+                ->filter(new TimeFilter()),
+        ]);
+
+        $this->setSortField('time');
+        $this->setSortOrder('DESC');
+    }
+
+    #[\Override]
+    public function isAccessible(): bool
+    {
+        return WCF::getSession()->getPermission('admin.management.canViewLog');
+    }
+
+    #[\Override]
+    protected function createObjectList(): DatabaseObjectList
+    {
+        return new ModificationLogList();
+    }
+
+    #[\Override]
+    public function getRows(): array
+    {
+        if (!isset($this->logItems)) {
+            $this->logItems = [];
+            $this->getObjectList()->readObjects();
+
+            $itemsPerType = [];
+            foreach ($this->getObjectList() as $modificationLog) {
+                if (!isset($itemsPerType[$modificationLog->objectTypeID])) {
+                    $itemsPerType[$modificationLog->objectTypeID] = [];
+                }
+
+                $itemsPerType[$modificationLog->objectTypeID][] = $modificationLog;
+            }
+
+            if (!empty($itemsPerType)) {
+                foreach ($itemsPerType as $objectTypeID => $items) {
+                    $objectType = ObjectTypeCache::getInstance()->getObjectType($objectTypeID);
+                    if (!$objectType) {
+                        continue;
+                    }
+                    $processor = $objectType->getProcessor();
+                    if (!$processor) {
+                        continue;
+                    }
+                    \assert($processor instanceof IExtendedModificationLogHandler);
+
+                    $this->logItems = \array_merge(
+                        $this->logItems,
+                        $processor->processItems($items)
+                    );
+                }
+            }
+
+            DatabaseObject::sort($this->logItems, $this->getSortField(), $this->getSortOrder());
+        }
+
+        return $this->logItems;
+    }
+
+    #[\Override]
+    protected function getInitializedEvent(): ?IPsr14Event
+    {
+        return new ModificationLogGridViewInitialized($this);
+    }
+
+    private function getAvailableActions(): array
+    {
+        $packages = $actions = $availableActions = [];
+
+        foreach (ObjectTypeCache::getInstance()->getObjectTypes('com.woltlab.wcf.modifiableContent') as $objectType) {
+            $processor = $objectType->getProcessor();
+            if ($processor === null) {
+                continue;
+            }
+            \assert($processor instanceof IExtendedModificationLogHandler);
+
+            if (!$processor->includeInLogList()) {
+                continue;
+            }
+
+            if (!isset($packages[$objectType->packageID])) {
+                $actions[$objectType->packageID] = [];
+                $packages[$objectType->packageID] = $objectType->getPackage();
+            }
+
+            foreach ($processor->getAvailableActions() as $action) {
+                $actions[$objectType->packageID]["{$objectType->objectType}-{$action}"]
+                    = WCF::getLanguage()->get("wcf.acp.modificationLog.{$objectType->objectType}.{$action}");
+            }
+        }
+
+        foreach ($actions as &$actionsPerPackage) {
+            \asort($actionsPerPackage, \SORT_NATURAL);
+        }
+        \uasort($packages, static function (Package $a, Package $b) {
+            return \strnatcasecmp($a->package, $b->package);
+        });
+
+        foreach ($packages as $package) {
+            $availableActions[$package->packageID]
+                = WCF::getLanguage()->getDynamicVariable(
+                    'wcf.acp.modificationLog.action.allPackageActions',
+                    ['package' => $package]
+                );
+
+            foreach ($actions[$package->packageID] as $actionName => $actionLabel) {
+                $availableActions[$actionName] = \str_repeat('&nbsp;', 4) . $actionLabel;
+            }
+        }
+
+        return $availableActions;
+    }
+}
index 19a6b14cac1d4a3de94e04c6bbdbf4ebfdb1b399..f50f9e75f3303ff7710f91dd0fd70df7c474897b 100644 (file)
@@ -1254,14 +1254,8 @@ Die Entwickler-Lizenz gestattet ausschließlich den Einsatz während der Entwick
        <category name="wcf.acp.modificationLog">
                <item name="wcf.acp.modificationLog.list"><![CDATA[Globales Änderungsprotokoll]]></item>
                <item name="wcf.acp.modificationLog.action"><![CDATA[Aktion]]></item>
-               <item name="wcf.acp.modificationLog.action.all"><![CDATA[Alle Aktionen]]></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>
                <item name="wcf.acp.modificationLog.action.allPackageActions"><![CDATA[Alle Aktionen aus „{$package->getName()}“]]></item>
        </category>
        <category name="wcf.acp.notice">
index c32f92617a0cd9fe878878a5b07de5f8f4e67fdc..54dfc2bae3c6903573534a03bf1e5f5d57dd64ef 100644 (file)
@@ -1232,14 +1232,8 @@ The developer license permits exclusively the use during the development as well
        <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>
                <item name="wcf.acp.modificationLog.action.allPackageActions"><![CDATA[All Actions from “{$package->getName()}”]]></item>
        </category>
        <category name="wcf.acp.notice">