Merge "Add missing dependency from 'mediawiki.user' to 'mediawiki.storage'"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Mon, 6 Mar 2017 20:53:05 +0000 (20:53 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Mon, 6 Mar 2017 20:53:06 +0000 (20:53 +0000)
includes/Title.php
resources/Resources.php
resources/src/mediawiki.rcfilters/images/marker-ltr.svg [new file with mode: 0644]
resources/src/mediawiki.rcfilters/images/marker-rtl.svg [new file with mode: 0644]
resources/src/mediawiki.rcfilters/mw.rcfilters.Controller.js
resources/src/mediawiki.rcfilters/mw.rcfilters.init.js
resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.FilterItemHighlightButton.less
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterItemHighlightButton.js
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterWrapperWidget.js
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FormWrapperWidget.js

index 13a6f56..3ed6b8b 100644 (file)
@@ -2549,6 +2549,29 @@ class Title implements LinkTarget {
         *   protection, or false if there's none.
         */
        public function getTitleProtection() {
+               $protection = $this->getTitleProtectionInternal();
+               if ( $protection ) {
+                       if ( $protection['permission'] == 'sysop' ) {
+                               $protection['permission'] = 'editprotected'; // B/C
+                       }
+                       if ( $protection['permission'] == 'autoconfirmed' ) {
+                               $protection['permission'] = 'editsemiprotected'; // B/C
+                       }
+               }
+               return $protection;
+       }
+
+       /**
+        * Fetch title protection settings
+        *
+        * To work correctly, $this->loadRestrictions() needs to have access to the
+        * actual protections in the database without munging 'sysop' =>
+        * 'editprotected' and 'autoconfirmed' => 'editsemiprotected'. Other
+        * callers probably want $this->getTitleProtection() instead.
+        *
+        * @return array|bool
+        */
+       protected function getTitleProtectionInternal() {
                // Can't protect pages in special namespaces
                if ( $this->getNamespace() < 0 ) {
                        return false;
@@ -2576,12 +2599,6 @@ class Title implements LinkTarget {
                        // fetchRow returns false if there are no rows.
                        $row = $dbr->fetchRow( $res );
                        if ( $row ) {
-                               if ( $row['permission'] == 'sysop' ) {
-                                       $row['permission'] = 'editprotected'; // B/C
-                               }
-                               if ( $row['permission'] == 'autoconfirmed' ) {
-                                       $row['permission'] = 'editsemiprotected'; // B/C
-                               }
                                $row['expiry'] = $dbr->decodeExpiry( $row['expiry'] );
                        }
                        $this->mTitleProtection = $row;
@@ -2979,7 +2996,7 @@ class Title implements LinkTarget {
 
                        $this->loadRestrictionsFromRows( $rows, $oldFashionedRestrictions );
                } else {
-                       $title_protection = $this->getTitleProtection();
+                       $title_protection = $this->getTitleProtectionInternal();
 
                        if ( $title_protection ) {
                                $now = wfTimestampNow();
index 5f9b5e0..67a3de5 100644 (file)
@@ -1930,7 +1930,10 @@ return [
        'mediawiki.special.block' => [
                'scripts' => 'resources/src/mediawiki.special/mediawiki.special.block.js',
                'styles' => 'resources/src/mediawiki.special/mediawiki.special.block.css',
-               'dependencies' => 'mediawiki.util',
+               'dependencies' => [
+                       'mediawiki.util',
+                       'mediawiki.htmlform',
+               ],
        ],
        'mediawiki.special.changeslist' => [
                'styles' => 'resources/src/mediawiki.special/mediawiki.special.changeslist.css',
diff --git a/resources/src/mediawiki.rcfilters/images/marker-ltr.svg b/resources/src/mediawiki.rcfilters/images/marker-ltr.svg
new file mode 100644 (file)
index 0000000..eb42923
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" height="24" width="24">
+    <path d="M5.066 18.236l.14-.244c.976-1.69 1.341-4.587.815-6.469l-.14-.507.2-.365L11.074 2l9.011 5.203-4.994 8.65-.204.354-.522.134c-1.893.485-4.22 2.252-5.195 3.94l-.14.244-.721-.416-1.041 1.89H3.914l1.893-3.336z" fill-rule="evenodd"/>
+</svg>
diff --git a/resources/src/mediawiki.rcfilters/images/marker-rtl.svg b/resources/src/mediawiki.rcfilters/images/marker-rtl.svg
new file mode 100644 (file)
index 0000000..9b1940e
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" height="24" width="24">
+    <path d="M18.934 18.236l-.14-.244c-.976-1.69-1.341-4.587-.815-6.469l.14-.507-.2-.365L12.926 2 3.914 7.203l4.994 8.65.204.354.522.134c1.893.485 4.22 2.252 5.195 3.94l.14.244.721-.416 1.041 1.89h3.355l-1.893-3.336z" fill-rule="evenodd"/>
+</svg>
index 98eaa59..1c05909 100644 (file)
                                function ( pieces ) {
                                        var $changesListContent = pieces.changes,
                                                $fieldset = pieces.fieldset;
-
                                        this.changesListModel.update( $changesListContent, $fieldset );
                                }.bind( this )
                                // Do nothing for failure
index 746907b..089b106 100644 (file)
                                filtersWidget = new mw.rcfilters.ui.FilterWrapperWidget(
                                        controller, filtersModel, { $overlay: $overlay } );
 
+                       // TODO: The changesListWrapperWidget should be able to initialize
+                       // after the model is ready.
                        // eslint-disable-next-line no-new
                        new mw.rcfilters.ui.ChangesListWrapperWidget(
                                filtersModel, changesListModel, $( '.mw-changeslist, .mw-changeslist-empty' ) );
 
-                       // eslint-disable-next-line no-new
-                       new mw.rcfilters.ui.FormWrapperWidget(
-                               changesListModel, controller, $( 'fieldset.rcoptions' ) );
-
                        controller.initialize( {
                                registration: {
                                        title: mw.msg( 'rcfilters-filtergroup-registration' ),
                                }
                        } );
 
+                       // eslint-disable-next-line no-new
+                       new mw.rcfilters.ui.FormWrapperWidget(
+                               filtersModel, changesListModel, controller, $( 'fieldset.rcoptions' ) );
+
                        $( '.rcfilters-container' ).append( filtersWidget.$element );
                        $( 'body' ).append( $overlay );
 
-                       // HACK: Remove old-style filter links for filters handled by the widget
-                       // Ideally the widget would handle all filters and we'd just remove .rcshowhide entirely
-                       $( '.rcshowhide' ).children().each( function () {
-                               // HACK: Interpret the class name to get the filter name
-                               // This should really be set as a data attribute
-                               var i,
-                                       name = null,
-                                       // Some of the older browsers we support don't have .classList,
-                                       // so we have to interpret the class attribute manually.
-                                       classes = this.getAttribute( 'class' ).split( ' ' );
-                               for ( i = 0; i < classes.length; i++ ) {
-                                       if ( classes[ i ].substr( 0, 'rcshow'.length ) === 'rcshow' ) {
-                                               name = classes[ i ].substr( 'rcshow'.length );
-                                               break;
-                                       }
-                               }
-                               if ( name === null ) {
-                                       return;
-                               }
-                               if ( name === 'hidemine' ) {
-                                       // HACK: the span for hidemyself is called hidemine
-                                       name = 'hidemyself';
-                               }
-                               // This span corresponds to a filter that's in our model, so remove it
-                               if ( filtersModel.getItemByName( name ) ) {
-                                       // HACK: Remove the text node after the span.
-                                       // If there isn't one, we're at the end, so remove the text node before the span.
-                                       // This would be unnecessary if we added separators with CSS.
-                                       if ( this.nextSibling && this.nextSibling.nodeType === Node.TEXT_NODE ) {
-                                               this.parentNode.removeChild( this.nextSibling );
-                                       } else if ( this.previousSibling && this.previousSibling.nodeType === Node.TEXT_NODE ) {
-                                               this.parentNode.removeChild( this.previousSibling );
-                                       }
-                                       // Remove the span itself
-                                       this.parentNode.removeChild( this );
-                               }
-                       } );
-
                        window.addEventListener( 'popstate', function () {
                                controller.updateChangesList();
                        } );
index 3f70125..0f30137 100644 (file)
@@ -1,6 +1,10 @@
 @import 'mw.rcfilters.mixins';
 
 .mw-rcfilters-ui-filterItemHighlightButton {
+       .oo-ui-iconElement-icon.oo-ui-icon-highlight {
+               /* @embed */
+               background-image: url( ../images/marker-ltr.svg );
+       }
 
        .oo-ui-buttonWidget.oo-ui-popupButtonWidget .oo-ui-buttonElement-button > &-circle {
                display: inline-block;
index 34fa82e..17aad51 100644 (file)
@@ -16,7 +16,7 @@
 
                // Parent
                mw.rcfilters.ui.FilterItemHighlightButton.parent.call( this, $.extend( {}, config, {
-                       icon: 'edit',
+                       icon: 'highlight',
                        indicator: 'down',
                        popup: {
                                anchor: false,
index 7da97a1..886dc43 100644 (file)
         */
        mw.rcfilters.ui.FilterWrapperWidget.prototype.scrollToTop = function ( $element, marginFromTop ) {
                var container = OO.ui.Element.static.getClosestScrollableContainer( $element[ 0 ], 'y' ),
-                       pos = OO.ui.Element.static.getRelativePosition( $element, $( container ) );
+                       pos = OO.ui.Element.static.getRelativePosition( $element, $( container ) ),
+                       containerScrollTop = $( container ).is( 'body' ) ? 0 : $( container ).scrollTop();
 
                // Scroll to item
                $( container ).animate( {
-                       scrollTop: $( container ).scrollTop() + pos.top + ( marginFromTop || 0 )
+                       scrollTop: containerScrollTop + pos.top - ( marginFromTop || 0 )
                } );
        };
 }( mediaWiki ) );
index 3c81ff1..51311e1 100644 (file)
@@ -1,16 +1,18 @@
 ( function ( mw ) {
        /**
         * Wrapper for the RC form with hide/show links
+        * Must be constructed after the model is initialized.
         *
         * @extends OO.ui.Widget
         *
         * @constructor
-        * @param {mw.rcfilters.dm.ChangesListViewModel} model Changes list view model
+        * @param {mw.rcfilters.dm.FiltersViewModel} filtersModel Changes list view model
+        * @param {mw.rcfilters.dm.ChangesListViewModel} changeListModel Changes list view model
         * @param {mw.rcfilters.Controller} controller RCfilters controller
         * @param {jQuery} $formRoot Root element of the form to attach to
         * @param {Object} config Configuration object
         */
-       mw.rcfilters.ui.FormWrapperWidget = function MwRcfiltersUiFormWrapperWidget( model, controller, $formRoot, config ) {
+       mw.rcfilters.ui.FormWrapperWidget = function MwRcfiltersUiFormWrapperWidget( filtersModel, changeListModel, controller, $formRoot, config ) {
                config = config || {};
 
                // Parent
@@ -20,7 +22,8 @@
                // Mixin constructors
                OO.ui.mixin.PendingElement.call( this, config );
 
-               this.model = model;
+               this.changeListModel = changeListModel;
+               this.filtersModel = filtersModel;
                this.controller = controller;
                this.$submitButton = this.$element.find( 'form input[type=submit]' );
 
                        .on( 'submit', 'form', this.onFormSubmit.bind( this ) );
 
                // Events
-               this.model.connect( this, {
-                       invalidate: 'onModelInvalidate',
-                       update: 'onModelUpdate'
+               this.changeListModel.connect( this, {
+                       invalidate: 'onChangesModelInvalidate',
+                       update: 'onChangesModelUpdate'
                } );
 
                // Initialize
-               this.cleanupForm();
+               this.cleanUpFieldset();
                this.$element
                        .addClass( 'mw-rcfilters-ui-FormWrapperWidget' )
                        .addClass( 'mw-rcfilters-ui-ready' );
        OO.inheritClass( mw.rcfilters.ui.FormWrapperWidget, OO.ui.Widget );
        OO.mixinClass( mw.rcfilters.ui.FormWrapperWidget, OO.ui.mixin.PendingElement );
 
-       /**
-        * Clean up the base form we're getting from the back-end.
-        * Remove <strong> tags and replace those with classes, so
-        * we can toggle those on click.
-        */
-       mw.rcfilters.ui.FormWrapperWidget.prototype.cleanupForm = function () {
-               this.$element.find( '[data-keys] strong' ).each( function () {
-                       $( this )
-                               .parent().addClass( 'mw-rcfilters-staticfilters-selected' );
-
-                       $( this )
-                               .replaceWith( $( this ).contents() );
-               } );
-       };
-
        /**
         * Respond to link click
         *
         * @return {boolean} false
         */
        mw.rcfilters.ui.FormWrapperWidget.prototype.onLinkClick = function ( e ) {
-               var $element = $( e.target ),
-                       data = $element.data( 'params' ),
-                       keys = $element.data( 'keys' ),
-                       $similarElements = $element.parent().find( '[data-keys="' + keys + '"]' );
-
-               // Only highlight choice if this link isn't a show/hide link
-               if ( !$element.parents( '.rcshowhideoption' ).length ) {
-                       // Remove the class from similar elements
-                       $similarElements.removeClass( 'mw-rcfilters-staticfilters-selected' );
-                       // Add the class to this element
-                       $element.addClass( 'mw-rcfilters-staticfilters-selected' );
-               }
-
-               e.stopPropagation();
-
-               this.controller.updateChangesList( data );
+               this.controller.updateChangesList( $( e.target ).data( 'params' ) );
                return false;
        };
 
        /**
         * Respond to model invalidate
         */
-       mw.rcfilters.ui.FormWrapperWidget.prototype.onModelInvalidate = function () {
+       mw.rcfilters.ui.FormWrapperWidget.prototype.onChangesModelInvalidate = function () {
                this.pushPending();
                this.$submitButton.prop( 'disabled', true );
        };
         * @param {jQuery|string} $changesList Updated changes list
         * @param {jQuery} $fieldset Updated fieldset
         */
-       mw.rcfilters.ui.FormWrapperWidget.prototype.onModelUpdate = function ( $changesList, $fieldset ) {
+       mw.rcfilters.ui.FormWrapperWidget.prototype.onChangesModelUpdate = function ( $changesList, $fieldset ) {
                this.$submitButton.prop( 'disabled', false );
 
-               // Replace the links we have in the content
-               // We don't want to replace the entire thing, because there is a big difference between
-               // the links in the backend and the links we have initialized, since we are removing
-               // the ones that are implemented in the new system
+               // Replace the entire fieldset
+               this.$element.empty().append( $fieldset.contents() );
+
+               this.cleanUpFieldset();
+
+               this.popPending();
+       };
+
+       /**
+        * Clean up the old-style show/hide that we have implemented in the filter list
+        */
+       mw.rcfilters.ui.FormWrapperWidget.prototype.cleanUpFieldset = function () {
+               var widget = this;
+
+               // HACK: Remove old-style filter links for filters handled by the widget
+               // Ideally the widget would handle all filters and we'd just remove .rcshowhide entirely
                this.$element.find( '.rcshowhide' ).children().each( function () {
-                       // Go over existing links and replace only them
-                       var classes = $( this ).attr( 'class' ).split( ' ' ),
-                               // Look for that item in the fieldset from the server
-                               $remoteItem = $fieldset.find( '.' + classes.join( '.' ) );
+                       // HACK: Interpret the class name to get the filter name
+                       // This should really be set as a data attribute
+                       var i,
+                               name = null,
+                               // Some of the older browsers we support don't have .classList,
+                               // so we have to interpret the class attribute manually.
+                               classes = this.getAttribute( 'class' ).split( ' ' );
+                       for ( i = 0; i < classes.length; i++ ) {
+                               if ( classes[ i ].substr( 0, 'rcshow'.length ) === 'rcshow' ) {
+                                       name = classes[ i ].substr( 'rcshow'.length );
+                                       break;
+                               }
+                       }
+                       if ( name === null ) {
+                               return;
+                       }
+                       if ( name === 'hidemine' ) {
+                               // HACK: the span for hidemyself is called hidemine
+                               name = 'hidemyself';
+                       }
 
-                       if ( $remoteItem ) {
-                               $( this ).replaceWith( $remoteItem );
+                       // This span corresponds to a filter that's in our model, so remove it
+                       if ( widget.filtersModel.getItemByName( name ) ) {
+                               // HACK: Remove the text node after the span.
+                               // If there isn't one, we're at the end, so remove the text node before the span.
+                               // This would be unnecessary if we added separators with CSS.
+                               if ( this.nextSibling && this.nextSibling.nodeType === Node.TEXT_NODE ) {
+                                       this.parentNode.removeChild( this.nextSibling );
+                               } else if ( this.previousSibling && this.previousSibling.nodeType === Node.TEXT_NODE ) {
+                                       this.parentNode.removeChild( this.previousSibling );
+                               }
+                               // Remove the span itself
+                               this.parentNode.removeChild( this );
                        }
                } );
-
-               this.popPending();
        };
 }( mediaWiki ) );