[FEATURE] Add filters to BE module 87/50087/3
authorFrancois Suter <francois@typo3.org>
Tue, 4 Oct 2016 10:36:07 +0000 (12:36 +0200)
committerFrancois Suter <francois@typo3.org>
Tue, 4 Oct 2016 10:52:29 +0000 (12:52 +0200)
Restore column filters in the new BE module.

Change-Id: I61b03a9ec3cb6c19efb3d0dbf0300fd8f38c03ed
Resolves: #76971
Releases: 3.0
Reviewed-on: https://review.typo3.org/50087
Reviewed-by: Francois Suter <francois@typo3.org>
Tested-by: Francois Suter <francois@typo3.org>
ChangeLog
Classes/Controller/ListModuleController.php
Classes/Domain/Repository/EntryRepository.php
Resources/Private/Language/locallang.xlf
Resources/Private/Templates/ListModule/Index.html
Resources/Public/JavaScript/ListModule.js
Tests/Functional/Domain/Repository/EntryRepositoryTest.php

index 4b2ac57..09e0db2 100755 (executable)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2016-09-04 Francois Suter <typo3@cobweb.ch>
+
+       * Restored column filters in new backend module, resolves #76971
+
 2016-07-09 Francois Suter <typo3@cobweb.ch>
 
        * New backend module, resolves #76970
index 1b6d922..64769ec 100644 (file)
@@ -120,6 +120,16 @@ class ListModuleController extends ActionController
                 'DevLog',
                 $this->extensionConfiguration->toArray()
         );
+        $pageRenderer->addInlineLanguageLabelArray(
+                array(
+                        'severity-1' => 'LLL:EXT:devlog/Resources/Private/Language/locallang.xlf:severity_ok',
+                        'severity0' => 'LLL:EXT:devlog/Resources/Private/Language/locallang.xlf:severity_info',
+                        'severity1' => 'LLL:EXT:devlog/Resources/Private/Language/locallang.xlf:severity_notification',
+                        'severity2' => 'LLL:EXT:devlog/Resources/Private/Language/locallang.xlf:severity_warning',
+                        'severity3' => 'LLL:EXT:devlog/Resources/Private/Language/locallang.xlf:severity_error',
+                ),
+                true
+        );
         // Add open in new window button
         $newWindowIcon = $this->view->getModuleTemplate()->getIconFactory()->getIcon('actions-window-open', Icon::SIZE_SMALL);
         $newWindowButton = GeneralUtility::makeInstance(ExtendedLinkButton::class);
index bc0ca34..28276f2 100644 (file)
@@ -206,7 +206,7 @@ class EntryRepository implements SingletonInterface
                 if ($pid > 0 && isset($pageInformationCache[$pid])) {
                     $entries[$i]['page'] = $pageInformationCache[$pid];
                 } else {
-                    $pageTitle = $pid;
+                    $pageTitle = '';
                     $pageRecord = BackendUtility::getRecord(
                             'pages',
                             $pid
index 44ac51f..3436805 100644 (file)
@@ -75,6 +75,9 @@
                        <trans-unit id="extensionKey" xml:space="preserve">
                                <source>Key (extension, class, ...)</source>
                        </trans-unit>
+                       <trans-unit id="key" xml:space="preserve">
+                               <source>Key</source>
+                       </trans-unit>
                        <trans-unit id="message" xml:space="preserve">
                                <source>Message</source>
                        </trans-unit>
                        <trans-unit id="severity_error" xml:space="preserve">
                                <source>Error</source>
                        </trans-unit>
-                       <trans-unit id="latest" xml:space="preserve">
-                               <source>latest &gt;&gt;</source>
-                       </trans-unit>
-                       <trans-unit id="next" xml:space="preserve">
-                               <source>next &gt;</source>
-                       </trans-unit>
-                       <trans-unit id="previous" xml:space="preserve">
-                               <source>&lt; previous</source>
-                       </trans-unit>
-                       <trans-unit id="oldest" xml:space="preserve">
-                               <source>&lt;&lt; oldest</source>
-                       </trans-unit>
                        <trans-unit id="expand_all_extra_data" xml:space="preserve">
                                <source>Expand all extra data</source>
                        </trans-unit>
                                <source>1 year</source>
                        </trans-unit>
                        <trans-unit id="no_entries" xml:space="preserve">
-                               <source>There are currently no log entries in the Developer Log.</source>
-                       </trans-unit>
-                       <trans-unit id="xx_entries" xml:space="preserve">
-                               <source>There are currently %d log entries in the Developer Log.</source>
-                       </trans-unit>
-                       <trans-unit id="loading" xml:space="preserve">
-                               <source>Loading...</source>
-                       </trans-unit>
-                       <trans-unit id="selectextentionkey" xml:space="preserve">
-                               <source>Select an extension key</source>
-                       </trans-unit>
-                       <trans-unit id="selectseverity" xml:space="preserve">
-                               <source>Select severity</source>
-                       </trans-unit>
-                       <trans-unit id="selectpage" xml:space="preserve">
-                               <source>Select page</source>
+                               <source>There are currently no log entries in the Developer's Log.</source>
                        </trans-unit>
                        <trans-unit id="status_ok">
                                <source>OK</source>
index 28fa794..5528f76 100644 (file)
                        </div>
                </form>
                <div class="clearfix"></div>
-               <form name="searchAndFilter">
-                       <div class="form-group">
-                               <f:form.textfield name="devlog-searchfield" placeholder="{f:translate(key:'search')}"
-                                                                 id="tx_devlog_search"
-                                                                 class="form-control t3js-devlog-searchfield"/>
+               <form name="search">
+                       <div class="form-row">
+                               <div class="form-group">
+                                       <f:form.textfield name="devlog-searchfield" placeholder="{f:translate(key:'search')}"
+                                                                         id="tx_devlog_search"
+                                                                         class="form-control t3js-devlog-searchfield"/>
+                               </div>
+                       </div>
+               </form>
+               <form name="filter" class="form-inline">
+                       <div class="form-row">
+                               <div class="form-group">
+                                       <label><f:translate id="severity" /></label>
+                                       <select id="tx_devlog_filter_severity"></select>
+                               </div>
+                               <div class="form-group">
+                                       <label><f:translate id="key" /></label>
+                                       <select id="tx_devlog_filter_key"></select>
+                               </div>
+                               <div class="form-group">
+                                       <label><f:translate id="ipAddress" /></label>
+                                       <select id="tx_devlog_filter_ip"></select>
+                               </div>
+                               <div class="form-group">
+                                       <label><f:translate id="page" /></label>
+                                       <select id="tx_devlog_filter_page"></select>
+                               </div>
+                               <div class="form-group">
+                                       <label><f:translate id="user" /></label>
+                                       <select id="tx_devlog_filter_user"></select>
+                               </div>
+                               <div class="form-group">
+                                       <button type="button" name="clearall" id="tx_devlog_clearall" class="btn btn-default">
+                                               <f:translate id="clear_filters" />
+                                       </button>
+                               </div>
                        </div>
                </form>
                <table class="table table-striped table-hover" id="tx_devlog_list">
        <div id="tx_devlog_list_loader">
                <core:icon identifier="provider-fontawesome-spinner" size="default" />
        </div>
+       <!-- No entries message -->
+       <div id="tx_devlog_list_empty" class="hidden">
+               <f:be.infobox message="{f:translate(id: 'no_entries')}" state="-1" />
+       </div>
 </html>
\ No newline at end of file
index afe1fa8..a3b6059 100644 (file)
@@ -41,15 +41,28 @@ define(['jquery',
                severityIcons: {},
                expandIcon: null,
                collapseIcon: null,
+               tableView: null,
                listWrapper: null,
                loadingMask: null,
+               noEntriesMessage: null,
                lastUpdateTime: null,
-               intervalID: 0
+               intervalID: 0,
+               // List columns to avoid hard-coding numbers all over the code
+               columns: {
+                       severity: 1,
+                       key: 2,
+                       ip: 5,
+                       page: 6,
+                       user: 7
+               },
+               filters: []
        };
 
        DevlogListModule.init = function() {
+               this.tableView = $('#tx_devlog_list');
                this.loadingMask = $('#tx_devlog_list_loader');
                this.listWrapper = $('#tx_devlog_list_wrapper');
+               this.noEntriesMessage = $('#tx_devlog_list_empty');
        };
 
        /**
@@ -82,16 +95,30 @@ define(['jquery',
        };
 
        /**
-        * Loads log data dynamically and initializes DataTables.
+        * Returns the unique elements of any given array.
         *
-        * @param tableView
+        * @param array
+        * @returns {Array}
+        */
+       DevlogListModule.arrayUnique = function (array) {
+               var filteredArray = [];
+               for (var i = 0; i < array.length; i++) {
+                       if (filteredArray.indexOf(array[i]) === -1) {
+                               filteredArray.push(array[i]);
+                       }
+               }
+               return filteredArray;
+       };
+
+       /**
+        * Loads log data dynamically and initializes DataTables.
         */
-       DevlogListModule.buildDynamicTable = function(tableView) {
+       DevlogListModule.buildDynamicTable = function() {
                this.lastUpdateTime = moment().unix();
                $.ajax({
                        url: TYPO3.settings.ajaxUrls['tx_devlog_list'],
                        success: function (data, status, xhr) {
-                               DevlogListModule.table = tableView.DataTable({
+                               DevlogListModule.table = DevlogListModule.tableView.DataTable({
                                        data: data,
                                        dom: 'tp',
                                        // Default ordering is "crdate" column
@@ -119,6 +146,8 @@ define(['jquery',
                                                        render:  function(data, type, row, meta) {
                                                                if (type === 'display') {
                                                                        return DevlogListModule.severityIcons[data];
+                                                               } else if (type === 'filter') {
+                                                                       return TYPO3.lang['severity' + data];
                                                                } else {
                                                                        return data;
                                                                }
@@ -175,8 +204,9 @@ define(['jquery',
                                        ],
                                        initComplete: function() {
                                                DevlogListModule.initializeSearchField();
-                                               DevlogListModule.initializeExtraDataToggle(tableView);
+                                               DevlogListModule.initializeExtraDataToggle();
                                                DevlogListModule.initializeReloadControls();
+                                               DevlogListModule.initializeFilters();
                                                DevlogListModule.toggleLoadingMask();
                                        }
                                });
@@ -206,16 +236,14 @@ define(['jquery',
 
        /**
         * Initializes the extra data toggle buttons.
-        *
-        * @param tableView
         */
-       DevlogListModule.initializeExtraDataToggle = function(tableView) {
+       DevlogListModule.initializeExtraDataToggle = function() {
                // Single toggle button
-               tableView.on('click', 'button.extra-data-toggle', function() {
+               DevlogListModule.tableView.on('click', 'button.extra-data-toggle', function() {
                        DevlogListModule.toggleExtraData($(this));
                });
                // Global toggle button
-               tableView.on('click', '#tx_devlog_expand_all', function() {
+               DevlogListModule.tableView.on('click', '#tx_devlog_expand_all', function() {
                        var toggleIcon = $('#tx_devlog_expand_all_icon');
                        // Switch expand/collapse icon for global toggle
                        if (toggleIcon.find('.t3js-icon').hasClass('icon-actions-view-list-expand')) {
@@ -275,6 +303,116 @@ define(['jquery',
        };
 
        /**
+        * Initializes all filter selectors and set their options list.
+        */
+       DevlogListModule.initializeFilters = function () {
+               // Reset list of filters
+               DevlogListModule.filters = [];
+
+               // Severity column
+               var currentColumn = DevlogListModule.columns.severity;
+               // Get unique values in column
+               var filteredData = DevlogListModule.arrayUnique(DevlogListModule.tableView.DataTable().column(currentColumn).data());
+               filteredData.sort();
+               // Get associated labels
+               var labels = [];
+               for (var i = 0; i < filteredData.length; i++) {
+                       labels.push(TYPO3.lang['severity' + filteredData[i]]);
+               }
+               var selector = $('#tx_devlog_filter_severity');
+               DevlogListModule.initializeSingleFilter(selector, labels, labels, currentColumn);
+
+               // Key/extension key column
+               currentColumn = DevlogListModule.columns.key;
+               // Get unique values in column
+               filteredData = DevlogListModule.arrayUnique(DevlogListModule.tableView.DataTable().column(currentColumn).data());
+               selector = $('#tx_devlog_filter_key');
+               DevlogListModule.initializeSingleFilter(selector, filteredData, filteredData, currentColumn);
+
+               // IP address column
+               currentColumn = DevlogListModule.columns.ip;
+               // Get unique values in column
+               filteredData = DevlogListModule.arrayUnique(DevlogListModule.tableView.DataTable().column(currentColumn).data());
+               selector = $('#tx_devlog_filter_ip');
+               DevlogListModule.initializeSingleFilter(selector, filteredData, filteredData, currentColumn);
+
+               // Page column
+               currentColumn = DevlogListModule.columns.page;
+               // Get unique values in column
+               filteredData = DevlogListModule.arrayUnique(DevlogListModule.tableView.DataTable().column(currentColumn).data());
+               selector = $('#tx_devlog_filter_page');
+               DevlogListModule.initializeSingleFilter(selector, filteredData, filteredData, currentColumn);
+
+               // User column
+               currentColumn = DevlogListModule.columns.user;
+               // Get unique values in column
+               filteredData = DevlogListModule.arrayUnique(DevlogListModule.tableView.DataTable().column(currentColumn).data());
+               selector = $('#tx_devlog_filter_user');
+               DevlogListModule.initializeSingleFilter(selector, filteredData, filteredData, currentColumn);
+
+               // Activate the clear all filters button (note: it also clears the search field)
+               $('#tx_devlog_clearall').on('click', function() {
+                       // Reset values for each filter selector
+                       for (var i = 0; i < DevlogListModule.filters.length; i++) {
+                               DevlogListModule.filters[i].val('');
+                       }
+                       // Cancel search on all columns
+                       for (var column in DevlogListModule.columns) {
+                               if (DevlogListModule.columns.hasOwnProperty(column)) {
+                                       DevlogListModule.table.column(DevlogListModule.columns[column]).search('');
+                               }
+                       }
+                       // Cancel general search
+                       var searchField = $('#tx_devlog_search');
+                       searchField.val('');
+                       // Hide the clear button (from clearable library)
+                       searchField.next('.close').hide();
+                       DevlogListModule.table.search('');
+                       // Redraw the table
+                       DevlogListModule.table.draw();
+               });
+       };
+
+       /**
+        * Updates and activates a single filter selector.
+        *
+        * @param selector
+        * @param texts
+        * @param values
+        * @param dataColumn
+        */
+       DevlogListModule.initializeSingleFilter = function (selector, texts, values, dataColumn) {
+               var selectorParent = selector.parent('.form-group');
+
+               // If there are no options, hide the selector
+               if (texts.length === 0) {
+                       selectorParent.hide();
+
+               // If there are options, load them and activate the selector
+               } else {
+                       // Make sure the selector is visible
+                       selectorParent.show();
+                       // Empty current options list
+                       selector.empty();
+                       // Add empty option on top
+                       selector.append($(new Option('')));
+                       // Add new options
+                       for (var i = 0; i < texts.length; i++) {
+                               if (values[i] !== '') {
+                                       var option = new Option(texts[i], values[i]);
+                                       selector.append($(option));
+                               }
+                       }
+                       // Add the selector to the list of filters
+                       DevlogListModule.filters.push(selector);
+                       // Activate change event
+                       selector.on('change', function () {
+                               DevlogListModule.table.column(dataColumn).search($(this).val()).draw();
+                       });
+               }
+       };
+
+       /**
         * Loads records created since last update and refreshes table view.
         */
        DevlogListModule.loadNewRecords = function () {
@@ -295,33 +433,45 @@ define(['jquery',
                        complete: function (xhr, status) {
                                // Restore table view
                                DevlogListModule.toggleLoadingMask();
+                               // Reload the filters (there may new values to insert into the selectors)
+                               DevlogListModule.initializeFilters($('#tx_devlog_list'));
                        }
                });
        };
 
        /**
-        * Toggles visibility of loading mask and table view
+        * Toggles visibility of loading mask and table view.
+        *
+        * Also toggles visibility of the "no entries" message.
         */
        DevlogListModule.toggleLoadingMask = function() {
+               // Show table view
                if (this.listWrapper.hasClass('hidden')) {
-                       this.listWrapper.removeClass('hidden');
+                       // If there's no data in the table, show the "no entries" message instead
+                       if (this.tableView.DataTable().data().length === 0) {
+                               this.noEntriesMessage.removeClass('hidden');
+                       } else {
+                               this.listWrapper.removeClass('hidden');
+                       }
                        this.loadingMask.addClass('hidden');
+
+               // Hide table view
                } else {
                        this.listWrapper.addClass('hidden');
+                       this.noEntriesMessage.addClass('hidden');
                        this.loadingMask.removeClass('hidden');
                }
 
        };
 
        /**
-        * Initializes this module
+        * Initializes this module.
         */
        $(function() {
-               var tableView = $('#tx_devlog_list');
                DevlogListModule.init();
                // @todo: how to ensure that all icons have been loaded? Deliver a new promise?
                DevlogListModule.loadIcons();
-               DevlogListModule.buildDynamicTable(tableView);
+               DevlogListModule.buildDynamicTable();
        });
 
        return DevlogListModule;
index b4a0292..b91b5b4 100644 (file)
@@ -40,6 +40,11 @@ class EntryRepositoryTest extends \Tx_Phpunit_Database_TestCase
         $this->subject = $objectManager->get(EntryRepository::class);
     }
 
+    protected function tearDown()
+    {
+        $this->dropDatabase();
+    }
+
     /**
      * @test
      */