Merge "Enforce partial blocks"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Wed, 24 Oct 2018 03:15:39 +0000 (03:15 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Wed, 24 Oct 2018 03:15:39 +0000 (03:15 +0000)
1  2 
includes/api/i18n/en.json
includes/api/i18n/qqq.json
includes/specials/SpecialEmailuser.php
languages/i18n/en.json
languages/i18n/qqq.json

@@@ -40,8 -40,6 +40,8 @@@
        "apihelp-block-param-reblock": "If the user is already blocked, overwrite the existing block.",
        "apihelp-block-param-watchuser": "Watch the user's or IP address's user and talk pages.",
        "apihelp-block-param-tags": "Change tags to apply to the entry in the block log.",
 +      "apihelp-block-param-partial": "Block user from specific pages or namespaces rather than the entire site.",
 +      "apihelp-block-param-pagerestrictions": "List of titles to block the user from editing. Only applies when 'partial' is set to true.",
        "apihelp-block-example-ip-simple": "Block IP address <kbd>192.0.2.5</kbd> for three days with reason <kbd>First strike</kbd>.",
        "apihelp-block-example-user-complex": "Block user <kbd>Vandal</kbd> indefinitely with reason <kbd>Vandalism</kbd>, and prevent new account creation and email sending.",
  
        "apierror-bad-watchlist-token": "Incorrect watchlist token provided. Please set a correct token in [[Special:Preferences]].",
        "apierror-blockedfrommail": "You have been blocked from sending email.",
        "apierror-blocked": "You have been blocked from editing.",
+       "apierror-blocked-partial": "You have been blocked from editing this page.",
        "apierror-botsnotsupported": "This interface is not supported for bots.",
        "apierror-cannot-async-upload-file": "The parameters <var>async</var> and <var>file</var> cannot be combined. If you want asynchronous processing of your uploaded file, first upload it to stash (using the <var>stash</var> parameter) and then publish the stashed file asynchronously (using <var>filekey</var> and <var>async</var>).",
        "apierror-cannotreauthenticate": "This action is not available as your identity cannot be verified.",
@@@ -48,8 -48,6 +48,8 @@@
        "apihelp-block-param-reblock": "{{doc-apihelp-param|block|reblock}}",
        "apihelp-block-param-watchuser": "{{doc-apihelp-param|block|watchuser}}",
        "apihelp-block-param-tags": "{{doc-apihelp-param|block|tags}}",
 +      "apihelp-block-param-partial": "{{doc-apihelp-param|block|partial}}",
 +      "apihelp-block-param-pagerestrictions": "{{doc-apihelp-param|block|pagerestrictions}}",
        "apihelp-block-example-ip-simple": "{{doc-apihelp-example|block}}",
        "apihelp-block-example-user-complex": "{{doc-apihelp-example|block}}",
        "apihelp-changeauthenticationdata-summary": "{{doc-apihelp-summary|changeauthenticationdata}}",
        "apierror-bad-watchlist-token": "{{doc-apierror}}",
        "apierror-blockedfrommail": "{{doc-apierror}}",
        "apierror-blocked": "{{doc-apierror}}",
+       "apierror-blocked-partial": "{{doc-apierror}}",
        "apierror-botsnotsupported": "{{doc-apierror}}",
        "apierror-cannot-async-upload-file": "{{doc-apierror}}",
        "apierror-cannotreauthenticate": "{{doc-apierror}}",
@@@ -92,6 -92,7 +92,6 @@@ class SpecialEmailUser extends Unlisted
                        'Text' => [
                                'type' => 'textarea',
                                'rows' => 20,
 -                              'cols' => 80,
                                'label-message' => 'emailmessage',
                                'required' => true,
                        ],
  
        public function execute( $par ) {
                $out = $this->getOutput();
 +              $request = $this->getRequest();
                $out->addModuleStyles( 'mediawiki.special' );
  
                $this->mTarget = is_null( $par )
 -                      ? $this->getRequest()->getVal( 'wpTarget', $this->getRequest()->getVal( 'target', '' ) )
 +                      ? $request->getVal( 'wpTarget', $request->getVal( 'target', '' ) )
                        : $par;
  
 +              // Make sure, that HTMLForm uses the correct target.
 +              $request->setVal( 'wpTarget', $this->mTarget );
 +
                // This needs to be below assignment of $this->mTarget because
                // getDescription() needs it to determine the correct page title.
                $this->setHeaders();
                        case 'badaccess':
                                throw new PermissionsError( 'sendemail' );
                        case 'blockedemailuser':
-                               throw new UserBlockedError( $this->getUser()->mBlock );
+                               throw $this->getBlockedEmailError();
                        case 'actionthrottledtext':
                                throw new ThrottledError;
                        case 'mailnologin':
                                list( $title, $msg, $params ) = $error;
                                throw new ErrorPageError( $title, $msg, $params );
                }
 -              // Got a valid target user name? Else ask for one.
 -              $ret = self::getTarget( $this->mTarget, $this->getUser() );
 -              if ( !$ret instanceof User ) {
 -                      if ( $this->mTarget != '' ) {
 -                              // Messages used here: notargettext, noemailtext, nowikiemailtext
 -                              $ret = ( $ret == 'notarget' ) ? 'emailnotarget' : ( $ret . 'text' );
 -                              $out->wrapWikiMsg( "<p class='error'>$1</p>", $ret );
 -                      }
 -                      $out->addHTML( $this->userForm( $this->mTarget ) );
 -
 -                      return;
 -              }
 -
 -              $this->mTargetObj = $ret;
 -
 -              // Set the 'relevant user' in the skin, so it displays links like Contributions,
 -              // User logs, UserRights, etc.
 -              $this->getSkin()->setRelevantUser( $this->mTargetObj );
  
 +              // Make sure, that a submitted form isn't submitted to a subpage (which could be
 +              // a non-existing username)
                $context = new DerivativeContext( $this->getContext() );
                $context->setTitle( $this->getPageTitle() ); // Remove subpage
 -              $form = new HTMLForm( $this->getFormFields(), $context );
 -              // By now we are supposed to be sure that $this->mTarget is a user name
 -              $form->addPreText( $this->msg( 'emailpagetext', $this->mTarget )->parse() );
 -              $form->setSubmitTextMsg( 'emailsend' );
 -              $form->setSubmitCallback( [ __CLASS__, 'uiSubmit' ] );
 -              $form->setWrapperLegendMsg( 'email-legend' );
 -              $form->loadData();
 -
 -              if ( !Hooks::run( 'EmailUserForm', [ &$form ] ) ) {
 -                      return;
 -              }
 -
 -              $result = $form->show();
 -
 -              if ( $result === true || ( $result instanceof Status && $result->isGood() ) ) {
 -                      $out->setPageTitle( $this->msg( 'emailsent' ) );
 -                      $out->addWikiMsg( 'emailsenttext', $this->mTarget );
 -                      $out->returnToMain( false, $this->mTargetObj->getUserPage() );
 +              $this->setContext( $context );
 +
 +              // A little hack: HTMLForm will check $this->mTarget only, if the form was posted, not
 +              // if the user opens Special:EmailUser/Florian (e.g.). So check, if the user did that
 +              // and show the "Send email to user" form directly, if so. Show the "enter username"
 +              // form, otherwise.
 +              $this->mTargetObj = self::getTarget( $this->mTarget, $this->getUser() );
 +              if ( !$this->mTargetObj instanceof User ) {
 +                      $this->userForm( $this->mTarget );
 +              } else {
 +                      $this->sendEmailForm();
                }
        }
  
         * @return string Form asking for user name.
         */
        protected function userForm( $name ) {
 -              $this->getOutput()->addModules( 'mediawiki.userSuggest' );
 -              $string = Html::openElement(
 -                              'form',
 -                              [ 'method' => 'get', 'action' => wfScript(), 'id' => 'askusername' ]
 -                      ) .
 -                      Html::hidden( 'title', $this->getPageTitle()->getPrefixedText() ) .
 -                      Html::openElement( 'fieldset' ) .
 -                      Html::rawElement( 'legend', null, $this->msg( 'emailtarget' )->parse() ) .
 -                      Html::label(
 -                              $this->msg( 'emailusername' )->text(),
 -                              'emailusertarget'
 -                      ) . "\u{00A0}" .
 -                      Html::input(
 -                              'target',
 -                              $name,
 -                              'text',
 -                              [
 -                                      'id' => 'emailusertarget',
 -                                      'class' => 'mw-autocomplete-user', // used by mediawiki.userSuggest
 -                                      'autofocus' => true,
 -                                      'size' => 30,
 -                              ]
 -                      ) .
 -                      ' ' .
 -                      Html::submitButton( $this->msg( 'emailusernamesubmit' )->text(), [] ) .
 -                      Html::closeElement( 'fieldset' ) .
 -                      Html::closeElement( 'form' ) . "\n";
 -
 -              return $string;
 +              $htmlForm = HTMLForm::factory( 'ooui', [
 +                      'Target' => [
 +                              'type' => 'user',
 +                              'exists' => true,
 +                              'label' => $this->msg( 'emailusername' )->text(),
 +                              'id' => 'emailusertarget',
 +                              'autofocus' => true,
 +                              'value' => $name,
 +                      ]
 +              ], $this->getContext() );
 +
 +              $htmlForm
 +                      ->setMethod( 'post' )
 +                      ->setSubmitCallback( [ $this, 'sendEmailForm' ] )
 +                      ->setFormIdentifier( 'userForm' )
 +                      ->setId( 'askusername' )
 +                      ->setWrapperLegendMsg( 'emailtarget' )
 +                      ->setSubmitTextMsg( 'emailusernamesubmit' )
 +                      ->show();
        }
  
 -      /**
 -       * Submit callback for an HTMLForm object, will simply call submit().
 -       *
 -       * @since 1.20
 -       * @param array $data
 -       * @param HTMLForm $form
 -       * @return Status|bool
 -       */
 -      public static function uiSubmit( array $data, HTMLForm $form ) {
 -              return self::submit( $data, $form->getContext() );
 +      public function sendEmailForm() {
 +              $out = $this->getOutput();
 +
 +              $ret = $this->mTargetObj;
 +              if ( !$ret instanceof User ) {
 +                      if ( $this->mTarget != '' ) {
 +                              // Messages used here: notargettext, noemailtext, nowikiemailtext
 +                              $ret = ( $ret == 'notarget' ) ? 'emailnotarget' : ( $ret . 'text' );
 +                              return Status::newFatal( $ret );
 +                      }
 +                      return false;
 +              }
 +
 +              $htmlForm = HTMLForm::factory( 'ooui', $this->getFormFields(), $this->getContext() );
 +              // By now we are supposed to be sure that $this->mTarget is a user name
 +              $htmlForm
 +                      ->addPreText( $this->msg( 'emailpagetext', $this->mTarget )->parse() )
 +                      ->setSubmitTextMsg( 'emailsend' )
 +                      ->setSubmitCallback( [ __CLASS__, 'submit' ] )
 +                      ->setFormIdentifier( 'sendEmailForm' )
 +                      ->setWrapperLegendMsg( 'email-legend' )
 +                      ->loadData();
 +
 +              if ( !Hooks::run( 'EmailUserForm', [ &$htmlForm ] ) ) {
 +                      return false;
 +              }
 +
 +              $result = $htmlForm->show();
 +
 +              if ( $result === true || ( $result instanceof Status && $result->isGood() ) ) {
 +                      $out->setPageTitle( $this->msg( 'emailsent' ) );
 +                      $out->addWikiMsg( 'emailsenttext', $this->mTarget );
 +                      $out->returnToMain( false, $ret->getUserPage() );
 +              }
 +              return true;
        }
  
        /**
        protected function getGroupName() {
                return 'users';
        }
+       /**
+        * Builds an error message based on the block params
+        *
+        * @return ErrorPageError
+        */
+       private function getBlockedEmailError() {
+               $block = $this->getUser()->mBlock;
+               $params = $block->getBlockErrorParams( $this->getContext() );
+               $msg = $block->isSitewide() ? 'blockedtext' : 'blocked-email-user';
+               return new ErrorPageError( 'blockedtitle', $msg, $params );
+       }
  }
diff --combined languages/i18n/en.json
        "subject-preview": "Preview of subject:",
        "previewerrortext": "An error occurred while attempting to preview your changes.",
        "blockedtitle": "User is blocked",
+       "blocked-email-user": "<strong>Your username has been blocked from sending email. You can still edit other pages on this wiki.</strong> You can view the full block details at [[Special:MyContributions|account contributions]].\n\nThe block was made by $1.\n\nThe reason given is <em>$2</em>.\n\n* Start of block: $8\n* Expiration of block: $6\n* Intended blockee: $7\n* Block ID #$5",
+       "blockedtext-partial": "<strong>Your username or IP address has been blocked from making changes to this page. You can still edit other pages on this wiki.</strong> You can view the full block details at [[Special:MyContributions|account contributions]].\n\nThe block was made by $1.\n\nThe reason given is <em>$2</em>.\n\n* Start of block: $8\n* Expiration of block: $6\n* Intended blockee: $7\n* Block ID #$5",
        "blockedtext": "<strong>Your username or IP address has been blocked.</strong>\n\nThe block was made by $1.\nThe reason given is <em>$2</em>.\n\n* Start of block: $8\n* Expiration of block: $6\n* Intended blockee: $7\n\nYou can contact $1 or another [[{{MediaWiki:Grouppage-sysop}}|administrator]] to discuss the block.\nYou cannot use the \"{{int:emailuser}}\" feature unless a valid email address is specified in your [[Special:Preferences|account preferences]] and you have not been blocked from using it.\nYour current IP address is $3, and the block ID is #$5.\nPlease include all above details in any queries you make.",
        "autoblockedtext": "Your IP address has been automatically blocked because it was used by another user, who was blocked by $1.\nThe reason given is:\n\n:<em>$2</em>\n\n* Start of block: $8\n* Expiration of block: $6\n* Intended blockee: $7\n\nYou may contact $1 or one of the other [[{{MediaWiki:Grouppage-sysop}}|administrators]] to discuss the block.\n\nNote that you may not use the \"{{int:emailuser}}\" feature unless you have a valid email address registered in your [[Special:Preferences|user preferences]] and you have not been blocked from using it.\n\nYour current IP address is $3, and the block ID is #$5.\nPlease include all above details in any queries you make.",
        "systemblockedtext": "Your username or IP address has been automatically blocked by MediaWiki.\nThe reason given is:\n\n:<em>$2</em>\n\n* Start of block: $8\n* Expiration of block: $6\n* Intended blockee: $7\n\nYour current IP address is $3.\nPlease include all above details in any queries you make.",
        "ipb-disableusertalk": "Prevent this user from editing their own talk page while blocked",
        "ipb-change-block": "Re-block the user with these settings",
        "ipb-confirm": "Confirm block",
 +      "ipb-sitewide": "Sitewide",
 +      "ipb-partial": "Partial",
 +      "ipb-type-label": "Type",
 +      "ipb-pages-label": "Pages",
        "badipaddress": "Invalid IP address",
        "blockipsuccesssub": "Block succeeded",
        "blockipsuccesstext": "[[Special:Contributions/$1|$1]] has been blocked.<br />\nSee the [[Special:BlockList|block list]] to review blocks.",
        "logentry-block-block": "$1 {{GENDER:$2|blocked}} {{GENDER:$4|$3}} with an expiration time of $5 $6",
        "logentry-block-unblock": "$1 {{GENDER:$2|unblocked}} {{GENDER:$4|$3}}",
        "logentry-block-reblock": "$1 {{GENDER:$2|changed}} block settings for {{GENDER:$4|$3}} with an expiration time of $5 $6",
 +      "logentry-partialblock-block": "$1 {{GENDER:$2|blocked}} {{GENDER:$4|$3}} from editing {{PLURAL:$8||the pages}} $7 with an expiration time of $5 $6",
 +      "logentry-partialblock-reblock": "$1 {{GENDER:$2|changed}} block settings for {{GENDER:$4|$3}} preventing edits on {{PLURAL:$8||the pages}} $7 with an expiration time of $5 $6",
 +      "logentry-non-editing-block-block": "$1 {{GENDER:$2|blocked}} {{GENDER:$4|$3}} from non-editing actions with an expiration time of $5 $6",
 +      "logentry-non-editing-block-reblock": "$1 {{GENDER:$2|changed}} block settings for {{GENDER:$4|$3}} for non-editing actions with an expiration time of $5 $6",
        "logentry-suppress-block": "$1 {{GENDER:$2|blocked}} {{GENDER:$4|$3}} with an expiration time of $5 $6",
        "logentry-suppress-reblock": "$1 {{GENDER:$2|changed}} block settings for {{GENDER:$4|$3}} with an expiration time of $5 $6",
        "logentry-import-upload": "$1 {{GENDER:$2|imported}} $3 by file upload",
        "mw-widgets-titleinput-description-redirect": "redirect to $1",
        "mw-widgets-categoryselector-add-category-placeholder": "Add a category...",
        "mw-widgets-usersmultiselect-placeholder": "Add more...",
 +      "mw-widgets-titlesmultiselect-placeholder": "Add more...",
        "date-range-from": "From date:",
        "date-range-to": "To date:",
        "sessionmanager-tie": "Cannot combine multiple request authentication types: $1.",
        "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}}",
 -      "easydeflate-invaliddeflate": "Content provided is not properly deflated"
 +      "easydeflate-invaliddeflate": "Content provided is not properly deflated",
 +      "unprotected-js": "For security reasons JavaScript cannot be loaded from unprotected pages. Please only create javascript in the MediaWiki: namespace or as a User subpage"
  }
diff --combined languages/i18n/qqq.json
        "subject-preview": "Used as label for preview of the section title when adding a new section on a talk page.\n\nShould match {{msg-mw|subject}}.\n\nSee also:\n* {{msg-mw|Summary-preview}}\n\n{{Identical|Subject}}",
        "previewerrortext": "When a user has the editing preference LivePreview enabled, clicked the Preview or Show Changes button in the edit page and the action did not succeed.",
        "blockedtitle": "Used as title displayed for blocked users. The corresponding message body is one of the following messages:\n* {{msg-mw|Blockedtext|notext=1}}\n* {{msg-mw|Autoblockedtext|notext=1}}\n* {{msg-mw|Systemblockedtext}}",
+       "blocked-email-user": "Text displayed to partially blocked users that are blocked from sending email.\n\n\"email this user\" should be consistent with {{msg-mw|Emailuser}}.\n\nParameters:\n* $1 - the blocking sysop (with a link to his/her userpage)\n* $2 - the reason for the block\n* $3 - the current IP address of the blocked user\n* $4 - (Unused) the blocking sysop's username (plain text, without the link)\n* $5 - the unique numeric identifier of the applied autoblock\n* $6 - the expiry of the block\n* $7 - the intended target of the block (what the blocking user specified in the blocking form)\n* $8 - the timestamp when the block started\nSee also:\n* {{msg-mw|Grouppage-sysop}}\n* {{msg-mw|Autoblockedtext}}\n* {{msg-mw|Systemblockedtext}}",
+       "blockedtext-partial": "Text displayed to partially blocked users.\n\n\"email this user\" should be consistent with {{msg-mw|Emailuser}}.\n\nParameters:\n* $1 - the blocking sysop (with a link to his/her userpage)\n* $2 - the reason for the block\n* $3 - the current IP address of the blocked user\n* $4 - (Unused) the blocking sysop's username (plain text, without the link)\n* $5 - the unique numeric identifier of the applied autoblock\n* $6 - the expiry of the block\n* $7 - the intended target of the block (what the blocking user specified in the blocking form)\n* $8 - the timestamp when the block started\nSee also:\n* {{msg-mw|Grouppage-sysop}}\n* {{msg-mw|Autoblockedtext}}\n* {{msg-mw|Systemblockedtext}}",
        "blockedtext": "Text displayed to blocked users.\n\n\"email this user\" should be consistent with {{msg-mw|Emailuser}}.\n\nParameters:\n* $1 - the blocking sysop (with a link to his/her userpage)\n* $2 - the reason for the block\n* $3 - the current IP address of the blocked user\n* $4 - (Unused) the blocking sysop's username (plain text, without the link)\n* $5 - the unique numeric identifier of the applied autoblock\n* $6 - the expiry of the block\n* $7 - the intended target of the block (what the blocking user specified in the blocking form)\n* $8 - the timestamp when the block started\nSee also:\n* {{msg-mw|Grouppage-sysop}}\n* {{msg-mw|Autoblockedtext}}\n* {{msg-mw|Systemblockedtext}}",
        "autoblockedtext": "Text displayed to automatically blocked users.\n\n\"email this user\" should be consistent with {{msg-mw|Emailuser}}.\n\nParameters:\n* $1 - the blocking sysop (with a link to his/her userpage)\n* $2 - the reason for the block (in case of autoblocks: {{msg-mw|autoblocker}})\n* $3 - the current IP address of the blocked user\n* $4 - (Unused) the blocking sysop's username (plain text, without the link). Use it for GENDER.\n* $5 - the unique numeric identifier of the applied autoblock\n* $6 - the expiry of the block\n* $7 - the intended target of the block (what the blocking user specified in the blocking form)\n* $8 - the timestamp when the block started\nSee also:\n* {{msg-mw|Grouppage-sysop}}\n* {{msg-mw|Blockedtext}}\n* {{msg-mw|Systemblockedtext}}",
        "systemblockedtext": "Text displayed to requests blocked by MediaWiki configuration.\n\n\"email this user\" should be consistent with {{msg-mw|Emailuser}}.\n\nParameters:\n* $1 - (Unused) A dummy user attributed as the blocker, possibly as a link to a user page.\n* $2 - the reason for the block\n* $3 - the current IP address of the blocked user\n* $4 - (Unused) the dummy blocking user's username (plain text, without the link).\n* $5 - A short string indicating the type of system block.\n* $6 - the expiry of the block\n* $7 - the intended target of the block\n* $8 - the timestamp when the block started\nSee also:\n* {{msg-mw|Grouppage-sysop}}\n* {{msg-mw|Blockedtext}}\n* {{msg-mw|Autoblockedtext}}",
        "ipb-disableusertalk": "{{doc-singularthey}}\nUsed as label for checkbox in [[Special:Block]].\n\nSee also:\n* {{msg-mw|ipbemailban}}\n* {{msg-mw|ipbenableautoblock}}\n* {{msg-mw|ipbhidename}}\n* {{msg-mw|ipbwatchuser}}\n* {{msg-mw|ipb-hardblock}}",
        "ipb-change-block": "Confirmation checkbox required for blocks that would override an earlier block. Appears together with {{msg-mw|ipb-needreblock}}.",
        "ipb-confirm": "Used as hidden field in the form on [[Special:Block]].",
 +      "ipb-sitewide": "A type of block the user can select from on [[Special:Block]].",
 +      "ipb-partial": "A type of block the user can select from on [[Special:Block]].",
 +      "ipb-type-label": "The label of the type of editing restriction the admin would like to impose on [[Special:Block]].",
 +      "ipb-pages-label": "The label for a autocomplete text field to specify pages to block a user from editing on [[Special:Block]].",
        "badipaddress": "An error message shown when one entered an invalid IP address in blocking page.",
        "blockipsuccesssub": "Used as page title in [[Special:Block]].\n\nThis message is the subject for the following message:\n* {{msg-mw|Blockipsuccesstext}}",
        "blockipsuccesstext": "Used in [[Special:Block]].\nThe title (subject) for this message is {{msg-mw|Blockipsuccesssub}}.\n\nParameters:\n* $1 - username, can be used for GENDER",
        "logentry-block-block": "{{Logentry|[[Special:Log/block]]}}\n* $4 - user name for gender or empty string for autoblocks\n* $5 - the block duration, localized and formatted with the english tooltip\n* $6 - block detail flags or empty string\n\nCf. {{msg-mw|Blocklogentry}}",
        "logentry-block-unblock": "{{Logentry|[[Special:Log/block]]}}\n* $4 - user name for gender or empty string for autoblocks\n\nCf. {{msg-mw|Unblocklogentry}}",
        "logentry-block-reblock": "{{Logentry|[[Special:Log/block]]}}\n* $4 - user name for gender or empty string for autoblocks\n* $5 - the block duration, localized and formatted with the english tooltip\n* $6 - block detail flags or empty string\n\nCf. {{msg-mw|Reblock-logentry}}",
 +      "logentry-partialblock-block": "{{Logentry|[[Special:Log/block]]}}\n* $4 - user name for gender or empty string for autoblocks\n* $5 - the block duration, localized and formatted with the english tooltip\n* $6 - block detail flags or empty string\n* $7 - list of pages separated by a comma\n* $8 - total number of pages\n\nCf. {{msg-mw|Blocklogentry}}",
 +      "logentry-partialblock-reblock": "{{Logentry|[[Special:Log/block]]}}\n* $4 - user name for gender or empty string for autoblocks\n* $5 - the block duration, localized and formatted with the english tooltip\n* $6 - block detail flags or empty string\n* $7 - list of pages separated by a comma\n* $8 - total number of pages\n\nCf. {{msg-mw|Reblock-logentry}}",
 +      "logentry-non-editing-block-block": "{{Logentry|[[Special:Log/block]]}}\n* $4 - user name for gender or empty string for autoblocks\n* $5 - the block duration, localized and formatted with the english tooltip\n* $6 - block detail flags or empty string\n\nCf. {{msg-mw|Blocklogentry}}",
 +      "logentry-non-editing-block-reblock": "{{Logentry|[[Special:Log/block]]}}\n* $4 - user name for gender or empty string for autoblocks\n* $5 - the block duration, localized and formatted with the english tooltip\n* $6 - block detail flags or empty string\n\nCf. {{msg-mw|Reblock-logentry}}",
        "logentry-suppress-block": "{{Logentry}}\n* $4 - user name for gender or empty string for autoblocks\n* $5 - the block duration, localized and formatted with the english tooltip\n* $6 - block detail flags or empty string",
        "logentry-suppress-reblock": "{{Logentry}}\n* $4 - user name for gender or empty string for autoblocks\n* $5 - the block duration, localized and formatted with the english tooltip\n* $6 - block detail flags or empty string",
        "logentry-import-upload": "{{Logentry|[[Special:Log/import]]}}",
        "mw-widgets-titleinput-description-redirect": "Description label for a redirect in the title input widget.",
        "mw-widgets-categoryselector-add-category-placeholder": "Placeholder displayed in the category selector widget after the capsules of already added categories.",
        "mw-widgets-usersmultiselect-placeholder": "Placeholder displayed in the input field, where new usernames are entered",
 +      "mw-widgets-titlesmultiselect-placeholder": "Placeholder displayed in the input field, where new titles are entered",
        "date-range-from": "Label for an input field that specifies the start date of a date range filter.",
        "date-range-to": "Label for an input field that specifies the end date of a date range filter.",
        "sessionmanager-tie": "Used as an error message when multiple session sources are tied in priority.\n\nParameters:\n* $1 - List of dession type descriptions, from messages like {{msg-mw|sessionprovider-mediawiki-session-cookiesessionprovider}}.",
        "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",
 -      "easydeflate-invaliddeflate": "Error message if the content passed to easydeflate was not deflated (compressed) properly"
 +      "easydeflate-invaliddeflate": "Error message if the content passed to easydeflate was not deflated (compressed) properly",
 +      "unprotected-js": "Error message shown when trying to load javascript via action=raw that is not protected"
  }