Migrate cronjob log list to grid view
authorMarcel Werk <burntime@woltlab.com>
Mon, 30 Sep 2024 14:09:17 +0000 (16:09 +0200)
committerMarcel Werk <burntime@woltlab.com>
Mon, 30 Sep 2024 14:09:17 +0000 (16:09 +0200)
wcfsetup/install/files/acp/js/WCF.ACP.js
wcfsetup/install/files/acp/templates/cronjobLogList.tpl
wcfsetup/install/files/lib/acp/page/CronjobLogListPage.class.php
wcfsetup/install/files/lib/system/view/grid/CronjobLogGridView.class.php [new file with mode: 0644]
wcfsetup/install/lang/de.xml
wcfsetup/install/lang/en.xml

index d559e5869dacf9b9236b9ea770e35f4f5668adbb..b2d23e5a848e27130fe9b121bf73c99f3344e097 100644 (file)
@@ -96,12 +96,6 @@ WCF.ACP.Cronjob.ExecutionHandler = Class.extend({
  * Handles the cronjob log list.
  */
 WCF.ACP.Cronjob.LogList = Class.extend({
-       /**
-        * error message dialog
-        * @var jQuery
-        */
-       _dialog: null,
-       
        /**
         * Initializes WCF.ACP.Cronjob.LogList object.
         */
@@ -123,29 +117,6 @@ WCF.ACP.Cronjob.LogList = Class.extend({
                                }
                        });
                });
-               
-               // bind event listeners to error badges
-               $('.jsCronjobError').click($.proxy(this._showError, this));
-       },
-       
-       /**
-        * Shows certain error message
-        * 
-        * @param       object          event
-        */
-       _showError: function(event) {
-               var $errorBadge = $(event.currentTarget);
-               
-               if (this._dialog === null) {
-                       this._dialog = $('<div style="overflow: auto"><pre>' + $errorBadge.next().html() + '</pre></div>').hide().appendTo(document.body);
-                       this._dialog.wcfDialog({
-                               title: WCF.Language.get('wcf.acp.cronjob.log.error.details')
-                       });
-               }
-               else {
-                       this._dialog.html('<pre>' + $errorBadge.next().html() + '</pre>');
-                       this._dialog.wcfDialog('open');
-               }
        }
 });
 
index af482fc14c24a1f67e4b3baf2c9cd50774158a77..980e7e5bf88b41a76068b151be8a56b02015b5ba 100644 (file)
 
 <header class="contentHeader">
        <div class="contentHeaderTitle">
-               <h1 class="contentTitle">{lang}wcf.acp.cronjob.log{/lang}{if $items} <span class="badge badgeInverse">{#$items}</span>{/if}</h1>
+               <h1 class="contentTitle">{lang}wcf.acp.cronjob.log{/lang}{if $gridView->countRows()} <span class="badge badgeInverse">{#$gridView->countRows()}</span>{/if}</h1>
        </div>
        
        {hascontent}
                <nav class="contentHeaderNavigation">
                        <ul>
                                {content}
-                                       {if $objects|count}
+                                       {if $gridView->countRows()}
                                                <li><a title="{lang}wcf.acp.cronjob.log.clear{/lang}" class="button jsCronjobLogDelete">{icon name='xmark'} <span>{lang}wcf.acp.cronjob.log.clear{/lang}</span></a></li>
                                        {/if}
                                        
        {/hascontent}
 </header>
 
-<form method="post" action="{link controller='CronjobLogList'}{/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>
-                                       <select name="cronjobID" aria-label="{lang}wcf.acp.cronjob.description{/lang}">
-                                               <option value="0">{lang}wcf.acp.cronjob.description{/lang}</option>
-                                               {foreach from=$availableCronjobs item=availableCronjob}
-                                                       <option value="{$availableCronjob->cronjobID}"{if $availableCronjob->cronjobID == $cronjobID} selected{/if}>{$availableCronjob->getDescription()}</option>
-                                               {/foreach}
-                                       </select>
-                               </dd>
-                       </dl>
-                       
-                       <dl class="col-xs-12 col-md-4">
-                               <dt></dt>
-                               <dd>
-                                       <select name="success" aria-label="{lang}wcf.acp.cronjob.log.status{/lang}">
-                                               <option value="-1">{lang}wcf.acp.cronjob.log.status{/lang}</option>
-                                               <option value="1"{if $success == 1} selected{/if}>{lang}wcf.acp.cronjob.log.success{/lang}</option>
-                                               <option value="0"{if $success == 0} selected{/if}>{lang}wcf.acp.cronjob.log.error{/lang}</option>
-                                       </select>
-                               </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>
-
-{assign var='linkParameters' value=''}
-{if $cronjobID}{capture append=linkParameters}&cronjobID={@$cronjobID}{/capture}{/if}
-{if $success != -1}{capture append=linkParameters}&success={@$success}{/capture}{/if}
-
-{hascontent}
-       <div class="paginationTop">
-               {content}{pages print=true assign=pagesLinks controller="CronjobLogList" link="pageNo=%d&sortField=$sortField&sortOrder=$sortOrder$linkParameters"}{/content}
-       </div>
-{/hascontent}
-
-{if $objects|count}
-       <div class="section tabularBox">
-               <table class="table">
-                       <thead>
-                               <tr>
-                                       <th class="columnID columnCronjobID{if $sortField == 'cronjobID'} active {@$sortOrder}{/if}"><a href="{link controller='CronjobLogList'}pageNo={@$pageNo}&sortField=cronjobID&sortOrder={if $sortField == 'cronjobID' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{@$linkParameters}{/link}">{lang}wcf.global.objectID{/lang}</a></th>
-                                       <th class="columnTitle columnClassName{if $sortField == 'className'} active {@$sortOrder}{/if}"><a href="{link controller='CronjobLogList'}pageNo={@$pageNo}&sortField=className&sortOrder={if $sortField == 'className' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{@$linkParameters}{/link}">{lang}wcf.acp.cronjob.className{/lang}</a></th>
-                                       <th class="columnText columnDescription{if $sortField == 'description'} active {@$sortOrder}{/if}"><a href="{link controller='CronjobLogList'}pageNo={@$pageNo}&sortField=description&sortOrder={if $sortField == 'description' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{@$linkParameters}{/link}">{lang}wcf.acp.cronjob.description{/lang}</a></th>
-                                       <th class="columnDate columnExecTime{if $sortField == 'execTime'} active {@$sortOrder}{/if}"><a href="{link controller='CronjobLogList'}pageNo={@$pageNo}&sortField=execTime&sortOrder={if $sortField == 'execTime' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{@$linkParameters}{/link}">{lang}wcf.acp.cronjob.log.execTime{/lang}</a></th>
-                                       <th class="columnText columnSuccess{if $sortField == 'success'} active {@$sortOrder}{/if}"><a href="{link controller='CronjobLogList'}pageNo={@$pageNo}&sortField=success&sortOrder={if $sortField == 'success' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{@$linkParameters}{/link}">{lang}wcf.acp.cronjob.log.status{/lang}</a></th>
-                                       
-                                       {event name='columnHeads'}
-                               </tr>
-                       </thead>
-                       
-                       <tbody>
-                               {foreach from=$objects item=cronjobLog}
-                                       <tr>
-                                               <td class="columnID columnCronjobID">{@$cronjobLog->cronjobID}</td>
-                                               <td class="columnTitle columnClassName">{$cronjobLog->className}</td>
-                                               <td class="columnText columnDescription">{$cronjobLog->description|phrase}</td>
-                                               <td class="columnDate columnExecTime">{if $cronjobLog->execTime}{@$cronjobLog->execTime|time}{/if}</td>
-                                               
-                                               <td class="columnText columnSuccess">
-                                                       {if $cronjobLog->success}
-                                                               <span class="badge green">{lang}wcf.acp.cronjob.log.success{/lang}</span>
-                                                       {elseif $cronjobLog->error}
-                                                               <a class="badge red jsTooltip jsCronjobError" title="{lang}wcf.acp.cronjob.log.error.showDetails{/lang}">{lang}wcf.acp.cronjob.log.error{/lang}</a>
-                                                               <span style="display: none">{$cronjobLog->error}</span>
-                                                       {/if}
-                                               </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}
-                                               {if $objects|count}
-                                                       <li><a title="{lang}wcf.acp.cronjob.log.clear{/lang}" class="button jsCronjobLogDelete">{icon name='xmark'} <span>{lang}wcf.acp.cronjob.log.clear{/lang}</span></a></li>
-                                               {/if}
-                                               
-                                               {event name='contentFooterNavigation'}
-                                       {/content}
-                               </ul>
-                       </nav>
-               {/hascontent}
-       </footer>
-{else}
-       <woltlab-core-notice type="info">{lang}wcf.global.noItems{/lang}</woltlab-core-notice>
-{/if}
+{unsafe:$gridView->render()}
 
 {include file='footer'}
index c3d6f0f92e5fd5d6e16ea80aaa85fb86a9eb7e2c..74548218b63d2874dc7c12d48c5c47fe6c9bdebe 100755 (executable)
@@ -2,22 +2,20 @@
 
 namespace wcf\acp\page;
 
-use wcf\data\cronjob\CronjobList;
-use wcf\data\cronjob\I18nCronjobList;
-use wcf\data\cronjob\log\CronjobLogList;
-use wcf\page\SortablePage;
-use wcf\system\WCF;
+use wcf\page\AbstractGridViewPage;
+use wcf\system\view\grid\AbstractGridView;
+use wcf\system\view\grid\CronjobLogGridView;
 
 /**
  * Shows cronjob log information.
  *
- * @author  Marcel Werk
- * @copyright   2001-2019 WoltLab GmbH
- * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @author      Marcel Werk
+ * @copyright   2001-2024 WoltLab GmbH
+ * @license     GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
  *
- * @property    CronjobLogList $objectList
+ * @property    CronjobLogGridView    $gridView
  */
-class CronjobLogListPage extends SortablePage
+class CronjobLogListPage extends AbstractGridViewPage
 {
     /**
      * @inheritDoc
@@ -29,114 +27,9 @@ class CronjobLogListPage extends SortablePage
      */
     public $neededPermissions = ['admin.management.canManageCronjob'];
 
-    /**
-     * @inheritDoc
-     */
-    public $itemsPerPage = 100;
-
-    /**
-     * @inheritDoc
-     */
-    public $defaultSortField = 'execTime';
-
-    /**
-     * @inheritDoc
-     */
-    public $defaultSortOrder = 'DESC';
-
-    /**
-     * @inheritDoc
-     */
-    public $validSortFields = ['cronjobID', 'className', 'description', 'execTime', 'success'];
-
-    /**
-     * @inheritDoc
-     */
-    public $objectListClassName = CronjobLogList::class;
-
-    /**
-     * @var int
-     */
-    public $cronjobID = 0;
-
-    /**
-     * @var int
-     */
-    public $success = -1;
-
-    /**
-     * @var CronjobList
-     */
-    public $availableCronjobs;
-
-    /**
-     * @inheritDoc
-     */
-    public function readParameters()
-    {
-        parent::readParameters();
-
-        if (!empty($_REQUEST['cronjobID'])) {
-            $this->cronjobID = \intval($_REQUEST['cronjobID']);
-        }
-        if (isset($_REQUEST['success'])) {
-            $this->success = \intval($_REQUEST['success']);
-        }
-    }
-
-    /**
-     * @inheritDoc
-     */
-    protected function initObjectList()
-    {
-        parent::initObjectList();
-
-        $this->objectList->sqlSelects = "cronjob.*";
-        $this->objectList->sqlJoins = "
-            LEFT JOIN   wcf" . WCF_N . "_cronjob cronjob
-            ON          cronjob.cronjobID = cronjob_log.cronjobID";
-
-        if ($this->cronjobID) {
-            $this->objectList->getConditionBuilder()->add('cronjob_log.cronjobID = ?', [$this->cronjobID]);
-        }
-        if ($this->success != -1) {
-            $this->objectList->getConditionBuilder()->add('cronjob_log.success = ?', [$this->success]);
-        }
-    }
-
-    /**
-     * @inheritDoc
-     */
-    protected function readObjects()
+    #[\Override]
+    protected function createGridViewController(): AbstractGridView
     {
-        $this->sqlOrderBy = (($this->sortField == 'className' || $this->sortField == 'description') ? 'cronjob.' : 'cronjob_log.') . $this->sortField . " " . $this->sortOrder;
-
-        parent::readObjects();
-    }
-
-    /**
-     * @inheritDoc
-     */
-    public function readData()
-    {
-        parent::readData();
-
-        $this->availableCronjobs = new I18nCronjobList();
-        $this->availableCronjobs->sqlOrderBy = 'descriptionI18n';
-        $this->availableCronjobs->readObjects();
-    }
-
-    /**
-     * @inheritDoc
-     */
-    public function assignVariables()
-    {
-        parent::assignVariables();
-
-        WCF::getTPL()->assign([
-            'cronjobID' => $this->cronjobID,
-            'success' => $this->success,
-            'availableCronjobs' => $this->availableCronjobs,
-        ]);
+        return new CronjobLogGridView();
     }
 }
diff --git a/wcfsetup/install/files/lib/system/view/grid/CronjobLogGridView.class.php b/wcfsetup/install/files/lib/system/view/grid/CronjobLogGridView.class.php
new file mode 100644 (file)
index 0000000..4060d49
--- /dev/null
@@ -0,0 +1,118 @@
+<?php
+
+namespace wcf\system\view\grid;
+
+use wcf\data\cronjob\Cronjob;
+use wcf\data\cronjob\I18nCronjobList;
+use wcf\data\cronjob\log\CronjobLog;
+use wcf\data\cronjob\log\CronjobLogList;
+use wcf\data\DatabaseObjectList;
+use wcf\system\view\grid\filter\SelectFilter;
+use wcf\system\view\grid\renderer\DefaultColumnRenderer;
+use wcf\system\view\grid\renderer\NumberColumnRenderer;
+use wcf\system\view\grid\renderer\TimeColumnRenderer;
+use wcf\system\view\grid\renderer\TitleColumnRenderer;
+use wcf\system\WCF;
+use wcf\util\StringUtil;
+
+final class CronjobLogGridView extends DatabaseObjectListGridView
+{
+    #[\Override]
+    protected function init(): void
+    {
+        $availableCronjobs = $this->getAvailableCronjobs();
+
+        $this->addColumns([
+            GridViewColumn::for('cronjobLogID')
+                ->label('wcf.global.objectID')
+                ->renderer(new NumberColumnRenderer())
+                ->sortable(),
+            GridViewColumn::for('cronjobID')
+                ->label('wcf.acp.cronjob')
+                ->sortable()
+                ->filter(new SelectFilter($availableCronjobs))
+                ->renderer([
+                    new class($availableCronjobs) extends TitleColumnRenderer {
+                        public function __construct(private readonly array $availableCronjobs) {}
+
+                        public function render(mixed $value, mixed $context = null): string
+                        {
+                            return $this->availableCronjobs[$value];
+                        }
+                    },
+                ]),
+            GridViewColumn::for('execTime')
+                ->label('wcf.acp.cronjob.log.execTime')
+                ->sortable()
+                // TODO: Add some time frame filter.
+                ->renderer(new TimeColumnRenderer()),
+            GridViewColumn::for('success')
+                ->label('wcf.acp.cronjob.log.status')
+                ->sortable()
+                ->filter(new SelectFilter([
+                    1 => 'wcf.acp.cronjob.log.success',
+                    0 => 'wcf.acp.cronjob.log.error',
+                ]))
+                ->renderer([
+                    new class extends DefaultColumnRenderer {
+                        public function render(mixed $value, mixed $context = null): string
+                        {
+                            \assert($context instanceof CronjobLog);
+
+                            if ($context->success) {
+                                return '<span class="badge green">' . WCF::getLanguage()->get('wcf.acp.cronjob.log.success') . '</span>';
+                            }
+                            if ($context->error) {
+                                $label = WCF::getLanguage()->get('wcf.acp.cronjob.log.error');
+                                $buttonId = 'cronjobLogErrorButton' . $context->cronjobLogID;
+                                $id = 'cronjobLogError' . $context->cronjobLogID;
+                                $error = StringUtil::encodeHTML($context->error);
+                                $dialogTitle = StringUtil::encodeJS(WCF::getLanguage()->get('wcf.acp.cronjob.log.error.details'));
+
+                                return <<<HTML
+                                    <button type="button" id="{$buttonId}" class="badge red">
+                                        {$label}
+                                    </button>
+                                    <template id="{$id}">{$error}</template>
+                                    <script data-relocate="true">
+                                        require(['WoltLabSuite/Core/Component/Dialog'], ({ dialogFactory }) => {
+                                            document.getElementById('{$buttonId}').addEventListener('click', () => {
+                                                const dialog = dialogFactory().fromId('{$id}').withoutControls();
+                                                dialog.show('{$dialogTitle}');
+                                            });
+                                        });
+                                    </script>
+                                    HTML;
+                            }
+
+                            return '';
+                        }
+                    },
+                ]),
+        ]);
+
+        $this->setSortField('execTime');
+        $this->setSortOrder('DESC');
+    }
+
+    #[\Override]
+    public function isAccessible(): bool
+    {
+        return WCF::getSession()->getPermission('admin.management.canManageCronjob');
+    }
+
+    #[\Override]
+    protected function createObjectList(): DatabaseObjectList
+    {
+        return new CronjobLogList();
+    }
+
+    private function getAvailableCronjobs(): array
+    {
+        $list = new I18nCronjobList();
+        $list->sqlOrderBy = 'descriptionI18n';
+        $list->readObjects();
+
+        return \array_map(fn(Cronjob $cronjob) => $cronjob->getDescription(), $list->getObjects());
+    }
+}
index d478f069b309cf4b57d30a6516254ad7fa820a60..0089665d32d7b06615a24da8aecd2ed54874d671 100644 (file)
                <item name="wcf.acp.content.provider.com.woltlab.wcf.reaction"><![CDATA[Reaktionen]]></item>
        </category>
        <category name="wcf.acp.cronjob">
+               <item name="wcf.acp.cronjob"><![CDATA[Cronjob]]></item>
                <item name="wcf.acp.cronjob.list"><![CDATA[Cronjobs]]></item>
                <item name="wcf.acp.cronjob.add"><![CDATA[Cronjob hinzufügen]]></item>
                <item name="wcf.acp.cronjob.subtitle"><![CDATA[„Zeitgesteuerte Aufgaben“]]></item>
index ca0f31be040c9c8367a3398542ee759fef3380e0..71d910723f008bcc451b7f402918f5ab9264521f 100644 (file)
                <item name="wcf.acp.captcha.question.incorrectSubmissions"><![CDATA[Incorrect Answers]]></item>
        </category>
        <category name="wcf.acp.cronjob">
+               <item name="wcf.acp.cronjob"><![CDATA[Cronjob]]></item>
                <item name="wcf.acp.cronjob.list"><![CDATA[Cronjobs]]></item>
                <item name="wcf.acp.cronjob.add"><![CDATA[Add Cronjob]]></item>
                <item name="wcf.acp.cronjob.subtitle"/>