Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
60.00% covered (warning)
60.00%
51 / 85
50.00% covered (danger)
50.00%
1 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
AutoModeratorConfigValidation
60.00% covered (warning)
60.00%
51 / 85
50.00% covered (danger)
50.00%
1 / 2
154.90
0.00% covered (danger)
0.00%
0 / 1
 getConfigDescriptors
n/a
0 / 0
n/a
0 / 0
1
 validateField
56.96% covered (warning)
56.96%
45 / 79
0.00% covered (danger)
0.00%
0 / 1
132.65
 validate
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 validateVariable
n/a
0 / 0
n/a
0 / 0
2
 getDefaultContent
n/a
0 / 0
n/a
0 / 0
1
1<?php
2
3namespace AutoModerator\Config\Validation;
4
5use AutoModerator\Config\AutoModeratorWikiConfigLoader;
6use InvalidArgumentException;
7use MediaWiki\MediaWikiServices;
8use MediaWiki\Message\Message;
9use StatusValue;
10
11/**
12 * Validation class for MediaWiki:AutoModeratorConfig.json
13 */
14class AutoModeratorConfigValidation implements IConfigValidator {
15    use DatatypeValidationTrait;
16
17    /**
18     * @codeCoverageIgnore
19     */
20    private function getConfigDescriptors(): array {
21        return [
22            'AutoModeratorEnableRevisionCheck' => [
23                'type' => 'bool',
24            ],
25            'AutoModeratorFalsePositivePageTitle' => [
26                'type' => '?string',
27            ],
28            'AutoModeratorUseEditFlagMinor' => [
29                'type' => 'bool',
30            ],
31            'AutoModeratorRevertTalkPageMessageEnabled' => [
32                'type' => 'bool',
33            ],
34            'AutoModeratorRevertTalkPageMessageRegisteredUsersOnly' => [
35                'type' => 'bool',
36            ],
37            'AutoModeratorEnableBotFlag' => [
38                'type' => 'bool',
39            ],
40            'AutoModeratorSkipUserRights' => [
41                'type' => 'array'
42            ],
43            'AutoModeratorCautionLevel' => [
44                'type' => 'string',
45            ],
46            'AutoModeratorEnableUserRevertsPerPage' => [
47                'type' => 'bool',
48            ],
49            'AutoModeratorUserRevertsPerPage' => [
50                'type' => '?string',
51            ],
52            'AutoModeratorHelpPageLink' => [
53                'type' => '?string'
54            ],
55            'AutoModeratorMultilingualConfigEnableRevisionCheck' => [
56                'type' => 'bool',
57            ],
58            'AutoModeratorMultilingualConfigFalsePositivePageTitle' => [
59                'type' => '?string',
60            ],
61            'AutoModeratorMultilingualConfigUseEditFlagMinor' => [
62                'type' => 'bool',
63            ],
64            'AutoModeratorMultilingualConfigRevertTalkPageMessageEnabled' => [
65                'type' => 'bool',
66            ],
67            'AutoModeratorMultilingualConfigRevertTalkPageMessageRegisteredUsersOnly' => [
68                'type' => 'bool',
69            ],
70            'AutoModeratorMultilingualConfigEnableBotFlag' => [
71                'type' => 'bool',
72            ],
73            'AutoModeratorMultilingualConfigSkipUserRights' => [
74                'type' => 'array'
75            ],
76            'AutoModeratorMultilingualConfigCautionLevel' => [
77                'type' => 'string',
78            ],
79            'AutoModeratorMultilingualConfigEnableUserRevertsPerPage' => [
80                'type' => 'bool',
81            ],
82            'AutoModeratorMultilingualConfigUserRevertsPerPage' => [
83                'type' => '?string',
84            ],
85            'AutoModeratorMultilingualConfigHelpPageLink' => [
86                'type' => '?string'
87            ],
88            'AutoModeratorMultilingualConfigEnableLanguageAgnostic' => [
89                'type' => 'bool'
90            ],
91            'AutoModeratorMultilingualConfigEnableMultilingual' => [
92                'type' => 'bool'
93            ],
94            'AutoModeratorMultilingualConfigMultilingualThreshold' => [
95                'type' => '?string',
96            ]
97        ];
98    }
99
100    /**
101     * Validate a given field
102     *
103     * @param string $fieldName Name of the field to be validated
104     * @param array $descriptor Descriptor of the field (
105     * @param array $data
106     * @return StatusValue
107     */
108    private function validateField(
109        string $fieldName,
110        array $descriptor,
111        array $data
112    ): StatusValue {
113        // validate is supposed to make sure $data has $field as a key,
114        // so this should not throw key errors.
115        $value = $data[$fieldName];
116
117        $expectedType = $descriptor['type'];
118        if ( !$this->validateFieldDatatype( $expectedType, $value ) ) {
119            return StatusValue::newFatal(
120                'automoderator-config-validator-datatype-mismatch',
121                $fieldName,
122                $expectedType,
123                gettype( $value )
124            );
125        }
126
127        if ( isset( $descriptor['maxSize'] ) && count( $value ) > $descriptor['maxSize'] ) {
128            return StatusValue::newFatal(
129                'automoderator-config-validator-array-toobig',
130                $fieldName,
131                Message::numParam( $descriptor['maxSize'] )
132            );
133        }
134
135        $isUserRightsField = $fieldName == "AutoModeratorSkipUserRights" ||
136            $fieldName == "AutoModeratorMultilingualConfigSkipUserRights";
137        if ( $isUserRightsField ) {
138            $allPermissions = MediaWikiServices::getInstance()->getPermissionManager()->getAllPermissions();
139            foreach ( $value as $userRight ) {
140                if ( !in_array( $userRight, $allPermissions ) ) {
141                    return StatusValue::newFatal(
142                        'automoderator-config-validator-userrights-not-allowed',
143                        $userRight
144                    );
145                }
146            }
147        }
148
149        $isUserRevertsPerPageField = $fieldName == "AutoModeratorUserRevertsPerPage" ||
150            $fieldName == "AutoModeratorMultilingualConfigUserRevertsPerPage";
151        if ( $isUserRevertsPerPageField && $value && !is_numeric( $value ) ) {
152            return StatusValue::newFatal(
153                'automoderator-config-validator-user-reverts-per-page-not-number',
154                $value
155            );
156        }
157
158        if ( $fieldName == "AutoModeratorMultilingualConfigMultilingualThreshold" && $value && !is_numeric( $value ) ) {
159            return StatusValue::newFatal(
160                'automoderator-config-validator-multilingual-threshold-not-number',
161                $value
162            );
163        }
164
165        if ( $fieldName == "AutoModeratorMultilingualConfigMultilingualThreshold" &&
166            $data['AutoModeratorMultilingualConfigEnableMultilingual'] &&
167            ( $value < 0.850 || $value > 0.999 ) ) {
168            return StatusValue::newFatal(
169                'automoderator-config-validator-multilingual-threshold-value-outside-range',
170                $value
171            );
172        }
173        if ( $fieldName == "AutoModeratorMultilingualConfigMultilingualThreshold" && $value &&
174            !$data['AutoModeratorMultilingualConfigEnableMultilingual'] ) {
175            return StatusValue::newFatal(
176                'automoderator-config-validator-multilingual-threshold-multilingual-not-enabled',
177                $value
178            );
179        }
180
181        if ( $fieldName == "AutoModeratorMultilingualConfigEnableLanguageAgnostic" && $value
182            && $data['AutoModeratorMultilingualConfigEnableMultilingual'] ) {
183            return StatusValue::newFatal(
184                'automoderator-config-validator-multilingual-select-only-one-model',
185                $value
186            );
187        }
188
189        if ( $fieldName == "AutoModeratorMultilingualConfigRevertTalkPageMessageEnabled" && $value
190            && !$data['AutoModeratorMultilingualConfigFalsePositivePageTitle'] ) {
191            return StatusValue::newFatal(
192                'automoderator-config-validator-multilingual-add-false-positive-page-talk-page-msg-enabled',
193                $value
194            );
195        }
196
197        if ( $fieldName == "AutoModeratorRevertTalkPageMessageEnabled" && $value
198            && !$data['AutoModeratorFalsePositivePageTitle'] ) {
199            return StatusValue::newFatal(
200                'automoderator-config-validator-add-false-positive-page-talk-page-msg-enabled',
201                $value
202            );
203        }
204
205        $isFalsePositivePageTitle = $fieldName == 'AutoModeratorFalsePositivePageTitle' ||
206            $fieldName == 'AutoModeratorMultilingualConfigFalsePositivePageTitle';
207        if ( $isFalsePositivePageTitle && $value ) {
208            $titleFactory = MediaWikiServices::getInstance()->getTitleFactory();
209            $title = $titleFactory->newFromText( $value );
210            if ( !$title?->exists() ) {
211                return StatusValue::newFatal(
212                    'automoderator-config-validator-false-positive-page-not-exist',
213                    $value
214                );
215            }
216        }
217
218        return StatusValue::newGood();
219    }
220
221    /**
222     * @inheritDoc
223     */
224    public function validate( array $data ): StatusValue {
225        $status = StatusValue::newGood();
226        foreach ( $this->getConfigDescriptors() as $field => $descriptor ) {
227            if ( !array_key_exists( $field, $data ) ) {
228                // No need to validate something we're not setting
229                continue;
230            }
231
232            $status->merge( $this->validateField( $field, $descriptor, $data ) );
233        }
234
235        return $status;
236    }
237
238    /**
239     * @inheritDoc
240     * @codeCoverageIgnore
241     */
242    public function validateVariable( string $variable, $value ): void {
243        if ( !in_array( $variable, AutoModeratorWikiConfigLoader::ALLOW_LIST ) ) {
244            throw new InvalidArgumentException(
245                'Invalid attempt to set a variable via WikiPageConfigWriter'
246            );
247        }
248    }
249
250    /**
251     * @inheritDoc
252     * @codeCoverageIgnore
253     */
254    public function getDefaultContent(): array {
255        return [];
256    }
257}