Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
40 / 40
100.00% covered (success)
100.00%
5 / 5
CRAP
100.00% covered (success)
100.00%
1 / 1
SettingsFormValidator
100.00% covered (success)
100.00%
40 / 40
100.00% covered (success)
100.00%
5 / 5
18
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 validateAnonBot
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 validateAnonMinor
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 validateSitesChosen
100.00% covered (success)
100.00%
26 / 26
100.00% covered (success)
100.00%
1 / 1
9
 requireShowingOneType
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3/**
4 * Implements validation for HTMLForm at Special:GlobalWatchlistSettings
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 * http://www.gnu.org/copyleft/gpl.html
20 *
21 * @file
22 */
23
24namespace MediaWiki\Extension\GlobalWatchlist;
25
26use Message;
27use MessageLocalizer;
28
29/**
30 * @author DannyS712
31 * @internal
32 */
33class SettingsFormValidator {
34
35    /**
36     * For formatting the relevant errors
37     *
38     * @var MessageLocalizer
39     */
40    private $messageLocalizer;
41
42    /**
43     * Reference to $wgGlobalWatchlistSiteLimit, limiting the number of sites a user can include
44     *
45     * @var int
46     */
47    private $maxSites;
48
49    /**
50     * Array of url forms of sites the user has attached accounts on (from CentralAuth),
51     * to check against, or null if CentralAuth is not available
52     *
53     * @var string[]|null
54     */
55    private $validSites;
56
57    /**
58     * @param MessageLocalizer $messageLocalizer
59     * @param int $maxSites
60     * @param string[]|null $validSites
61     */
62    public function __construct(
63        MessageLocalizer $messageLocalizer,
64        int $maxSites,
65        ?array $validSites
66    ) {
67        $this->messageLocalizer = $messageLocalizer;
68        $this->maxSites = $maxSites;
69        $this->validSites = $validSites;
70    }
71
72    /**
73     * Validation callback called from HTMLRadioField::validate.
74     *
75     * Ensure that user doesn't try to filter for anonymous bot edits
76     *
77     * @see HTMLRadioField::validate
78     *
79     * @param string $value for the specific field (bot edit filter)
80     * @param array $allData values for all of the form fields
81     * @return bool|string|Message True on success, or string/Message error to display, or
82     *   false to fail validation without displaying an error.
83     */
84    public function validateAnonBot( $value, $allData ) {
85        if ( (int)$value === SettingsManager::FILTER_REQUIRE &&
86            (int)$allData['anon'] === SettingsManager::FILTER_REQUIRE
87        ) {
88            return $this->messageLocalizer->msg( 'globalwatchlist-settings-error-anon-bot' );
89        }
90        return true;
91    }
92
93    /**
94     * Validation callback called from HTMLRadioField::validate.
95     *
96     * Ensure that user doesn't try to filter for anonymous minor edits
97     *
98     * @see HTMLRadioField::validate
99     *
100     * @param string $value for the specific field (minor edit filter)
101     * @param array $allData values for all of the form fields
102     * @return bool|string|Message True on success, or string/Message error to display, or
103     *   false to fail validation without displaying an error.
104     */
105    public function validateAnonMinor( $value, $allData ) {
106        if ( (int)$value === SettingsManager::FILTER_REQUIRE &&
107            (int)$allData['anon'] === SettingsManager::FILTER_REQUIRE
108        ) {
109            return $this->messageLocalizer->msg( 'globalwatchlist-settings-error-anon-minor' );
110        }
111        return true;
112    }
113
114    /**
115     * Validation callback called from HTMLFormFieldCloner::validate.
116     *
117     * Ensure that at least one site is chosen, that no site is chosen multiple times, and that
118     * the maximum number of sites is not exceeded.
119     *
120     * @see HTMLFormFieldCloner::validate
121     *
122     * @param array $value for the specific field (the cloned site fields)
123     *   should always be a multidimensional array since this is for an HTMLFormFieldCloner field
124     * @param array $allData values for all of the form fields
125     * @return bool|string|Message True on success, or string/Message error to display, or
126     *   false to fail validation without displaying an error.
127     */
128    public function validateSitesChosen( $value, $allData ) {
129        $sitesChosen = [];
130        foreach ( $value as $row ) {
131            $site = trim( $row['site'] ?? '' );
132            // Since there isn't an easy way to reorder sites other than just deleting
133            // the rows and adding them to the bottom manually, sometimes a field might
134            // be blank - thats okay, skip it
135            if ( $site === '' ) {
136                continue;
137            }
138
139            // Accept and handle sites with a protocol, see T262762
140            // normalize before checking for duplicates and invalid sites
141            $site = preg_replace( '/^(?:https?:)?\/\//', '', $site );
142
143            // Avoid duplicate sites, T273532
144            if ( isset( $sitesChosen[$site] ) ) {
145                return $this->messageLocalizer
146                    ->msg( 'globalwatchlist-settings-error-duplicate-site' )
147                    ->params( $site );
148            }
149
150            // Validate against CentralAuth, if available, T268210
151            if ( $this->validSites !== null &&
152                !in_array( $site, $this->validSites )
153            ) {
154                return $this->messageLocalizer
155                    ->msg( 'globalwatchlist-settings-error-invalid-site' )
156                    ->params( $site );
157            }
158
159            $sitesChosen[$site] = true;
160        }
161
162        $siteCount = count( $sitesChosen );
163        if ( $siteCount === 0 ) {
164            return $this->messageLocalizer->msg(
165                'globalwatchlist-settings-error-no-sites'
166            );
167        }
168
169        if ( $this->maxSites && $siteCount > $this->maxSites ) {
170            return $this->messageLocalizer
171                ->msg( 'globalwatchlist-settings-error-too-many-sites' )
172                ->numParams( $siteCount, $this->maxSites );
173        }
174
175        return true;
176    }
177
178    /**
179     * Validation callback called from HTMLMultiSelectField::validate.
180     *
181     * Ensure that at least one type of change is shown
182     *
183     * @see HTMLMultiSelectField::validate
184     *
185     * @param array $value for the specific field (the checkboxes)
186     * @param array $allData values for all of the form fields
187     * @return bool|string|Message True on success, or string/Message error to display, or
188     *   false to fail validation without displaying an error.
189     */
190    public function requireShowingOneType( $value, $allData ) {
191        if ( $value === [] ) {
192            return $this->messageLocalizer->msg( 'globalwatchlist-settings-error-no-types' );
193        }
194        return true;
195    }
196
197}