Mooeypoo has uploaded a new change for review. ( 
https://gerrit.wikimedia.org/r/356056 )

Change subject: [wip] RCFilters: Add edit tags drop down
......................................................................

[wip] RCFilters: Add edit tags drop down

Bug: T159942
Bug: T161650
Change-Id: I7bfa99cd5aeb34b6c7de74c15aac158ee40eac2f
---
M languages/i18n/en.json
M languages/i18n/qqq.json
M resources/Resources.php
M resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FiltersViewModel.js
M resources/src/mediawiki.rcfilters/mw.rcfilters.Controller.js
M 
resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.FilterWrapperWidget.less
M 
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterTagMultiselectWidget.js
M resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterWrapperWidget.js
8 files changed, 198 insertions(+), 79 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/core 
refs/changes/56/356056/1

diff --git a/languages/i18n/en.json b/languages/i18n/en.json
index 46d86f5..1631a9f 100644
--- a/languages/i18n/en.json
+++ b/languages/i18n/en.json
@@ -1453,6 +1453,8 @@
        "rcfilters-filter-excluded": "Excluded",
        "rcfilters-tag-prefix-namespace": ":$1",
        "rcfilters-tag-prefix-namespace-inverted": "<strong>:not</strong> $1",
+       "rcfilters-tag-prefix-tags": "#$1",
+       "rcfilters-view-tags": "Tags",
        "rcnotefrom": "Below {{PLURAL:$5|is the change|are the changes}} since 
<strong>$3, $4</strong> (up to <strong>$1</strong> shown).",
        "rclistfromreset": "Reset date selection",
        "rclistfrom": "Show new changes starting from $2, $3",
diff --git a/languages/i18n/qqq.json b/languages/i18n/qqq.json
index a9077ea..c5ce883 100644
--- a/languages/i18n/qqq.json
+++ b/languages/i18n/qqq.json
@@ -1641,6 +1641,8 @@
        "rcfilters-filter-excluded": "Label for a menu item in 
[[Special:RecentChanges]] noting that the item is being excluded from the 
results.",
        "rcfilters-tag-prefix-namespace": "Prefix for the namespace tags in 
[[Special:RecentChanges]]. Namespace tags use a colon (:) as prefix. Please 
keep this format.\n\nParameters:\n* $1 - Filter name.",
        "rcfilters-tag-prefix-namespace-inverted": "Prefix for the namespace 
inverted tags in [[Special:RecentChanges]]. Namespace tags use a colon (:) as 
prefix. Please keep this format.\n\nParameters:\n* $1 - Filter name.",
+       "rcfilters-tag-prefix-tags": "Prefix for the edit tags in 
[[Special:RecentChanges]]. Namespace tags use a colon (:) as prefix. Please 
keep this format.\n\nParameters:\n* $1 - Tag display name.",
+       "rcfilters-view-tags": "Title for the tags view in 
[[Special:RecentChanges]]",
        "rcnotefrom": "This message is displayed at [[Special:RecentChanges]] 
when viewing recentchanges from some specific time.\n\nThe corresponding 
message is {{msg-mw|Rclistfrom}}.\n\nParameters:\n* $1 - the maximum number of 
changes that are displayed\n* $2 - (Optional) a date and time\n* $3 - a date\n* 
$4 - a time\n* $5 - Number of changes are displayed, for use with PLURAL",
        "rclistfromreset": "Used on [[Special:RecentChanges]] to reset a 
selection of a certain date range.",
        "rclistfrom": "Used on [[Special:RecentChanges]]. Parameters:\n* $1 - 
(Currently not use) date and time. The date and the time adds to the rclistfrom 
description.\n* $2 - time. The time adds to the rclistfrom link description 
(with split of date and time).\n* $3 - date. The date adds to the rclistfrom 
link description (with split of date and time).\n\nThe corresponding message is 
{{msg-mw|Rcnotefrom}}.",
diff --git a/resources/Resources.php b/resources/Resources.php
index 6c20c2f..2851fc6 100644
--- a/resources/Resources.php
+++ b/resources/Resources.php
@@ -1831,6 +1831,8 @@
                        'rcfilters-filter-excluded',
                        'rcfilters-tag-prefix-namespace',
                        'rcfilters-tag-prefix-namespace-inverted',
+                       'rcfilters-tag-prefix-tags',
+                       'rcfilters-view-tags',
                        'blanknamespace',
                        'namespaces',
                        'invert',
diff --git 
a/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FiltersViewModel.js 
b/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FiltersViewModel.js
index f60598f..c3ca0ff 100644
--- a/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FiltersViewModel.js
+++ b/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FiltersViewModel.js
@@ -201,12 +201,14 @@
         *
         * @param {Array} filters Filter group definition
         * @param {Object} [namespaces] Namespace definition
+        * @param {Object[]} [tags] Tag array definition
         */
-       mw.rcfilters.dm.FiltersViewModel.prototype.initializeFilters = function 
( filters, namespaces ) {
+       mw.rcfilters.dm.FiltersViewModel.prototype.initializeFilters = function 
( filters, namespaces, tags ) {
                var filterItem, filterConflictResult, groupConflictResult,
                        model = this,
                        items = [],
                        namespaceDefinition = [],
+                       tagDefinition = [],
                        groupConflictMap = {},
                        filterConflictMap = {},
                        /*!
@@ -353,8 +355,43 @@
                        items = items.concat( model.groups.namespace.getItems() 
);
                }
 
+               tags = tags || [];
+               if ( tags.length ) {
+                       // Define view
+                       this.views.tags = { name: 'tags', label: mw.msg( 
'rcfilters-view-tags' ), trigger: '#' };
+
+                       // Build item data
+                       tags.forEach( function ( tagData ) {
+                               tagDefinition.push( {
+                                       name: tagData.name,
+                                       label: tagData.displayname,
+                                       description: tagData.description
+                               } );
+                       } );
+
+                       // Add the group
+                       model.groups.tags = new mw.rcfilters.dm.FilterGroup(
+                               'tags',
+                               {
+                                       type: 'string_options',
+                                       view: 'tags',
+                                       title: 'rcfilters-view-tags', // 
Message key
+                                       labelPrefixKey: 
'rcfilters-tag-prefix-tags',
+                                       separator: ',',
+                                       fullCoverage: false
+                               }
+                       );
+
+                       // Add tag items to group
+                       model.groups.tags.initializeFilters( tagDefinition );
+
+                       // Add item references to the model, for lookup
+                       items = items.concat( model.groups.tags.getItems() );
+               }
+
                // Add item references to the model, for lookup
                this.addItems( items );
+
                // Expand conflicts
                groupConflictResult = expandConflictDefinitions( 
groupConflictMap );
                filterConflictResult = expandConflictDefinitions( 
filterConflictMap );
@@ -756,12 +793,12 @@
                        groupTitle,
                        result = {},
                        flatResult = [],
-                       view = query.indexOf( this.getViewTrigger( 'namespaces' 
) ) === 0 ? 'namespaces' : 'default',
+                       view = this.getViewByTrigger( query.substr( 0, 1 ) ),
                        items = this.getFiltersByView( view );
 
                // Normalize so we can search strings regardless of case and 
view
                query = query.toLowerCase();
-               if ( view === 'namespaces' ) {
+               if ( view !== 'default' ) {
                        query = query.substr( 1 );
                }
 
@@ -839,6 +876,22 @@
                return this.views[ this.getCurrentView() ].label;
        };
 
+       mw.rcfilters.dm.FiltersViewModel.prototype.getAvailableViews = function 
() {
+               return Object.keys( this.views );
+       };
+
+       mw.rcfilters.dm.FiltersViewModel.prototype.getViewByTrigger = function 
( trigger ) {
+               var result = 'default';
+
+               $.each( this.views, function ( name, data ) {
+                       if ( data.trigger === trigger ) {
+                               result = name;
+                       }
+               } );
+
+               return result;
+       };
+
        /**
         * Toggle the highlight feature on and off.
         * Propagate the change to filter items.
diff --git a/resources/src/mediawiki.rcfilters/mw.rcfilters.Controller.js 
b/resources/src/mediawiki.rcfilters/mw.rcfilters.Controller.js
index 13dfbaf..45c8075 100644
--- a/resources/src/mediawiki.rcfilters/mw.rcfilters.Controller.js
+++ b/resources/src/mediawiki.rcfilters/mw.rcfilters.Controller.js
@@ -27,75 +27,80 @@
         */
        mw.rcfilters.Controller.prototype.initialize = function ( 
filterStructure, namespaceStructure ) {
                var parsedSavedQueries, validParameterNames,
+                       controller = this,
                        uri = new mw.Uri(),
                        $changesList = $( '.mw-changeslist' 
).first().contents();
 
                // Initialize the model
-               this.filtersModel.initializeFilters( filterStructure, 
namespaceStructure );
-
-               this._buildBaseFilterState();
-               this._buildEmptyParameterState();
-               validParameterNames = Object.keys( 
this._getEmptyParameterState() )
-                       .filter( function ( param ) {
-                               // Remove 'highlight' and 'invert' parameters 
from this check;
-                               // if it's the only parameter in the URL we 
still
-                               // want to consider the URL 'empty' for 
defaults to load
-                               return param !== 'highlight' && param !== 
'invert';
-                       } );
-
-               try {
-                       parsedSavedQueries = JSON.parse( mw.user.options.get( 
'rcfilters-saved-queries' ) || '{}' );
-               } catch ( err ) {
-                       parsedSavedQueries = {};
-               }
-
-               // The queries are saved in a minimized state, so we need
-               // to send over the base state so the saved queries model
-               // can normalize them per each query item
-               this.savedQueriesModel.initialize(
-                       parsedSavedQueries,
-                       this._getBaseFilterState()
-               );
-
-               // Check whether we need to load defaults.
-               // We do this by checking whether the current URI query
-               // is a subset of the base state. If it is, we don't load
-               // defaults. If it isn't, we have no values at all, and
-               // we need to load defaults.
-               // Defaults should only be applied on load (if necessary)
-               // or on request
-               if (
-                       Object.keys( uri.query ).some( function ( parameter ) {
-                               return validParameterNames.indexOf( parameter ) 
> -1;
+               this._fetchEditTags()
+                       .then( function ( tagList ) {
+                               controller.filtersModel.initializeFilters( 
filterStructure, namespaceStructure, tagList );
                        } )
-               ) {
-                       // There are parameters in the url, update model state
-                       this.updateStateBasedOnUrl();
-               } else {
-                       // No valid parameters are given, load defaults
-                       this._updateModelState(
-                               $.extend( true,
-                                       // We're ignoring the highlight and 
invert parameters,
-                                       // if they exist, in the check for 
"emptiness" but we
-                                       // should retain their value when we 
update the model state
-                                       {
-                                               highlight: String( Number( 
uri.query.highlight ) ),
-                                               invert: String( Number( 
uri.query.invert ) )
-                                       },
-                                       this._getDefaultParams()
-                               )
-                       );
-                       this.updateChangesList();
-               }
+                       .then( function () {
+                               this._buildBaseFilterState();
+                               this._buildEmptyParameterState();
+                               validParameterNames = Object.keys( 
this._getEmptyParameterState() )
+                                       .filter( function ( param ) {
+                                               // Remove 'highlight' and 
'invert' parameters from this check;
+                                               // if it's the only parameter 
in the URL we still
+                                               // want to consider the URL 
'empty' for defaults to load
+                                               return param !== 'highlight' && 
param !== 'invert';
+                                       } );
 
-               this.switchView( 'default' );
+                               try {
+                                       parsedSavedQueries = JSON.parse( 
mw.user.options.get( 'rcfilters-saved-queries' ) || '{}' );
+                               } catch ( err ) {
+                                       parsedSavedQueries = {};
+                               }
 
-               // Update the changes list with the existing data
-               // so it gets processed
-               this.changesListModel.update(
-                       $changesList.length ? $changesList : 'NO_RESULTS',
-                       $( 'fieldset.rcoptions' ).first()
-               );
+                               // The queries are saved in a minimized state, 
so we need
+                               // to send over the base state so the saved 
queries model
+                               // can normalize them per each query item
+                               this.savedQueriesModel.initialize(
+                                       parsedSavedQueries,
+                                       this._getBaseFilterState()
+                               );
+
+                               // Check whether we need to load defaults.
+                               // We do this by checking whether the current 
URI query
+                               // is a subset of the base state. If it is, we 
don't load
+                               // defaults. If it isn't, we have no values at 
all, and
+                               // we need to load defaults.
+                               // Defaults should only be applied on load (if 
necessary)
+                               // or on request
+                               if (
+                                       Object.keys( uri.query ).some( function 
( parameter ) {
+                                               return 
validParameterNames.indexOf( parameter ) > -1;
+                                       } )
+                               ) {
+                                       // There are parameters in the url, 
update model state
+                                       this.updateStateBasedOnUrl();
+                               } else {
+                                       // No valid parameters are given, load 
defaults
+                                       this._updateModelState(
+                                               $.extend( true,
+                                                       // We're ignoring the 
highlight and invert parameters,
+                                                       // if they exist, in 
the check for "emptiness" but we
+                                                       // should retain their 
value when we update the model state
+                                                       {
+                                                               highlight: 
String( Number( uri.query.highlight ) ),
+                                                               invert: String( 
Number( uri.query.invert ) )
+                                                       },
+                                                       this._getDefaultParams()
+                                               )
+                                       );
+                                       this.updateChangesList();
+                               }
+
+                               this.switchView( 'default' );
+
+                               // Update the changes list with the existing 
data
+                               // so it gets processed
+                               this.changesListModel.update(
+                                       $changesList.length ? $changesList : 
'NO_RESULTS',
+                                       $( 'fieldset.rcoptions' ).first()
+                               );
+                       }.bind( this ) );
        };
 
        /**
@@ -755,6 +760,39 @@
        };
 
        /**
+        * Fetch edit tags
+        *
+        * @return {jQuery.Promise} Promise that is resolved with the
+        *  array of edit tag information from the API.
+        */
+       mw.rcfilters.Controller.prototype._fetchEditTags = function () {
+               var api = new mw.Api( { ajax: { cache: false } } );
+
+               return api.get( {
+                               action: 'query',
+                               list: 'tags',
+                               tgprop: 'displayname|hitcount|description',
+                               tglimit: 500
+                       } )
+                       .then(
+                               function ( result ) {
+                                       var tags = OO.getProp( result, 'query', 
'tags' ) || [];
+
+                                       // Clean up the result. Displayname has 
html in it, and we
+                                       // don't want that in our view.
+                                       tags.forEach( function ( tagData ) {
+                                               tagData.displayname = $( 
'<span>' ).append( tagData.displayname ).text();
+                                       } );
+
+                                       return tags;
+                               },
+                               function () {
+                                       return [];
+                               }
+                       );
+       };
+
+       /**
         * Track usage of highlight feature
         *
         * @param {string} action
diff --git 
a/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.FilterWrapperWidget.less
 
b/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.FilterWrapperWidget.less
index 2cd77a8..be611eb 100644
--- 
a/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.FilterWrapperWidget.less
+++ 
b/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.FilterWrapperWidget.less
@@ -3,7 +3,7 @@
        // Make sure this uses the interface direction, not the content 
direction
        direction: ltr;
 
-       &-namespaceToggle {
+       &-viewToggleButtons {
                margin-top: 1em;
        }
 }
diff --git 
a/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterTagMultiselectWidget.js
 
b/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterTagMultiselectWidget.js
index d83f5f3..d8c79ca 100644
--- 
a/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterTagMultiselectWidget.js
+++ 
b/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterTagMultiselectWidget.js
@@ -171,6 +171,8 @@
 
                if ( value.indexOf( this.model.getViewTrigger( 'namespaces' ) ) 
=== 0 ) {
                        view = 'namespaces';
+               } else if ( value.indexOf( this.model.getViewTrigger( 'tags' ) 
) === 0 ) {
+                       view = 'tags';
                }
 
                this.controller.switchView( view );
@@ -271,15 +273,19 @@
                        newInputValue = inputValue;
 
                switch ( view ) {
+                       case 'tags':
                        case 'namespaces':
-                               if ( inputValue.indexOf( 
this.model.getViewTrigger( 'namespaces' ) ) !== 0 ) {
+                               if ( inputValue.indexOf( 
this.model.getViewTrigger( view ) ) !== 0 ) {
                                        // Add the prefix to the input
-                                       newInputValue = 
this.model.getViewTrigger( 'namespaces' ) + inputValue;
+                                       newInputValue = 
this.model.getViewTrigger( view ) + inputValue;
                                }
                                break;
                        default:
                        case 'default':
-                               if ( inputValue.indexOf( 
this.model.getViewTrigger( 'namespaces' ) ) === 0 ) {
+                               if (
+                                       inputValue.indexOf( 
this.model.getViewTrigger( 'namespaces' ) ) === 0 ||
+                                       inputValue.indexOf( 
this.model.getViewTrigger( 'tags' ) ) === 0
+                               ) {
                                        // Remove the prefix
                                        newInputValue = inputValue.substr( 1 );
                                }
diff --git 
a/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterWrapperWidget.js 
b/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterWrapperWidget.js
index ae8266a..4b93ae0 100644
--- 
a/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterWrapperWidget.js
+++ 
b/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterWrapperWidget.js
@@ -33,15 +33,26 @@
                        { $overlay: this.$overlay }
                );
 
-               this.namespaceButton = new OO.ui.ButtonWidget( {
-                       label: mw.msg( 'namespaces' ),
-                       classes: [ 
'mw-rcfilters-ui-filterWrapperWidget-namespaceToggle' ]
+               this.viewToggle = new OO.ui.ButtonGroupWidget( {
+                       classes: [ 
'mw-rcfilters-ui-filterWrapperWidget-viewToggleButtons' ],
+                       items: [
+                               new OO.ui.ButtonWidget( {
+                                       data: 'namespaces',
+                                       label: mw.msg( 'namespaces' ),
+                                       classes: [ 
'mw-rcfilters-ui-filterWrapperWidget-viewToggleButtons-namespace' ]
+                               } ),
+                               new OO.ui.ButtonWidget( {
+                                       data: 'tags',
+                                       label: mw.msg( 'rcfilters-view-tags' ),
+                                       classes: [ 
'mw-rcfilters-ui-filterWrapperWidget-viewToggleButtons-tags' ]
+                               } ),
+                       ]
                } );
-               this.namespaceButton.setActive( this.model.getCurrentView() === 
'namespaces' );
 
                // Events
                this.model.connect( this, { update: 'onModelUpdate' } );
-               this.namespaceButton.connect( this, { click: 
'onNamespaceToggleClick' } );
+               this.viewToggle.aggregate( { click: 'itemClick' } );
+               this.viewToggle.connect( this, { itemClick: 'onViewToggleClick' 
} );
 
                // Initialize
                this.$element
@@ -62,7 +73,7 @@
 
                this.$element.append(
                        this.filterTagWidget.$element,
-                       this.namespaceButton.$element
+                       this.viewToggle.$element
                );
        };
 
@@ -77,15 +88,20 @@
         * Respond to model update event
         */
        mw.rcfilters.ui.FilterWrapperWidget.prototype.onModelUpdate = function 
() {
-               // Synchronize the state of the toggle button with the current 
view
-               this.namespaceButton.setActive( this.model.getCurrentView() === 
'namespaces' );
+               var widget = this;
+
+               // Synchronize the state of the toggle buttons with the current 
view
+               this.viewToggle.getItems().forEach( function ( buttonWidget ) {
+                       buttonWidget.setActive( widget.model.getCurrentView() 
=== buttonWidget.getData() );
+               } );
        };
 
        /**
         * Respond to namespace toggle button click
         */
-       mw.rcfilters.ui.FilterWrapperWidget.prototype.onNamespaceToggleClick = 
function () {
-               this.controller.switchView( 'namespaces' );
+       mw.rcfilters.ui.FilterWrapperWidget.prototype.onViewToggleClick = 
function ( buttonWidget ) {
+               this.controller.switchView( buttonWidget.getData() );
                this.filterTagWidget.focus();
        };
+
 }( mediaWiki ) );

-- 
To view, visit https://gerrit.wikimedia.org/r/356056
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I7bfa99cd5aeb34b6c7de74c15aac158ee40eac2f
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/core
Gerrit-Branch: master
Gerrit-Owner: Mooeypoo <mor...@gmail.com>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to