Merge "RCFilters: Add an opt-out preference for filters on watchlist"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Tue, 5 Jun 2018 01:39:49 +0000 (01:39 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Tue, 5 Jun 2018 01:39:50 +0000 (01:39 +0000)
1  2 
includes/DefaultSettings.php
includes/preferences/DefaultPreferencesFactory.php
includes/specials/SpecialWatchlist.php
languages/i18n/en.json
languages/i18n/qqq.json

@@@ -2059,8 -2059,6 +2059,8 @@@ $wgDBerrorLogTZ = false
   * Even correct usage may cause failures with Unicode supplementary
   * characters (those not in the Basic Multilingual Plane) unless MySQL
   * has enhanced their Unicode support.
 + *
 + * @deprecated since 1.31
   */
  $wgDBmysql5 = false;
  
@@@ -2567,6 -2565,17 +2567,6 @@@ $wgCacheEpoch = '20030516000000'
   */
  $wgGitInfoCacheDirectory = false;
  
 -/**
 - * Bump this number when changing the global style sheets and JavaScript.
 - *
 - * It should be appended in the query string of static CSS and JS includes,
 - * to ensure that client-side caches do not keep obsolete copies of global
 - * styles.
 - *
 - * @deprecated since 1.31
 - */
 -$wgStyleVersion = '303';
 -
  /**
   * This will cache static pages for non-logged-in users to reduce
   * database traffic on public sites. ResourceLoader requests to default
@@@ -3229,8 -3238,8 +3229,8 @@@ $wgHTMLFormAllowTableFormat = true
  $wgUseMediaWikiUIEverywhere = false;
  
  /**
 - * Temporary variable that determines whether the EditPage class should use OOjs UI or not.
 - * This will be removed later and OOjs UI will become the only option.
 + * Temporary variable that determines whether Special:Preferences should use OOUI or not.
 + * This will be removed later and OOUI will become the only option.
   *
   * @since 1.32
   */
@@@ -3780,6 -3789,23 +3780,6 @@@ $wgResourceLoaderLESSVars = 
        'deviceWidthTablet' => '720px',
  ];
  
 -/**
 - * Default import paths for LESS modules. LESS files referenced in @import
 - * statements will be looked up here first, and relative to the importing file
 - * second. To avoid collisions, it's important for the LESS files in these
 - * directories to have a common, predictable file name prefix.
 - *
 - * Extensions need not (and should not) register paths in
 - * $wgResourceLoaderLESSImportPaths. The import path includes the path of the
 - * currently compiling LESS file, which allows each extension to freely import
 - * files from its own tree.
 - *
 - * @since 1.22
 - */
 -$wgResourceLoaderLESSImportPaths = [
 -      "$IP/resources/src/mediawiki.less/",
 -];
 -
  /**
   * Whether ResourceLoader should attempt to persist modules in localStorage on
   * browsers that support the Web Storage API.
@@@ -4879,6 -4905,7 +4879,7 @@@ $wgDefaultUserOptions = 
        'watchlistunwatchlinks' => 0,
        'watchmoves' => 0,
        'watchrollback' => 0,
+       'wlenhancedfilters-disable' => 0,
        'wllimit' => 250,
        'useeditwarning' => 1,
        'prefershttps' => 1,
@@@ -6017,15 -6044,6 +6018,15 @@@ $wgSessionName = false
   */
  $wgCookieSetOnAutoblock = false;
  
 +/**
 + * Whether to set a cookie when a logged-out user is blocked. Doing so means that a blocked user,
 + * even after moving to a new IP address, will still be blocked. This cookie will contain an
 + * authentication code if $wgSecretKey is set, or otherwise will just be the block ID (in which
 + * case there is a possibility of an attacker discovering the names of revdeleted users, so it
 + * is best to use this in conjunction with $wgSecretKey being set).
 + */
 +$wgCookieSetOnIpBlock = false;
 +
  /** @} */ # end of cookie settings }
  
  /************************************************************************//**
@@@ -6873,6 -6891,15 +6874,15 @@@ $wgUseRCPatrol = true
   */
  $wgStructuredChangeFiltersShowPreference = false;
  
+ /**
+  * Whether a preference is displayed for structured change filters on watchlist.
+  * Works just like $wgStructuredChangeFiltersShowPreference.
+  *
+  * Temporary variable during development and will be removed
+  * @since 1.32
+  */
+ $wgStructuredChangeFiltersShowWatchlistPreference = false;
  /**
   * Whether to enable RCFilters app on Special:Watchlist
   *
@@@ -7871,16 -7898,10 +7881,16 @@@ $wgActionFilteredLogs = 
  ];
  
  /**
 - * Maintain a log of newusers at Log/newusers?
 + * Maintain a log of newusers at Special:Log/newusers?
   */
  $wgNewUserLog = true;
  
 +/**
 + * Maintain a log of page creations at Special:Log/create?
 + * @since 1.32
 + */
 +$wgPageCreationLog = false;
 +
  /** @} */ # end logging }
  
  /*************************************************************************//**
@@@ -45,7 -45,8 +45,7 @@@ use MWTimestamp
  use OutputPage;
  use Parser;
  use ParserOptions;
 -use PreferencesForm;
 -use PreferencesFormOOUI;
 +use PreferencesFormLegacy;
  use Psr\Log\LoggerAwareTrait;
  use Psr\Log\NullLogger;
  use Skin;
@@@ -185,7 -186,9 +185,7 @@@ class DefaultPreferencesFactory impleme
                                $info['disabled'] = 'disabled';
                        }
                        $field = HTMLForm::loadInputFromParameters( $name, $info, $dummyForm ); // For validation
 -                      $globalDefault = isset( $defaultOptions[$name] )
 -                              ? $defaultOptions[$name]
 -                              : null;
 +                      $globalDefault = $defaultOptions[$name] ?? null;
  
                        // If it validates, set it as the default
                        if ( isset( $info['default'] ) ) {
                if ( ( isset( $info['type'] ) && $info['type'] == 'multiselect' ) ||
                                ( isset( $info['class'] ) && $info['class'] == \HTMLMultiSelectField::class ) ) {
                        $options = HTMLFormField::flattenOptions( $info['options'] );
 -                      $prefix = isset( $info['prefix'] ) ? $info['prefix'] : $name;
 +                      $prefix = $info['prefix'] ?? $name;
                        $val = [];
  
                        foreach ( $options as $value ) {
                                ( isset( $info['class'] ) && $info['class'] == \HTMLCheckMatrix::class ) ) {
                        $columns = HTMLFormField::flattenOptions( $info['columns'] );
                        $rows = HTMLFormField::flattenOptions( $info['rows'] );
 -                      $prefix = isset( $info['prefix'] ) ? $info['prefix'] : $name;
 +                      $prefix = $info['prefix'] ?? $name;
                        $val = [];
  
                        foreach ( $columns as $column ) {
                $languageCode = $this->config->get( 'LanguageCode' );
                if ( !array_key_exists( $languageCode, $languages ) ) {
                        $languages[$languageCode] = $languageCode;
 +                      // Sort the array again
 +                      ksort( $languages );
                }
 -              ksort( $languages );
  
                $options = [];
                foreach ( $languages as $code => $name ) {
                                'help-message' => 'prefs-help-watchlist-token2',
                        ];
                }
+               if ( $this->config->get( 'StructuredChangeFiltersShowWatchlistPreference' ) ) {
+                       $defaultPreferences['wlenhancedfilters-disable'] = [
+                               'type' => 'toggle',
+                               'section' => 'watchlist/opt-out',
+                               'label-message' => 'rcfilters-watchlist-preference-label',
+                               'help-message' => 'rcfilters-watchlist-preference-help',
+                       ];
+               }
        }
  
        /**
                $pixels = $l10n->msg( 'unit-pixel' )->text();
  
                foreach ( $this->config->get( 'ImageLimits' ) as $index => $limits ) {
 -                      // Note: A left-to-right marker (\u200e) is inserted, see T144386
 -                      $display = "{$limits[0]}" . json_decode( '"\u200e"' ) . "×{$limits[1]}" . $pixels;
 +                      // Note: A left-to-right marker (U+200E) is inserted, see T144386
 +                      $display = "{$limits[0]}\u{200E}×{$limits[1]}$pixels";
                        $ret[$display] = $index;
                }
  
         * @param IContextSource $context
         * @param string $formClass
         * @param array $remove Array of items to remove
 -       * @return PreferencesForm
 +       * @return HTMLForm
         */
        public function getForm(
                User $user,
                IContextSource $context,
 -              $formClass = PreferencesFormOOUI::class,
 +              $formClass = PreferencesFormLegacy::class,
                array $remove = []
        ) {
                if ( SpecialPreferences::isOouiEnabled( $context ) ) {
                }
  
                /**
 -               * @var $htmlForm PreferencesForm
 +               * @var $htmlForm HTMLForm
                 */
                $htmlForm = new $formClass( $formDescriptor, $context, 'prefs' );
  
                # Used message keys: 'accesskey-preferences-save', 'tooltip-preferences-save'
                $htmlForm->setSubmitTooltip( 'preferences-save' );
                $htmlForm->setSubmitID( 'prefcontrol' );
 -              $htmlForm->setSubmitCallback( function ( array $formData, PreferencesForm $form ) {
 +              $htmlForm->setSubmitCallback( function ( array $formData, HTMLForm $form ) {
                        return $this->submitForm( $formData, $form );
                } );
  
         * Handle the form submission if everything validated properly
         *
         * @param array $formData
 -       * @param PreferencesForm $form
 +       * @param HTMLForm $form
         * @return bool|Status|string
         */
 -      protected function saveFormData( $formData, PreferencesForm $form ) {
 +      protected function saveFormData( $formData, HTMLForm $form ) {
                $user = $form->getModifiedUser();
                $hiddenPrefs = $this->config->get( 'HiddenPrefs' );
                $result = true;
         * @deprecated since 1.31, its inception
         *
         * @param array $formData
 -       * @param PreferencesForm $form
 +       * @param HTMLForm $form
         * @return bool|Status|string
         */
 -      public function legacySaveFormData( $formData, PreferencesForm $form ) {
 +      public function legacySaveFormData( $formData, HTMLForm $form ) {
                return $this->saveFormData( $formData, $form );
        }
  
         * Save the form data and reload the page
         *
         * @param array $formData
 -       * @param PreferencesForm $form
 +       * @param HTMLForm $form
         * @return Status
         */
 -      protected function submitForm( array $formData, PreferencesForm $form ) {
 +      protected function submitForm( array $formData, HTMLForm $form ) {
                $res = $this->saveFormData( $formData, $form );
  
 -              if ( $res ) {
 +              if ( $res === true ) {
                        $context = $form->getContext();
 -
                        $urlOptions = [];
  
                        if ( $res === 'eauth' ) {
                        $context->getOutput()->redirect( $url );
                }
  
 -              return Status::newGood();
 +              return ( $res === true ? Status::newGood() : $res );
        }
  
        /**
         * @deprecated since 1.31, its inception
         *
         * @param array $formData
 -       * @param PreferencesForm $form
 +       * @param HTMLForm $form
         * @return Status
         */
 -      public function legacySubmitForm( array $formData, PreferencesForm $form ) {
 +      public function legacySubmitForm( array $formData, HTMLForm $form ) {
                return $this->submitForm( $formData, $form );
        }
  
@@@ -60,10 -60,11 +60,10 @@@ class SpecialWatchlist extends ChangesL
                $output = $this->getOutput();
                $request = $this->getRequest();
                $this->addHelpLink( 'Help:Watching pages' );
 +              $output->addModuleStyles( [ 'mediawiki.special' ] );
                $output->addModules( [
 -                      'mediawiki.special.changeslist.visitedstatus',
                        'mediawiki.special.watchlist',
                ] );
 -              $output->addModuleStyles( [ 'mediawiki.special.watchlist.styles' ] );
  
                $mode = SpecialEditWatchlist::getMode( $request, $subpage );
                if ( $mode !== false ) {
        }
  
        public static function checkStructuredFilterUiEnabled( Config $config, User $user ) {
-               return (
-                       $config->get( 'StructuredChangeFiltersOnWatchlist' ) &&
-                       $user->getOption( 'rcenhancedfilters' )
-               );
+               if ( !$config->get( 'StructuredChangeFiltersOnWatchlist' ) ) {
+                       return false;
+               }
+               if ( $config->get( 'StructuredChangeFiltersShowWatchlistPreference' ) ) {
+                       return !$user->getOption( 'wlenhancedfilters-disable' );
+               } else {
+                       return $user->getOption( 'rcenhancedfilters' );
+               }
        }
  
        /**
diff --combined languages/i18n/en.json
        "rcfilters-watchlist-showupdated": "Changes to pages you haven't visited since the changes occurred are in <strong>bold</strong>, with solid markers.",
        "rcfilters-preference-label": "Hide the improved version of Recent Changes",
        "rcfilters-preference-help": "Rolls back the 2017 interface redesign and all tools added then and since.",
+       "rcfilters-watchlist-preference-label": "Hide the improved version of the Watchlist",
+       "rcfilters-watchlist-preference-help": "Rolls back the 2017 interface redesign and all tools added then and since.",
        "rcfilters-filter-showlinkedfrom-label": "Show changes on pages linked from",
        "rcfilters-filter-showlinkedfrom-option-label": "<strong>Pages linked from</strong> the selected page",
        "rcfilters-filter-showlinkedto-label": "Show changes on pages linking to",
        "listusers": "User list",
        "listusers-summary": "",
        "listusers-editsonly": "Show only users with edits",
 +      "listusers-temporarygroupsonly": "Show only users in temporary user groups",
        "listusers-creationsort": "Sort by creation date",
        "listusers-desc": "Sort in descending order",
        "usereditcount": "$1 {{PLURAL:$1|edit|edits}}",
        "apisandbox-dynamic-parameters-add-label": "Add parameter:",
        "apisandbox-dynamic-parameters-add-placeholder": "Parameter name",
        "apisandbox-dynamic-error-exists": "A parameter named \"$1\" already exists.",
 +      "apisandbox-templated-parameter-reason": "This [[Special:ApiHelp/main#main/templatedparams|templated parameter]] is offered based on the {{PLURAL:$1|value|values}} of $2.",
        "apisandbox-deprecated-parameters": "Deprecated parameters",
        "apisandbox-fetch-token": "Auto-fill the token",
        "apisandbox-add-multi": "Add",
        "dellogpage": "Deletion log",
        "dellogpagetext": "Below is a list of the most recent deletions.",
        "deletionlog": "deletion log",
 +      "log-name-create": "Page creation log",
 +      "log-description-create": "Below is a list of the most recent page creations.",
 +      "logentry-create-create": "$1 {{GENDER:$2|created}} page $3",
        "reverted": "Reverted to earlier revision",
        "deletecomment": "Reason:",
        "deleteotherreason": "Other/additional reason:",
        "whatlinkshere-title": "Pages that link to \"$1\"",
        "whatlinkshere-summary": "",
        "whatlinkshere-page": "Page:",
 -      "linkshere": "The following pages link to <strong>[[:$1]]</strong>:",
 -      "nolinkshere": "No pages link to <strong>[[:$1]]</strong>.",
 -      "nolinkshere-ns": "No pages link to <strong>[[:$1]]</strong> in the chosen namespace.",
 +      "linkshere-2": "The following pages link to <strong>$2</strong>:",
 +      "nolinkshere-2": "No pages link to <strong>$2</strong>.",
 +      "nolinkshere-ns-2": "No pages link to <strong>$2</strong> in the chosen namespace.",
        "isredirect": "redirect page",
        "istemplate": "transclusion",
        "isimage": "file link",
        "pagedata-text": "This page provides a data interface to pages. Please provide the page title in the URL, using subpage syntax.\n* Content negotiation applies based on your client's Accept header. This means that the page data will be provided in the format preferred by your client.",
        "pagedata-not-acceptable": "No matching format found. Supported MIME types: $1",
        "pagedata-bad-title": "Invalid title: $1.",
 -      "unregistered-user-config": "For security reasons JavaScript, CSS and JSON user subpages cannot be loaded for unregistered users."
 +      "unregistered-user-config": "For security reasons JavaScript, CSS and JSON user subpages cannot be loaded for unregistered users.",
 +      "passwordpolicies": "Password policies",
 +      "passwordpolicies-summary": "This is a list of the effective password policies for the user groups defined in this wiki.",
 +      "passwordpolicies-group": "Group",
 +      "passwordpolicies-policies": "Policies",
 +      "passwordpolicies-policy-display": "<span class=\"passwordpolicies-policy\">$1 <code>($2)</code></span>",
 +      "passwordpolicies-policy-minimalpasswordlength": "Password must be at least $1 {{PLURAL:$1|character|characters}} long",
 +      "passwordpolicies-policy-minimumpasswordlengthtologin": "Password must be at least $1 {{PLURAL:$1|character|characters}} long to be able to login",
 +      "passwordpolicies-policy-passwordcannotmatchusername": "Password cannot be the same as username",
 +      "passwordpolicies-policy-passwordcannotmatchblacklist": "Password cannot match specifically blacklisted passwords",
 +      "passwordpolicies-policy-maximalpasswordlength": "Password must be less than $1 {{PLURAL:$1|character|characters}} long",
 +      "passwordpolicies-policy-passwordcannotbepopular": "Password cannot be {{PLURAL:$1|the popular password|in the list of $1 popular passwords}}"
  }
diff --combined languages/i18n/qqq.json
                        "Trizek (WMF)",
                        "Acamicamacaraca",
                        "Avatar6",
 -                      "Akapochtli"
 +                      "Akapochtli",
 +                      "ديفيد",
 +                      "Daimona Eaytoy"
                ]
        },
        "sidebar": "{{notranslate}}",
        "pool-servererror": "Error message. Parameters:\n* $1 - list of server addresses\n\nSee e.g. {{msg-mw|Poolcounter-desc}} (and the Pool Counter extension in general) for translation hints for “pool counter service”.",
        "poolcounter-usage-error": "Used as error message. Parameters:\n* $1 - non-localized string describing usage mistake.",
        "aboutsite": "Used as the label of the link that appears at the footer of every page on the wiki (in most of  the skins) and leads to the page that contains the site description. The link target is {{msg-mw|aboutpage}}.\n\n[[mw:Manual:Interface/Aboutsite|MediaWiki manual]].\n\n{{doc-important|Do not change <nowiki>{{SITENAME}}</nowiki>.}}\n\n{{Identical|About}}",
 -      "aboutpage": "Used as the target of the link that appears at the footer of every page on the wiki (in most of  the skins) and leads to the page that contains the site description. Therefore the content should be the same with the page name of the site description page. Only the message in the [[mw:Manual:$wgLanguageCode|site language]]  ([[MediaWiki:Aboutpage]]) is used. The link label is {{msg-mw|aboutsite}}.\n\n{{doc-important|Do not translate \"Project:\" part, for this is the namespace prefix.}}",
 +      "aboutpage": "{{doc-important|Do not translate \"Project:\" part, for this is the namespace prefix.}}\n\nUsed as the target of the link that appears at the footer of every page on the wiki (in most of  the skins) and leads to the page that contains the site description. Therefore the content should be the same with the page name of the site description page. Only the message in the [[mw:Manual:$wgLanguageCode|site language]]  ([[MediaWiki:Aboutpage]]) is used. The link label is {{msg-mw|aboutsite}}.",
        "copyright": "Parameters:\n* $1 - license name\n'''See also'''\n* {{msg-mw|Mobile-frontend-copyright}}",
        "copyrightpage": "{{doc-important|Do not change <nowiki>{{ns:project}}</nowiki>}}\n\n{{Identical|Copyright}}",
        "currentevents": "Standard link in the sidebar, for news.\n\nSee also {{msg-mw|Currentevents-url}} for the link URL.\n\nSee also:\n* {{msg-mw|Currentevents}}\n* {{msg-mw|Accesskey-n-currentevents}}\n* {{msg-mw|Tooltip-n-currentevents}}",
        "expansion-depth-exceeded-category": "This message is used as a category name for a [[mw:Help:Tracking categories|tracking category]] where pages are placed automatically if the [[meta:Help:Expansion_depth|expansion depth]] of the preprocessor exceeds the limit.",
        "expansion-depth-exceeded-category-desc": "Expansion depth exceeded category description. Shown on [[Special:TrackingCategories]].\n\nSee also:\n* {{msg-mw|Expansion-depth-exceeded-category}}",
        "expansion-depth-exceeded-warning": "Error message shown when a page exceeded the [[meta:Help:Expansion_depth|expansion depth limit]] of the preprocessor.\n\nParameters:\n* $1 - (Unused) the value of the depth limit\n* $2 - (Unused) the value of the max depth limit\nSee also:\n* {{msg-mw|Expansion-depth-exceeded-category}}",
 -      "parser-unstrip-loop-warning": "{{Doc-important|Do not translate function name <code>unstrip</code>.}}\nThis error is shown when a parser extension tag such as <code><nowiki><pre></nowiki></code> includes a reference to itself in its own output.\n\nThe reference must be to the exact same invocation of the tag at the same location in the source, merely writing <code><nowiki><pre><pre></pre></pre></nowiki></code> will not do it.\n\nThis is usually impossible and unlikely to happen by accident, so translation is not essential.\n\n\"Unstrip\" refers to the internal function of the parser, called \"unstrip\", which recursively puts the output of parser functions in the place of the parser function call and which would enter an infinite loop in the situation above.\n\nSee also:\n*{{msg-mw|Parser-unstrip-recursion-limit}}",
 -      "unstrip-depth-warning": "{{doc-important|Do not translate function name <code>unstrip</code>.}}\nThis message is shown when the recursion limit for nested parser extension tags is exceeded.\n\nThis warning may be encountered due to input text like <code><nowiki><ref><ref><ref>...</ref></ref></ref></nowiki></code>.\n\nParameters:\n* $1 - the depth limit\n\n\"Unstrip\" refers to the internal function of the parser, called 'unstrip', which recursively puts the output of parser functions in the place of the parser function call.\n\nSee also:\n* {{msg-mw|Parser-unstrip-loop-warning}}",
 -      "unstrip-depth-category": "This message is used as the category name of a [[mw:Help:Tracking categories|tracking category]] in which pages are placed automatically if the unstrip recursion depth limit is exceeded.",
 -      "unstrip-size-warning": "{{doc-important|Do not translate function name <code>unstrip</code>.}}\nThis message is shown when the maximum expansion size for nested parser extension tags is exceeded.\n\nParameters:\n* $1 - the size limit\n\n\"Unstrip\" refers to the internal function of the parser, called 'unstrip', which recursively puts the output of parser functions in the place of the parser function call.\n\nSee also:\n* {{msg-mw|Parser-unstrip-loop-warning}}",
 -      "unstrip-size-category": "This message is used as the category name of a [[mw:Help:Tracking categories|tracking category]] in which pages are placed automatically if the unstrip expansion size limit is exceeded.",
 +      "parser-unstrip-loop-warning": "{{Doc-important|Do not translate function name <code>unstrip</code>.}}\nThis error is shown when a parser extension tag such as <code><nowiki><pre></nowiki></code> includes a reference to itself in its own output.\n\nThe reference must be to the exact same invocation of the tag at the same location in the source, merely writing <code><nowiki><pre><pre></pre></pre></nowiki></code> will not do it.\n\nThis is usually impossible and unlikely to happen by accident, so translation is not essential.\n\n\"Unstrip\" refers to the internal function of the parser, called \"unstrip\", which recursively puts the output of parser functions in the place of the parser function call and which would enter an infinite loop in the situation above.\n\nSee also:\n*{{msg-mw|Limitreport-unstrip-depth}}\n*{{msg-mw|Limitreport-unstrip-size}}\n*{{msg-mw|Unstrip-depth-category}}\n*{{msg-mw|Unstrip-depth-warning}}\n*{{msg-mw|Unstrip-size-category}}\n*{{msg-mw|Unstrip-size-warning}}",
 +      "unstrip-depth-warning": "{{doc-important|Do not translate function name <code>unstrip</code>.}}\nThis message is shown when the recursion limit for nested parser extension tags is exceeded.\n\nThis warning may be encountered due to input text like <code><nowiki><ref><ref><ref>...</ref></ref></ref></nowiki></code>.\n\nParameters:\n* $1 - the depth limit\n\n\"Unstrip\" refers to the internal function of the parser, called 'unstrip', which recursively puts the output of parser functions in the place of the parser function call.\n\nSee also:\n*{{msg-mw|Limitreport-unstrip-depth}}\n*{{msg-mw|Limitreport-unstrip-size}}\n*{{msg-mw|Parser-unstrip-loop-warning}}\n*{{msg-mw|Unstrip-depth-category}}\n*{{msg-mw|Unstrip-size-category}}\n*{{msg-mw|Unstrip-size-warning}}",
 +      "unstrip-depth-category": "{{Doc-important|Do not translate function name <code>unstrip</code>.}}\nThis message is used as the category name of a [[mw:Help:Tracking categories|tracking category]] in which pages are placed automatically if the unstrip recursion depth limit is exceeded.\n\n\"Unstrip\" refers to the internal function of the parser, called \"unstrip\", which recursively puts the output of parser functions in the place of the parser function call.\n\nSee also:\n*{{msg-mw|Limitreport-unstrip-depth}}\n*{{msg-mw|Limitreport-unstrip-size}}\n*{{msg-mw|Parser-unstrip-loop-warning}}\n*{{msg-mw|Unstrip-depth-warning}}\n*{{msg-mw|Unstrip-size-category}}\n*{{msg-mw|Unstrip-size-warning}}",
 +      "unstrip-size-warning": "{{doc-important|Do not translate function name <code>unstrip</code>.}}\nThis message is shown when the maximum expansion size for nested parser extension tags is exceeded.\n\nParameters:\n* $1 - the size limit\n\n\"Unstrip\" refers to the internal function of the parser, called 'unstrip', which recursively puts the output of parser functions in the place of the parser function call.\n\nSee also:\n*{{msg-mw|Limitreport-unstrip-depth}}\n*{{msg-mw|Limitreport-unstrip-size}}\n*{{msg-mw|Parser-unstrip-loop-warning}}\n*{{msg-mw|Unstrip-depth-category}}\n*{{msg-mw|Unstrip-depth-warning}}\n*{{msg-mw|Unstrip-size-category}}",
 +      "unstrip-size-category": "{{Doc-important|Do not translate function name <code>unstrip</code>.}}\nThis message is used as the category name of a [[mw:Help:Tracking categories|tracking category]] in which pages are placed automatically if the unstrip expansion size limit is exceeded.\n\n\"Unstrip\" refers to the internal function of the parser, called \"unstrip\", which recursively puts the output of parser functions in the place of the parser function call.\n\nSee also:\n*{{msg-mw|Limitreport-unstrip-depth}}\n*{{msg-mw|Limitreport-unstrip-size}}\n*{{msg-mw|Parser-unstrip-loop-warning}}\n*{{msg-mw|Unstrip-depth-category}}\n*{{msg-mw|Unstrip-depth-warning}}\n*{{msg-mw|Unstrip-size-warning}}",
        "converter-manual-rule-error": "Used as error message when a manual conversion rule for the [[mw:Language_converter|language converter]] has errors. For example it's not using the correct syntax, or not supplying text in all variants.",
        "undo-success": "Text on special page to confirm edit revert. You arrive on this page by clicking on the \"undo\" link on a revision history special page.\n\n{{Identical|Undo}}",
        "undo-failure": "Message appears if an attempt to revert an edit by clicking the \"undo\" link on the page history fails.\n\nSee also:\n* {{msg-mw|Undo-norev}}\n* {{msg-mw|Undo-nochange}}\n{{Identical|Undo}}",
        "rcfilters-watchlist-showupdated": "Message at the top of [[Special:Watchlist]] when the Structured filters are enabled that describes what unseen changes look like.\n\nCf. {{msg-mw|wlheader-showupdated}}",
        "rcfilters-preference-label": "Option in RecentChanges tab of [[Special:Preferences]].",
        "rcfilters-preference-help": "Explanation for the option in the RecentChanges tab of [[Special:Preferences]].",
+       "rcfilters-watchlist-preference-label": "Option in Watchlist tab of [[Special:Preferences]].",
+       "rcfilters-watchlist-preference-help": "Explanation for the option in the Watchlist tab of [[Special:Preferences]].",
        "rcfilters-filter-showlinkedfrom-label": "Label that indicates that the page is showing changes that link FROM the target page. Used on [[Special:Recentchangeslinked]] when structured filters are enabled.",
        "rcfilters-filter-showlinkedfrom-option-label": "Menu option to show changes FROM the target page. Used on [[Special:Recentchangeslinked]] when structured filters are enabled.",
        "rcfilters-filter-showlinkedto-label": "Label that indicates that the page is showing changes that link TO the target page. Used on [[Special:Recentchangeslinked]] when structured filters are enabled.",
        "listusers": "{{doc-special|ListUsers}}",
        "listusers-summary": "{{notranslate}}\nThe summary displayed at the top of [[Special:Listusers]]. [[mw:Manual:Interface/Special pages summary|mw manual]].",
        "listusers-editsonly": "Option in [[Special:ListUsers]].",
 +      "listusers-temporarygroupsonly": "Option in [[Special:ListUsers]].",
        "listusers-creationsort": "Option in [[Special:ListUsers]].",
        "listusers-desc": "Used as label for the checkbox on [[Special:ListUsers]].",
        "usereditcount": "Shown behind every username on [[Special:ListUsers]]. Parameters:\n* $1 - number of edits",
        "apisandbox-dynamic-parameters-add-label": "JavaScript label for the widget to add a new arbitrary parameter.",
        "apisandbox-dynamic-parameters-add-placeholder": "JavaScript text field placeholder for the widget to add a new arbitrary parameter.",
        "apisandbox-dynamic-error-exists": "Displayed as an error message from JavaScript when trying to add a new arbitrary parameter with a name that already exists. Parameters:\n* $1 - Parameter name that failed.",
 +      "apisandbox-templated-parameter-reason": "Displayed (from JavaScript) on each instance of a templated parameter.\n\nParameters:\n* $1 - Number of fields in $2.\n* $2 - List of targeted fields, combined using {{msg-mw|comma-separator}} and {{msg-mw|and}}.",
        "apisandbox-deprecated-parameters": "JavaScript button label and fieldset legend for separating deprecated parameters in the UI.",
        "apisandbox-fetch-token": "Label for the button that fetches a CSRF token.",
        "apisandbox-add-multi": "Label for the button to add another value to a field that accepts multiple values\n{{Identical|Add}}",
        "dellogpage": "{{doc-logpage}}\n\nThe name of the deletion log. Used as heading on [[Special:Log/delete]] and in the drop down menu for selecting logs on [[Special:Log]].\n{{Identical|Deletion log}}",
        "dellogpagetext": "Text in [[Special:Log/delete]].",
        "deletionlog": "Used as text for the link which points to the deletion log:\n* Used as <code>$1</code> in {{msg-mw|Filewasdeleted}}\n* Used as <code>$2</code> in {{msg-mw|Deletedtext}}\n* Used in log lines on [[Special:DeletedContributions]]\n{{Identical|Deletion log}}",
 +      "log-name-create": "{{doc-logpage}}\n\nThe name of the page creation log. Used as heading on [[Special:Log/create]] and in the drop down menu for selecting logs on [[Special:Log]].",
 +      "log-description-create": "Text in [[Special:Log/create]].",
 +      "logentry-create-create": "{{Logentry|[[Special:Log/create]]}}",
        "reverted": "{{Identical|Revert}}",
        "deletecomment": "{{Identical|Reason}}",
        "deleteotherreason": "{{Identical|Other/additional reason}}",
        "whatlinkshere-title": "Title of the special page [[Special:WhatLinksHere]]. This page appears when you click on the 'What links here' button in the toolbox. $1 is the name of the page concerned.",
        "whatlinkshere-summary": "{{doc-specialpagesummary|whatlinkshere}}",
        "whatlinkshere-page": "{{Identical|Page}}",
 -      "linkshere": "This message is the header line of the [[Special:WhatLinksHere/$1]] page generated by clicking \"What links here\" in the sidebar toolbox.\n\nIt is followed by a navigation bar built using {{msg-mw|Viewprevnext}}.\n\nParameters:\n* $1 - page title",
 -      "nolinkshere": "Used in [[Special:WhatLinksHere]] if empty. Parameters:\n* $1 - page title\nSee also:\n* {{msg-mw|Nolinkshere-ns}}",
 -      "nolinkshere-ns": "Used in [[Special:WhatLinksHere]] if empty. Parameters:\n* $1 - page title\nSee also:\n* {{msg-mw|Nolinkshere}}",
 +      "linkshere-2": "This message is the header line of the [[Special:WhatLinksHere/$1]] page generated by clicking \"What links here\" in the sidebar toolbox.\n\nIt is followed by a navigation bar built using {{msg-mw|Viewprevnext}}.\n\nParameters:\n* $1 - Plain page title\n* $2 - HTML link to the page.",
 +      "nolinkshere-2": "Used in [[Special:WhatLinksHere]] if empty. Parameters:\n* $1 - Plain page title\n* $2 - HTML link to the page.\nSee also:\n* {{msg-mw|Nolinkshere-ns-2}}",
 +      "nolinkshere-ns-2": "Used in [[Special:WhatLinksHere]] if empty. Parameters:\n* $1 - Plain page title\n* $2 - HTML link to the page.\nSee also:\n* {{msg-mw|Nolinkshere-2}}",
        "isredirect": "Displayed in [[Special:WhatLinksHere]] (see [{{fullurl:Special:WhatLinksHere/Project:Translator|hidelinks=1}} Special:WhatLinksHere/Project:Translator] for example).\n\n{{Identical|Redirect page}}",
        "istemplate": "Means that a page (a template, specifically) is used as <code><nowiki>{{Page name}}</nowiki></code>.\nDisplayed in [[Special:WhatLinksHere]] (see [[Special:WhatLinksHere/Template:New portal]] for example).\nIf you are not sure how to translate this term, think of something like \"inclusion\", \"embedding\", or \"insertion\".\n{{Identical|Transclusion}}",
        "isimage": "This message is displayed on [[Special:WhatLinksHere]] for images. It means that the image is used on the page (as opposed to just being linked to like an non-image page).\n{{Identical|File link}}",
        "limitreport-expansiondepth-value": "{{optional}}\nFormat for the \"Highest expansion depth\" row in the limit report table.\n\nParameters:\n* $1 - the depth\n* $2 - the maximum",
        "limitreport-expensivefunctioncount": "Label for the \"Expensive parser function count\" row in the limit report table",
        "limitreport-expensivefunctioncount-value": "{{optional}}\nFormat for the \"Expensive parser function count\" row in the limit report table.\n\nParameters:\n* $1 - the usage\n* $2 - the maximum",
 -      "limitreport-unstrip-depth": "Label for the \"unstrip depth\" row in the limit report table. \"Unstrip\" is a MediaWiki function name and as such does not need to be translated.",
 +      "limitreport-unstrip-depth": "{{Doc-important|Do not translate function name <code>unstrip</code>.}}\nLabel for the \"unstrip depth\" row in the limit report table.\n\n\"Unstrip\" refers to the internal function of the parser, called \"unstrip\", which recursively puts the output of parser functions in the place of the parser function call.\n\nSee also:\n*{{msg-mw|Limitreport-unstrip-size}}\n*{{msg-mw|Parser-unstrip-loop-warning}}\n*{{msg-mw|Unstrip-depth-category}}\n*{{msg-mw|Unstrip-depth-warning}}\n*{{msg-mw|Unstrip-size-category}}\n*{{msg-mw|Unstrip-size-warning}}",
        "limitreport-unstrip-depth-value": "{{optional}}\nFormat for the \"unstrip depth\" row in the limit report table.\n\nParameters:\n* $1 - the usage\n* $2 - the maximum",
 -      "limitreport-unstrip-size": "Label for the \"unstrip size\" row in the limit report table. \"Unstrip\" is a MediaWiki function name and as such does not need to be translated.",
 +      "limitreport-unstrip-size": "{{Doc-important|Do not translate function name <code>unstrip</code>.}}\nLabel for the \"unstrip size\" row in the limit report table.\n\n\"Unstrip\" refers to the internal function of the parser, called \"unstrip\", which recursively puts the output of parser functions in the place of the parser function call.\n\nSee also:\n*{{msg-mw|Limitreport-unstrip-depth}}\n*{{msg-mw|Parser-unstrip-loop-warning}}\n*{{msg-mw|Unstrip-depth-category}}\n*{{msg-mw|Unstrip-depth-warning}}\n*{{msg-mw|Unstrip-size-category}}\n*{{msg-mw|Unstrip-size-warning}}",
        "limitreport-unstrip-size-value": "Format for the \"unstrip size\" row in the limit report table.\n\nParameters:\n* $1 - the usage\n* $2 - the maximum\n{{Identical|Byte}}",
        "expandtemplates": "{{doc-special|ExpandTemplates}}\nThe name of the [[mw:Extension:ExpandTemplates|Expand Templates extension]].",
        "expand_templates_intro": "This is the explanation given in the heading of the [[Special:ExpandTemplates]] page; it describes its functionality to the users.\nFor more information, see [[mw:Extension:ExpandTemplates]]",
        "mediastatistics-header-3d": "Header on [[Special:MediaStatistics]] for file types that are in the 3D category. Includes STL files.",
        "mediastatistics-header-total": "Header on [[Special:MediaStatistics]] for a summary of all file types.",
        "json-warn-trailing-comma": "A warning message notifying that JSON text was automatically corrected by removing erroneous commas.\n\nParameters:\n* $1 - number of commas that were removed\n{{Related|Json-error}}",
 -      "json-error-unknown": "User error message when there’s an unknown error.\n\nThis error is shown if we received an unexpected value from PHP. See http://php.net/manual/en/function.json-last-error.php\n\nParameters:\n* $1 - integer error code\n{{Related|Json-error}}",
 -      "json-error-depth": "User error message when the maximum stack depth is exceeded.\nSee http://php.net/manual/en/function.json-last-error.php\n{{Related|Json-error}}",
 -      "json-error-state-mismatch": "User error message when underflow or the modes mismatch.\n\n'''Underflow''': A data-processing error arising when the absolute value of a computed quantity is smaller than the limits of precision of the computing device, retaining at least one significant digit.\n\nSee http://php.net/manual/en/function.json-last-error.php\n{{Related|Json-error}}",
 -      "json-error-ctrl-char": "User error message when an unexpected control character has been found.\nSee http://php.net/manual/en/function.json-last-error.php\n{{Related|Json-error}}",
 -      "json-error-syntax": "User error message when there is a syntax error; a malformed JSON.\nSee http://php.net/manual/en/function.json-last-error.php\n{{Related|Json-error}}\n{{Identical|Syntax error}}",
 -      "json-error-utf8": "User error message when there are malformed UTF-8 characters, possibly incorrectly encoded.\nSee http://php.net/manual/en/function.json-last-error.php\n{{Related|Json-error}}",
 -      "json-error-recursion": "PHP JSON encoding/decoding error. See http://php.net/manual/en/function.json-last-error.php\n{{Related|Json-error}}",
 -      "json-error-inf-or-nan": "PHP JSON encoding/decoding error. See http://php.net/manual/en/function.json-last-error.php\n{{Related|Json-error}}",
 -      "json-error-unsupported-type": "PHP JSON encoding/decoding error. See http://php.net/manual/en/function.json-last-error.php\n{{Related|Json-error}}",
 +      "json-error-unknown": "User error message when there’s an unknown error.\n\nThis error is shown if we received an unexpected value from PHP. See https://secure.php.net/manual/en/function.json-last-error.php\n\nParameters:\n* $1 - integer error code\n{{Related|Json-error}}",
 +      "json-error-depth": "User error message when the maximum stack depth is exceeded.\nSee https://secure.php.net/manual/en/function.json-last-error.php\n{{Related|Json-error}}",
 +      "json-error-state-mismatch": "User error message when underflow or the modes mismatch.\n\n'''Underflow''': A data-processing error arising when the absolute value of a computed quantity is smaller than the limits of precision of the computing device, retaining at least one significant digit.\n\nSee https://secure.php.net/manual/en/function.json-last-error.php\n{{Related|Json-error}}",
 +      "json-error-ctrl-char": "User error message when an unexpected control character has been found.\nSee https://secure.php.net/manual/en/function.json-last-error.php\n{{Related|Json-error}}",
 +      "json-error-syntax": "User error message when there is a syntax error; a malformed JSON.\nSee https://secure.php.net/manual/en/function.json-last-error.php\n{{Related|Json-error}}\n{{Identical|Syntax error}}",
 +      "json-error-utf8": "User error message when there are malformed UTF-8 characters, possibly incorrectly encoded.\nSee https://secure.php.net/manual/en/function.json-last-error.php\n{{Related|Json-error}}",
 +      "json-error-recursion": "PHP JSON encoding/decoding error. See https://secure.php.net/manual/en/function.json-last-error.php\n{{Related|Json-error}}",
 +      "json-error-inf-or-nan": "PHP JSON encoding/decoding error. See https://secure.php.net/manual/en/function.json-last-error.php\n{{Related|Json-error}}",
 +      "json-error-unsupported-type": "PHP JSON encoding/decoding error. See https://secure.php.net/manual/en/function.json-last-error.php\n{{Related|Json-error}}",
        "headline-anchor-title": "Title tooltip for the section anchor symbol, which is a link to the current section. Can be interpreted both as a noun (\"this is a link\") or as a verb (\"use this to link\").",
        "special-characters-group-latin": "This is the name of a script, or alphabet, not a language.",
        "special-characters-group-latinextended": "The name of the Latin Extended character set.",
        "pagedata-text": "Error shown when none of the formats acceptable to the client is supported (HTTP error 406). Parameters:\n* $1 - the list of supported MIME types",
        "pagedata-not-acceptable": "No matching format found. Supported MIME types: $1",
        "pagedata-bad-title": "Error shown when the requested title is invalid. Parameters:\n* $1: the malformed ID",
 -      "unregistered-user-config": "Shown when viewing a user JS, CSS or JSON subpage with ?action=raw&ctype=<mime type> where there is no such user. It is shown as a paragraph after a header saying 'Forbidden'."
 +      "unregistered-user-config": "Shown when viewing a user JS, CSS or JSON subpage with ?action=raw&ctype=<mime type> where there is no such user. It is shown as a paragraph after a header saying 'Forbidden'.",
 +      "passwordpolicies": "The name of the special page [[Special:PasswordPolicies]].",
 +      "passwordpolicies-summary": "The description used on [[Special:PasswordPolicies]].\n\nRefers to {{msg-mw|Passwordpolicies-helppage}}.",
 +      "passwordpolicies-group": "The title of the column in the table, about user groups (like you are in the ''translator'' group).\n\n{{Identical|Group}}\n{{Related|Passwordpolicies}}",
 +      "passwordpolicies-policies": "The title of the column in the table, about password policies.\n{{Related|Passwordpolicies}}",
 +      "passwordpolicies-policy-display": "{{optional}}\nParameters:\n* $1 - the text from the \"passwordpolicies-policy-...\" messages, i.e. {{msg-mw|passwordpolicies-policy-minimalpasswordlength}}\n* $2 - the name of this password policy",
 +      "passwordpolicies-policy-minimalpasswordlength": "Password policy that enforces a minimum number of characters a password must be. $1 - minimum number of characters that a password can be",
 +      "passwordpolicies-policy-minimumpasswordlengthtologin": "Password policy that enforces a minimum number of characters a password must be to be able to login to the wiki. $1 - minimum number of characters that a password can be to be able to login",
 +      "passwordpolicies-policy-passwordcannotmatchusername": "Password policy that enforces that the password of the account cannot be the same as the username",
 +      "passwordpolicies-policy-passwordcannotmatchblacklist": "Password policy that enforces that passwords are not on a list of blacklisted passwords (often previously used during MediaWiki automated testing)",
 +      "passwordpolicies-policy-maximalpasswordlength": "Password policy that enforces a maximum number of characters a password must be. $1 - maximum number of characters that a password can be",
 +      "passwordpolicies-policy-passwordcannotbepopular": "Password policy that enforces that a password is not in a list of $1 number of \"popular\" passwords. $1 - number of popular passwords the password will be checked against"
  }