Update the design of Special:Block
[lhc/web/wiklou.git] / includes / specials / SpecialBlock.php
index ea4cd22..47d61b8 100644 (file)
@@ -66,7 +66,6 @@ class SpecialBlock extends FormSpecialPage {
         */
        protected function checkExecutePermissions( User $user ) {
                parent::checkExecutePermissions( $user );
-
                # T17810: blocked admins should have limited access here
                $status = self::checkUnblockSelf( $this->target, $user );
                if ( $status !== true ) {
@@ -74,6 +73,15 @@ class SpecialBlock extends FormSpecialPage {
                }
        }
 
+       /**
+        * We allow certain special cases where user is blocked
+        *
+        * @return bool
+        */
+       public function requiresUnblock() {
+               return false;
+       }
+
        /**
         * Handle some magic here
         *
@@ -148,22 +156,31 @@ class SpecialBlock extends FormSpecialPage {
                        'type' => 'user',
                        'ipallowed' => true,
                        'iprange' => true,
-                       'label-message' => 'ipaddressorusername',
                        'id' => 'mw-bi-target',
                        'size' => '45',
                        'autofocus' => true,
                        'required' => true,
                        'validation-callback' => [ __CLASS__, 'validateTargetField' ],
+                       'section' => 'target',
+               ];
+
+               $a['Editing'] = [
+                       'type' => 'check',
+                       'label-message' => 'block-prevent-edit',
+                       'default' => true,
+                       'section' => 'actions',
+                       'disabled' => $enablePartialBlocks ? false : true,
                ];
 
                if ( $enablePartialBlocks ) {
                        $a['EditingRestriction'] = [
                                'type' => 'radio',
-                               'label' => $this->msg( 'ipb-type-label' )->text(),
+                               'cssclass' => 'mw-block-editing-restriction',
                                'options' => [
                                        $this->msg( 'ipb-sitewide' )->text() => 'sitewide',
                                        $this->msg( 'ipb-partial' )->text() => 'partial',
                                ],
+                               'section' => 'actions',
                        ];
                        $a['PageRestrictions'] = [
                                'type' => 'titlesmultiselect',
@@ -175,38 +192,22 @@ class SpecialBlock extends FormSpecialPage {
                                'input' => [
                                        'autocomplete' => false
                                ],
+                               'section' => 'actions',
                        ];
                }
 
-               $a['Expiry'] = [
-                       'type' => 'expiry',
-                       'label-message' => 'ipbexpiry',
-                       'required' => true,
-                       'options' => $suggestedDurations,
-                       'default' => $this->msg( 'ipb-default-expiry' )->inContentLanguage()->text(),
-               ];
-
-               $a['Reason'] = [
-                       'type' => 'selectandother',
-                       // HTML maxlength uses "UTF-16 code units", which means that characters outside BMP
-                       // (e.g. emojis) count for two each. This limit is overridden in JS to instead count
-                       // Unicode codepoints (or 255 UTF-8 bytes for old schema).
-                       'maxlength' => $oldCommentSchema ? 255 : CommentStore::COMMENT_CHARACTER_LIMIT,
-                       'maxlength-unit' => 'codepoints',
-                       'label-message' => 'ipbreason',
-                       'options-message' => 'ipbreason-dropdown',
-               ];
-
                $a['CreateAccount'] = [
                        'type' => 'check',
                        'label-message' => 'ipbcreateaccount',
                        'default' => true,
+                       'section' => 'actions',
                ];
 
                if ( self::canBlockEmail( $user ) ) {
                        $a['DisableEmail'] = [
                                'type' => 'check',
                                'label-message' => 'ipbemailban',
+                               'section' => 'actions',
                        ];
                }
 
@@ -215,13 +216,34 @@ class SpecialBlock extends FormSpecialPage {
                                'type' => 'check',
                                'label-message' => 'ipb-disableusertalk',
                                'default' => false,
+                               'section' => 'actions',
                        ];
                }
 
+               $a['Expiry'] = [
+                       'type' => 'expiry',
+                       'required' => true,
+                       'options' => $suggestedDurations,
+                       'default' => $this->msg( 'ipb-default-expiry' )->inContentLanguage()->text(),
+                       'section' => 'expiry',
+               ];
+
+               $a['Reason'] = [
+                       'type' => 'selectandother',
+                       // HTML maxlength uses "UTF-16 code units", which means that characters outside BMP
+                       // (e.g. emojis) count for two each. This limit is overridden in JS to instead count
+                       // Unicode codepoints (or 255 UTF-8 bytes for old schema).
+                       'maxlength' => $oldCommentSchema ? 255 : CommentStore::COMMENT_CHARACTER_LIMIT,
+                       'maxlength-unit' => 'codepoints',
+                       'options-message' => 'ipbreason-dropdown',
+                       'section' => 'reason',
+               ];
+
                $a['AutoBlock'] = [
                        'type' => 'check',
                        'label-message' => 'ipbenableautoblock',
                        'default' => true,
+                       'section' => 'options',
                ];
 
                # Allow some users to hide name from block log, blocklist and listusers
@@ -230,6 +252,7 @@ class SpecialBlock extends FormSpecialPage {
                                'type' => 'check',
                                'label-message' => 'ipbhidename',
                                'cssclass' => 'mw-block-hideuser',
+                               'section' => 'options',
                        ];
                }
 
@@ -238,6 +261,7 @@ class SpecialBlock extends FormSpecialPage {
                        $a['Watch'] = [
                                'type' => 'check',
                                'label-message' => 'ipbwatchuser',
+                               'section' => 'options',
                        ];
                }
 
@@ -245,6 +269,7 @@ class SpecialBlock extends FormSpecialPage {
                        'type' => 'check',
                        'label-message' => 'ipb-hardblock',
                        'default' => false,
+                       'section' => 'options',
                ];
 
                # This is basically a copy of the Target field, but the user can't change it, so we
@@ -386,6 +411,10 @@ class SpecialBlock extends FormSpecialPage {
         * @return string
         */
        protected function preText() {
+               $this->getOutput()->addModuleStyles( [
+                       'mediawiki.widgets.TagMultiselectWidget.styles',
+                       'mediawiki.special',
+               ] );
                $this->getOutput()->addModules( [ 'mediawiki.special.block' ] );
 
                $blockCIDRLimit = $this->getConfig()->get( 'BlockCIDRLimit' );
@@ -873,6 +902,7 @@ class SpecialBlock extends FormSpecialPage {
                                }
 
                                $status = $currentBlock->update();
+                               // TODO handle failure
 
                                $logaction = 'reblock';
 
@@ -885,6 +915,8 @@ class SpecialBlock extends FormSpecialPage {
                                if ( (bool)$currentBlock->mHideName ) {
                                        $data['HideUser'] = true;
                                }
+
+                               $block = $currentBlock;
                        }
                } else {
                        $logaction = 'block';
@@ -929,9 +961,8 @@ class SpecialBlock extends FormSpecialPage {
                $logEntry->setComment( $data['Reason'][0] );
                $logEntry->setPerformer( $performer );
                $logEntry->setParameters( $logParams );
-               # Relate log ID to block IDs (T27763)
-               $blockIds = array_merge( [ $status['id'] ], $status['autoIds'] );
-               $logEntry->setRelations( [ 'ipb_id' => $blockIds ] );
+               # Relate log ID to block ID (T27763)
+               $logEntry->setRelations( [ 'ipb_id' => $block->getId() ] );
                $logId = $logEntry->insert();
 
                if ( !empty( $data['Tags'] ) ) {
@@ -1021,19 +1052,24 @@ class SpecialBlock extends FormSpecialPage {
         * T17810: blocked admins should not be able to block/unblock
         * others, and probably shouldn't be able to unblock themselves
         * either.
-        * @param User|int|string $user
+        *
+        * Exception: Users can block the user who blocked them, to reduce
+        * advantage of a malicious account blocking all admins (T150826)
+        *
+        * @param User|int|string|null $target Target to block or unblock; could be a User object,
+        *   or a user ID or username, or null when the target is not known yet (e.g. when
+        *   displaying Special:Block)
         * @param User $performer User doing the request
         * @return bool|string True or error message key
         */
-       public static function checkUnblockSelf( $user, User $performer ) {
-               if ( is_int( $user ) ) {
-                       $user = User::newFromId( $user );
-               } elseif ( is_string( $user ) ) {
-                       $user = User::newFromName( $user );
+       public static function checkUnblockSelf( $target, User $performer ) {
+               if ( is_int( $target ) ) {
+                       $target = User::newFromId( $target );
+               } elseif ( is_string( $target ) ) {
+                       $target = User::newFromName( $target );
                }
-
                if ( $performer->isBlocked() ) {
-                       if ( $user instanceof User && $user->getId() == $performer->getId() ) {
+                       if ( $target instanceof User && $target->getId() == $performer->getId() ) {
                                # User is trying to unblock themselves
                                if ( $performer->isAllowed( 'unblockself' ) ) {
                                        return true;
@@ -1043,6 +1079,19 @@ class SpecialBlock extends FormSpecialPage {
                                } else {
                                        return 'ipbnounblockself';
                                }
+                       } elseif (
+                               $target instanceof User &&
+                               $performer->getBlock() instanceof Block &&
+                               $performer->getBlock()->getBy() &&
+                               $performer->getBlock()->getBy() === $target->getId()
+                       ) {
+                               // Allow users to block the user that blocked them.
+                               // This is to prevent a situation where a malicious user
+                               // blocks all other users. This way, the non-malicious
+                               // user can block the malicious user back, resulting
+                               // in a stalemate.
+                               return true;
+
                        } else {
                                # User is trying to block/unblock someone else
                                return 'ipbblocked';