Merge "Check MW version requires before manifest_version"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Wed, 2 Nov 2016 10:22:08 +0000 (10:22 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Wed, 2 Nov 2016 10:22:08 +0000 (10:22 +0000)
autoload.php
includes/WatchedItemStore.php
includes/widget/AUTHORS.txt
includes/widget/DateInputWidget.php [new file with mode: 0644]
resources/src/mediawiki.widgets/mw.widgets.DateInputWidget.js
resources/src/mediawiki/mediawiki.util.js
tests/qunit/suites/resources/mediawiki/mediawiki.util.test.js

index 17e5df6..b96250d 100644 (file)
@@ -921,6 +921,7 @@ $wgAutoloadLocalClasses = [
        'MediaWiki\\Tidy\\TidyDriverBase' => __DIR__ . '/includes/tidy/TidyDriverBase.php',
        'MediaWiki\\Widget\\ComplexNamespaceInputWidget' => __DIR__ . '/includes/widget/ComplexNamespaceInputWidget.php',
        'MediaWiki\\Widget\\ComplexTitleInputWidget' => __DIR__ . '/includes/widget/ComplexTitleInputWidget.php',
+       'MediaWiki\\Widget\\DateInputWidget' => __DIR__ . '/includes/widget/DateInputWidget.php',
        'MediaWiki\\Widget\\DateTimeInputWidget' => __DIR__ . '/includes/widget/DateTimeInputWidget.php',
        'MediaWiki\\Widget\\NamespaceInputWidget' => __DIR__ . '/includes/widget/NamespaceInputWidget.php',
        'MediaWiki\\Widget\\SearchInputWidget' => __DIR__ . '/includes/widget/SearchInputWidget.php',
index 6c47cae..cc4779e 100644 (file)
@@ -167,7 +167,7 @@ class WatchedItemStore implements StatsdAwareInterface {
         * @param User $user
         * @param LinkTarget $target
         *
-        * @return WatchedItem|null
+        * @return WatchedItem|false
         */
        private function getCached( User $user, LinkTarget $target ) {
                return $this->cache->get( $this->getCacheKey( $user, $target ) );
@@ -495,7 +495,7 @@ class WatchedItemStore implements StatsdAwareInterface {
 
                $watchedItems = [];
                foreach ( $res as $row ) {
-                       // todo these could all be cached at some point?
+                       // @todo: Should we add these to the process cache?
                        $watchedItems[] = new WatchedItem(
                                $user,
                                new TitleValue( (int)$row->wl_namespace, $row->wl_title ),
@@ -602,6 +602,7 @@ class WatchedItemStore implements StatsdAwareInterface {
                }
 
                $rows = [];
+               $items = [];
                foreach ( $targets as $target ) {
                        $rows[] = [
                                'wl_user' => $user->getId(),
@@ -609,6 +610,11 @@ class WatchedItemStore implements StatsdAwareInterface {
                                'wl_title' => $target->getDBkey(),
                                'wl_notificationtimestamp' => null,
                        ];
+                       $items[] = new WatchedItem(
+                               $user,
+                               $target,
+                               null
+                       );
                        $this->uncache( $user, $target );
                }
 
@@ -618,6 +624,12 @@ class WatchedItemStore implements StatsdAwareInterface {
                        // if there's already an entry for this page
                        $dbw->insert( 'watchlist', $toInsert, __METHOD__, 'IGNORE' );
                }
+               // Update process cache to ensure skin doesn't claim that the current
+               // page is unwatched in the response of action=watch itself (T28292).
+               // This would otherwise be re-queried from a slave by isWatched().
+               foreach ( $items as $item ) {
+                       $this->cache( $item );
+               }
 
                return true;
        }
index ca188ba..2490b9d 100644 (file)
@@ -5,6 +5,7 @@ Bartosz Dziewoński <bdziewonski@wikimedia.org>
 Brad Jorsch <bjorsch@wikimedia.org>
 Ed Sanders <esanders@wikimedia.org>
 Florian Schmidt <florian.schmidt.welzow@t-online.de>
+Geoffrey Mon <geofbot@gmail.com>
 James D. Forrester <jforrester@wikimedia.org>
 Roan Kattouw <roan@wikimedia.org>
 Sucheta Ghoshal <sghoshal@wikimedia.org>
diff --git a/includes/widget/DateInputWidget.php b/includes/widget/DateInputWidget.php
new file mode 100644 (file)
index 0000000..f011f0b
--- /dev/null
@@ -0,0 +1,165 @@
+<?php
+/**
+ * MediaWiki Widgets – DateInputWidget class.
+ *
+ * @copyright 2016 MediaWiki Widgets Team and others; see AUTHORS.txt
+ * @license The MIT License (MIT); see LICENSE.txt
+ */
+
+namespace MediaWiki\Widget;
+
+use DateTime;
+
+/**
+ * Date input widget.
+ *
+ * @since 1.29
+ */
+class DateInputWidget extends \OOUI\TextInputWidget {
+
+       protected $inputFormat = null;
+       protected $displayFormat = null;
+       protected $placeholderLabel = null;
+       protected $placeholderDateFormat = null;
+       protected $precision = null;
+       protected $mustBeAfter = null;
+       protected $mustBeBefore = null;
+       protected $overlay = null;
+
+       /**
+        * @param array $config Configuration options
+        * @param string $config['inputFormat'] Date format string to use for the textual input field.
+        *   Displayed while the widget is active, and the user can type in a date in this format.
+        *   Should be short and easy to type. (default: 'YYYY-MM-DD' or 'YYYY-MM', depending on
+        *   `precision`)
+        * @param string $config['displayFormat'] Date format string to use for the clickable label.
+        *   while the widget is inactive. Should be as unambiguous as possible (for example, prefer
+        *   to spell out the month, rather than rely on the order), even if that makes it longer.
+        *   Applicable only if the widget is infused. (default: language-specific)
+        * @param string $config['placeholderLabel'] Placeholder text shown when the widget is not
+        *   selected. Applicable only if the widget is infused. (default: taken from message
+        *   `mw-widgets-dateinput-no-date`)
+        * @param string $config['placeholderDateFormat'] User-visible date format string displayed
+        *   in the textual input field when it's empty. Should be the same as `inputFormat`, but
+        *   translated to the user's language. (default: 'YYYY-MM-DD' or 'YYYY-MM', depending on
+        *   `precision`)
+        * @param string $config['precision'] Date precision to use, 'day' or 'month' (default: 'day')
+        * @param string $config['mustBeAfter'] Validates the date to be after this.
+        *   In the 'YYYY-MM-DD' or 'YYYY-MM' format, depending on `precision`.
+        * @param string $config['mustBeBefore'] Validates the date to be before this.
+        *   In the 'YYYY-MM-DD' or 'YYYY-MM' format, depending on `precision`.
+        * @param string $config['overlay'] The jQuery selector for the overlay layer on which to render
+        *   the calendar. This configuration is useful in cases where the expanded calendar is larger
+        *   than its container. The specified overlay layer is usually on top of the container and has
+        *   a larger area. Applicable only if the widget is infused. By default, the calendar uses
+        *   relative positioning.
+        */
+       public function __construct( array $config = [] ) {
+               $config = array_merge( [
+                       // Default config values
+                       'precision' => 'day',
+               ], $config );
+
+               // Properties
+               if ( isset( $config['inputFormat'] ) ) {
+                       $this->inputFormat = $config['inputFormat'];
+               }
+               if ( isset( $config['placeholderDateFormat'] ) ) {
+                       $this->placeholderDateFormat = $config['placeholderDateFormat'];
+               }
+               $this->precision = $config['precision'];
+               if ( isset( $config['mustBeAfter'] ) ) {
+                       $this->mustBeAfter = $config['mustBeAfter'];
+               }
+               if ( isset( $config['mustBeBefore'] ) ) {
+                       $this->mustBeBefore = $config['mustBeBefore'];
+               }
+
+               // Properties stored for the infused JS widget
+               if ( isset( $config['displayFormat'] ) ) {
+                       $this->displayFormat = $config['displayFormat'];
+               }
+               if ( isset( $config['placeholderLabel'] ) ) {
+                       $this->placeholderLabel = $config['placeholderLabel'];
+               }
+               if ( isset( $config['overlay'] ) ) {
+                       $this->overlay = $config['overlay'];
+               }
+
+               // Set up placeholder text visible if the browser doesn't override it (logic taken from JS)
+               if ( $this->placeholderDateFormat !== null ) {
+                       $placeholder = $this->placeholderDateFormat;
+               } elseif ( $this->inputFormat !== null ) {
+                       // We have no way to display a translated placeholder for custom formats
+                       $placeholder = '';
+               } else {
+                       $placeholder = wfMessage( "mw-widgets-dateinput-placeholder-$this->precision" )->text();
+               }
+
+               $config = array_merge( [
+                       // Processed config values
+                       'placeholder' => $placeholder,
+               ], $config );
+
+               // Parent constructor
+               parent::__construct( $config );
+
+               // Calculate min/max attributes (which are skipped by TextInputWidget) and add to <input>
+               // min/max attributes are inclusive, but mustBeAfter/Before are exclusive
+               if ( $this->mustBeAfter !== null ) {
+                       $min = new DateTime( $this->mustBeAfter );
+                       $min = $min->modify( '+1 day' );
+                       $min = $min->format( 'Y-m-d' );
+                       $this->input->setAttributes( [ 'min' => $min ] );
+               }
+               if ( $this->mustBeBefore !== null ) {
+                       $max = new DateTime( $this->mustBeBefore );
+                       $max = $max->modify( '-1 day' );
+                       $max = $max->format( 'Y-m-d' );
+                       $this->input->setAttributes( [ 'max' => $max ] );
+               }
+
+               // Initialization
+               $this->addClasses( [ 'mw-widget-dateInputWidget' ] );
+       }
+
+       protected function getJavaScriptClassName() {
+               return 'mw.widgets.DateInputWidget';
+       }
+
+       public function getConfig( &$config ) {
+               if ( $this->inputFormat !== null ) {
+                       $config['inputFormat'] = $this->inputFormat;
+               }
+               if ( $this->displayFormat !== null ) {
+                       $config['displayFormat'] = $this->displayFormat;
+               }
+               if ( $this->placeholderLabel !== null ) {
+                       $config['placeholderLabel'] = $this->placeholderLabel;
+               }
+               if ( $this->placeholderDateFormat !== null ) {
+                       $config['placeholderDateFormat'] = $this->placeholderDateFormat;
+               }
+               if ( $this->precision !== null ) {
+                       $config['precision'] = $this->precision;
+               }
+               if ( $this->mustBeAfter !== null ) {
+                       $config['mustBeAfter'] = $this->mustBeAfter;
+               }
+               if ( $this->mustBeBefore !== null ) {
+                       $config['mustBeBefore'] = $this->mustBeBefore;
+               }
+               if ( $this->overlay !== null ) {
+                       $config['overlay'] = $this->overlay;
+               }
+               return parent::getConfig( $config );
+       }
+
+       public function getInputElement( $config ) {
+               // Inserts date/month type attribute
+               return parent::getInputElement( $config )
+                       ->setAttributes( [
+                               'type' => ( $config['precision'] === 'month' ) ? 'month' : 'date'
+                       ] );
+       }
+}
index a9b5bc3..ed251f2 100644 (file)
                        .addClass( 'mw-widget-dateInputWidget' )
                        .append( this.$handle, this.textInput.$element, this.calendar.$element );
 
+               // config.overlay is the selector to be used for config.$overlay, specified from PHP
+               if ( config.overlay ) {
+                       config.$overlay = $( config.overlay );
+               }
+
                if ( config.$overlay ) {
                        this.calendar.setFloatableContainer( this.$element );
                        config.$overlay.append( this.calendar.$element );
                this.updateUI();
                this.textInput.toggle( false );
                this.calendar.toggle( false );
+
+               // Hide unused <input> from PHP after infusion is done
+               // See InputWidget#reusePreInfuseDOM about config.$input
+               if ( config.$input ) {
+                       config.$input.addClass( 'oo-ui-element-hidden' );
+               }
        };
 
        /* Inheritance */
index 866f213..654f232 100644 (file)
                                        ? util.wikiScript() + '?title=' + util.wikiUrlencode( title ) + '&' + query
                                        : util.wikiScript() + '?' + query;
                        } else {
-                               url = mw.config.get( 'wgArticlePath' ).replace( '$1', util.wikiUrlencode( title ) );
+                               url = mw.config.get( 'wgArticlePath' )
+                                       .replace( '$1', util.wikiUrlencode( title ).replace( /\$/g, '$$$$' ) );
                        }
 
                        // Append the encoded fragment
index 4eac362..6dd17f1 100644 (file)
                } );
        } );
 
-       QUnit.test( 'getUrl', 13, function ( assert ) {
+       QUnit.test( 'getUrl', 14, function ( assert ) {
                var href;
                mw.config.set( {
                        wgScript: '/w/index.php',
                href = mw.util.getUrl( 'Foo:Sandbox? 5+5=10! (test)/sub ' );
                assert.equal( href, '/wiki/Foo:Sandbox%3F_5%2B5%3D10!_(test)/sub_', 'complex title' );
 
+               // T149767
+               href = mw.util.getUrl( 'My$$test$$$$$title' );
+               assert.equal( href, '/wiki/My$$test$$$$$title', 'title with multiple consecutive dollar signs' );
+
                href = mw.util.getUrl();
                assert.equal( href, '/wiki/Foobar', 'default title' );