<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}>{@' '|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'}
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.
* @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();
}
}
--- /dev/null
+<?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) {}
+}
--- /dev/null
+<?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(' ', 4) . $actionLabel;
+ }
+ }
+
+ return $availableActions;
+ }
+}
<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">
<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">