Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
98.75% covered (success)
98.75%
79 / 80
91.67% covered (success)
91.67%
11 / 12
CRAP
0.00% covered (danger)
0.00%
0 / 1
SpecialGlobalBlockStatus
98.75% covered (success)
98.75%
79 / 80
91.67% covered (success)
91.67%
11 / 12
21
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 execute
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 setParameter
100.00% covered (success)
100.00%
21 / 21
100.00% covered (success)
100.00%
1 / 1
6
 alterForm
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 getFormFields
100.00% covered (success)
100.00%
20 / 20
100.00% covered (success)
100.00%
1 / 1
2
 onSubmit
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
3
 showSuccess
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 doesWrites
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getGroupName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getDisplayFormat
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getDescription
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 checkExecutePermissions
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3namespace MediaWiki\Extension\GlobalBlocking\Special;
4
5use ErrorPageError;
6use Exception;
7use HTMLForm;
8use MediaWiki\Block\BlockUtils;
9use MediaWiki\Extension\GlobalBlocking\Services\GlobalBlockingLinkBuilder;
10use MediaWiki\Extension\GlobalBlocking\Services\GlobalBlockLocalStatusLookup;
11use MediaWiki\Extension\GlobalBlocking\Services\GlobalBlockLocalStatusManager;
12use MediaWiki\Extension\GlobalBlocking\Services\GlobalBlockLookup;
13use MediaWiki\SpecialPage\FormSpecialPage;
14use MediaWiki\SpecialPage\SpecialPage;
15use MediaWiki\User\User;
16use MediaWiki\User\UserIdentity;
17use MediaWiki\User\UserNameUtils;
18use Wikimedia\IPUtils;
19
20class SpecialGlobalBlockStatus extends FormSpecialPage {
21    private ?string $mTarget;
22    private ?bool $mCurrentStatus;
23    private ?bool $mWhitelistStatus;
24
25    private BlockUtils $blockUtils;
26    private UserNameUtils $userNameUtils;
27    private GlobalBlockLookup $globalBlockLookup;
28    private GlobalBlockLocalStatusManager $globalBlockLocalStatusManager;
29    private GlobalBlockLocalStatusLookup $globalBlockLocalStatusLookup;
30    private GlobalBlockingLinkBuilder $globalBlockingLinkBuilder;
31
32    /**
33     * @param BlockUtils $blockUtils
34     * @param UserNameUtils $userNameUtils
35     * @param GlobalBlockLookup $globalBlockLookup
36     * @param GlobalBlockLocalStatusManager $globalBlockLocalStatusManager
37     * @param GlobalBlockLocalStatusLookup $globalBlockLocalStatusLookup
38     * @param GlobalBlockingLinkBuilder $globalBlockingLinkBuilder
39     */
40    public function __construct(
41        BlockUtils $blockUtils,
42        UserNameUtils $userNameUtils,
43        GlobalBlockLookup $globalBlockLookup,
44        GlobalBlockLocalStatusManager $globalBlockLocalStatusManager,
45        GlobalBlockLocalStatusLookup $globalBlockLocalStatusLookup,
46        GlobalBlockingLinkBuilder $globalBlockingLinkBuilder
47    ) {
48        parent::__construct( 'GlobalBlockStatus', 'globalblock-whitelist' );
49        $this->blockUtils = $blockUtils;
50        $this->userNameUtils = $userNameUtils;
51        $this->globalBlockLookup = $globalBlockLookup;
52        $this->globalBlockLocalStatusManager = $globalBlockLocalStatusManager;
53        $this->globalBlockLocalStatusLookup = $globalBlockLocalStatusLookup;
54        $this->globalBlockingLinkBuilder = $globalBlockingLinkBuilder;
55    }
56
57    /** @inheritDoc */
58    public function execute( $par ) {
59        $this->addHelpLink( 'Extension:GlobalBlocking' );
60        $out = $this->getOutput();
61        $out->disableClientCache();
62        $out->setSubtitle( $this->globalBlockingLinkBuilder->buildSubtitleLinks( $this ) );
63
64        parent::execute( $par );
65    }
66
67    /**
68     * @param string|null $par Parameter from the URL, may be null or a string (probably an IP)
69     * that was inserted
70     * @return void
71     * @throws Exception
72     */
73    protected function setParameter( $par ) {
74        $request = $this->getRequest();
75
76        // Parse the target from the request or URL.
77        $target = trim( $request->getText( 'address', $par ?? '' ) );
78        if ( !$target ) {
79            // If no target was provided, show the form with an empty target and the disable checkbox checked (as this
80            // is the most common action).
81            $this->mTarget = $target;
82            $this->mCurrentStatus = true;
83            $this->mWhitelistStatus = false;
84            return;
85        }
86
87        // If the target is an IP address, sanitize it before assigning it as the target.
88        if ( IPUtils::isValidRange( $target ) ) {
89            $this->mTarget = IPUtils::sanitizeRange( $target );
90        } elseif ( IPUtils::isIPAddress( $target ) ) {
91            $this->mTarget = IPUtils::sanitizeIP( $target );
92        } else {
93            $normalisedTarget = $this->userNameUtils->getCanonical( $target );
94            if ( $normalisedTarget ) {
95                $this->mTarget = $normalisedTarget;
96            } else {
97                // Allow invalid targets to be set, so that the user can be shown an error message.
98                $this->mTarget = $target;
99            }
100        }
101
102        // Set the relevant user for the skin and assign it before the form is rendered to HTML.
103        [ $targetForSkin ] = $this->blockUtils->parseBlockTarget( $target );
104        if ( $targetForSkin instanceof UserIdentity ) {
105            $this->getSkin()->setRelevantUser( $targetForSkin );
106        }
107
108        $this->mCurrentStatus = (bool)$this->globalBlockLocalStatusLookup
109            ->getLocalWhitelistInfo( $this->globalBlockLookup->getGlobalBlockId( $this->mTarget ) );
110        $this->mWhitelistStatus = $request->getCheck( 'wpWhitelistStatus' );
111    }
112
113    protected function alterForm( HTMLForm $form ) {
114        $form->setPreHtml( $this->msg( 'globalblocking-whitelist-intro' )->parse() );
115        $form->setWrapperLegendMsg( 'globalblocking-whitelist-legend' );
116        $form->setSubmitTextMsg( 'globalblocking-whitelist-submit' );
117    }
118
119    protected function getFormFields() {
120        $accountBlocksEnabled = $this->getConfig()->get( 'GlobalBlockingAllowGlobalAccountBlocks' );
121        return [
122            'address' => [
123                'name' => 'address',
124                'type' => 'text',
125                'id' => 'mw-globalblocking-ipaddress mw-globalblocking-target',
126                'label-message' => $accountBlocksEnabled ? 'globalblocking-target' : 'globalblocking-ipaddress',
127                'default' => $this->mTarget,
128                'required' => true,
129            ],
130            'Reason' => [
131                'type' => 'text',
132                'label-message' => 'globalblocking-whitelist-reason'
133            ],
134            'WhitelistStatus' => [
135                'type' => 'check',
136                'label-message' => 'globalblocking-whitelist-statuslabel',
137                'default' => $this->mCurrentStatus
138            ]
139        ];
140    }
141
142    public function onSubmit( array $data ) {
143        if ( $this->mWhitelistStatus ) {
144            // Locally disable the block
145            $status = $this->globalBlockLocalStatusManager
146                ->locallyDisableBlock( $this->mTarget, $data['Reason'], $this->getUser() );
147            $successMsg = 'globalblocking-whitelist-whitelisted';
148        } else {
149            // Locally re-enable the block
150            $status = $this->globalBlockLocalStatusManager
151                ->locallyEnableBlock( $this->mTarget, $data['Reason'], $this->getUser() );
152            $successMsg = 'globalblocking-whitelist-dewhitelisted';
153        }
154
155        if ( !$status->isGood() ) {
156            return $status;
157        }
158
159        return $this->showSuccess( $this->mTarget, $status->getValue()['id'], $successMsg );
160    }
161
162    /**
163     * Show a message indicating that the change in the local status of the global block was successful.
164     *
165     * @param string $target The target of the global block that had its local status modified
166     * @param int $id The ID of the global block that had its local status modified (same as the ID in gbw_id).
167     * @param string $successMsg The message key used as the success message.
168     * @return true
169     */
170    protected function showSuccess( string $target, int $id, string $successMsg ): bool {
171        $out = $this->getOutput();
172        $out->addWikiMsg( $successMsg, $target, $id );
173        $out->addHTML( $this->getLinkRenderer()->makeKnownLink(
174            SpecialPage::getTitleFor( 'GlobalBlockList' ),
175            $this->msg( 'globalblocking-return' )->text()
176        ) );
177        return true;
178    }
179
180    public function doesWrites() {
181        return true;
182    }
183
184    protected function getGroupName() {
185        return 'users';
186    }
187
188    protected function getDisplayFormat() {
189        return 'ooui';
190    }
191
192    public function getDescription() {
193        return $this->msg( 'globalblocking-whitelist' );
194    }
195
196    protected function checkExecutePermissions( User $user ) {
197        parent::checkExecutePermissions( $user );
198        // If wgApplyGlobalBlocks is false, the user should not be able to access this special page.
199        if ( !$this->getConfig()->get( 'ApplyGlobalBlocks' ) ) {
200            throw new ErrorPageError( $this->getDescription(), 'globalblocking-whitelist-notapplied' );
201        }
202    }
203}