Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
95.24% |
80 / 84 |
|
86.67% |
13 / 15 |
CRAP | |
0.00% |
0 / 1 |
SpecialMute | |
96.39% |
80 / 83 |
|
86.67% |
13 / 15 |
31 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
1 | |||
execute | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
1 | |||
requiresUnblock | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getDisplayFormat | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
onSuccess | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
onSubmit | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
3 | |||
getDescription | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getTarget | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
unmuteTarget | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
2 | |||
muteTarget | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
2 | |||
getForm | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
2 | |||
getFormFields | |
91.30% |
21 / 23 |
|
0.00% |
0 / 1 |
8.04 | |||
loadTarget | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
4 | |||
isTargetMuted | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
getMuteList | |
75.00% |
3 / 4 |
|
0.00% |
0 / 1 |
2.06 |
1 | <?php |
2 | /* |
3 | * This program is free software; you can redistribute it and/or modify |
4 | * it under the terms of the GNU General Public License as published by |
5 | * the Free Software Foundation; either version 2 of the License, or |
6 | * (at your option) any later version. |
7 | * |
8 | * This program is distributed in the hope that it will be useful, |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11 | * GNU General Public License for more details. |
12 | * |
13 | * You should have received a copy of the GNU General Public License along |
14 | * with this program; if not, write to the Free Software Foundation, Inc., |
15 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
16 | * http://www.gnu.org/copyleft/gpl.html |
17 | * |
18 | * @file |
19 | */ |
20 | |
21 | namespace MediaWiki\Specials; |
22 | |
23 | use ErrorPageError; |
24 | use MediaWiki\HTMLForm\HTMLForm; |
25 | use MediaWiki\MainConfigNames; |
26 | use MediaWiki\Preferences\MultiUsernameFilter; |
27 | use MediaWiki\SpecialPage\FormSpecialPage; |
28 | use MediaWiki\User\CentralId\CentralIdLookup; |
29 | use MediaWiki\User\Options\UserOptionsManager; |
30 | use MediaWiki\User\User; |
31 | use MediaWiki\User\UserIdentity; |
32 | use MediaWiki\User\UserIdentityLookup; |
33 | use MediaWiki\User\UserIdentityUtils; |
34 | |
35 | /** |
36 | * Modify your own notification preferences |
37 | * |
38 | * @ingroup SpecialPage |
39 | */ |
40 | class SpecialMute extends FormSpecialPage { |
41 | |
42 | private const PAGE_NAME = 'Mute'; |
43 | |
44 | /** @var UserIdentity|null */ |
45 | private $target; |
46 | |
47 | /** @var int */ |
48 | private $targetCentralId; |
49 | |
50 | private CentralIdLookup $centralIdLookup; |
51 | private UserOptionsManager $userOptionsManager; |
52 | private UserIdentityLookup $userIdentityLookup; |
53 | private UserIdentityUtils $userIdentityUtils; |
54 | |
55 | /** |
56 | * @param CentralIdLookup $centralIdLookup |
57 | * @param UserOptionsManager $userOptionsManager |
58 | * @param UserIdentityLookup $userIdentityLookup |
59 | * @param UserIdentityUtils $userIdentityUtils |
60 | */ |
61 | public function __construct( |
62 | CentralIdLookup $centralIdLookup, |
63 | UserOptionsManager $userOptionsManager, |
64 | UserIdentityLookup $userIdentityLookup, |
65 | UserIdentityUtils $userIdentityUtils |
66 | ) { |
67 | parent::__construct( self::PAGE_NAME, '', false ); |
68 | $this->centralIdLookup = $centralIdLookup; |
69 | $this->userOptionsManager = $userOptionsManager; |
70 | $this->userIdentityLookup = $userIdentityLookup; |
71 | $this->userIdentityUtils = $userIdentityUtils; |
72 | } |
73 | |
74 | /** |
75 | * Entry point for special pages |
76 | * |
77 | * @param string|null $par |
78 | */ |
79 | public function execute( $par ) { |
80 | $this->addHelpLink( |
81 | 'https://meta.wikimedia.org/wiki/Community_health_initiative/User_Mute_features', |
82 | true |
83 | ); |
84 | $this->requireNamedUser( 'specialmute-login-required' ); |
85 | $this->loadTarget( $par ); |
86 | |
87 | parent::execute( $par ); |
88 | |
89 | $out = $this->getOutput(); |
90 | $out->addModules( 'mediawiki.misc-authed-ooui' ); |
91 | } |
92 | |
93 | /** |
94 | * @inheritDoc |
95 | */ |
96 | public function requiresUnblock() { |
97 | return false; |
98 | } |
99 | |
100 | /** |
101 | * @inheritDoc |
102 | */ |
103 | protected function getDisplayFormat() { |
104 | return 'ooui'; |
105 | } |
106 | |
107 | /** |
108 | * @inheritDoc |
109 | */ |
110 | public function onSuccess() { |
111 | $out = $this->getOutput(); |
112 | $out->addWikiMsg( 'specialmute-success' ); |
113 | } |
114 | |
115 | /** |
116 | * @param array $data |
117 | * @param HTMLForm|null $form |
118 | * @return bool |
119 | */ |
120 | public function onSubmit( array $data, ?HTMLForm $form = null ) { |
121 | foreach ( $data as $userOption => $value ) { |
122 | if ( $value ) { |
123 | $this->muteTarget( $userOption ); |
124 | } else { |
125 | $this->unmuteTarget( $userOption ); |
126 | } |
127 | } |
128 | |
129 | return true; |
130 | } |
131 | |
132 | /** |
133 | * @inheritDoc |
134 | */ |
135 | public function getDescription() { |
136 | return $this->msg( 'specialmute' ); |
137 | } |
138 | |
139 | /** |
140 | * @return UserIdentity|null |
141 | */ |
142 | private function getTarget(): ?UserIdentity { |
143 | return $this->target; |
144 | } |
145 | |
146 | /** |
147 | * Un-mute target |
148 | * |
149 | * @param string $userOption up_property key that holds the list of muted users |
150 | */ |
151 | private function unmuteTarget( $userOption ) { |
152 | $muteList = $this->getMuteList( $userOption ); |
153 | |
154 | $key = array_search( $this->targetCentralId, $muteList ); |
155 | if ( $key !== false ) { |
156 | unset( $muteList[$key] ); |
157 | $muteList = implode( "\n", $muteList ); |
158 | |
159 | $user = $this->getUser(); |
160 | $this->userOptionsManager->setOption( $user, $userOption, $muteList ); |
161 | $user->saveSettings(); |
162 | } |
163 | } |
164 | |
165 | /** |
166 | * Mute target |
167 | * @param string $userOption up_property key that holds the blacklist |
168 | */ |
169 | private function muteTarget( $userOption ) { |
170 | // avoid duplicates just in case |
171 | if ( !$this->isTargetMuted( $userOption ) ) { |
172 | $muteList = $this->getMuteList( $userOption ); |
173 | |
174 | $muteList[] = $this->targetCentralId; |
175 | $muteList = implode( "\n", $muteList ); |
176 | |
177 | $user = $this->getUser(); |
178 | $this->userOptionsManager->setOption( $user, $userOption, $muteList ); |
179 | $user->saveSettings(); |
180 | } |
181 | } |
182 | |
183 | /** |
184 | * @inheritDoc |
185 | */ |
186 | protected function getForm() { |
187 | $target = $this->getTarget(); |
188 | $form = parent::getForm(); |
189 | $form->setId( 'mw-specialmute-form' ); |
190 | $form->setHeaderHtml( $this->msg( 'specialmute-header', $target ? $target->getName() : '' )->parse() ); |
191 | $form->setSubmitTextMsg( 'specialmute-submit' ); |
192 | $form->setSubmitID( 'save' ); |
193 | |
194 | return $form; |
195 | } |
196 | |
197 | /** |
198 | * @inheritDoc |
199 | */ |
200 | protected function getFormFields() { |
201 | $config = $this->getConfig(); |
202 | $fields = []; |
203 | |
204 | if ( !$config->get( MainConfigNames::EnableUserEmail ) ) { |
205 | throw new ErrorPageError( 'specialmute', 'specialmute-error-email-disabled' ); |
206 | } |
207 | |
208 | if ( !$config->get( MainConfigNames::EnableUserEmailMuteList ) ) { |
209 | throw new ErrorPageError( 'specialmute', 'specialmute-error-mutelist-disabled' ); |
210 | } |
211 | |
212 | if ( !$this->getUser()->isEmailConfirmed() ) { |
213 | throw new ErrorPageError( 'specialmute', 'specialmute-error-no-email-set' ); |
214 | } |
215 | |
216 | $target = $this->getTarget(); |
217 | |
218 | if ( $target && $this->userIdentityUtils->isNamed( $target ) ) { |
219 | $fields['email-blacklist'] = [ |
220 | 'type' => 'check', |
221 | 'label-message' => [ |
222 | 'specialmute-label-mute-email', |
223 | $target->getName() |
224 | ], |
225 | 'default' => $this->isTargetMuted( 'email-blacklist' ), |
226 | ]; |
227 | } |
228 | |
229 | $legacyUser = $target ? User::newFromIdentity( $target ) : null; |
230 | $this->getHookRunner()->onSpecialMuteModifyFormFields( $legacyUser, $this->getUser(), $fields ); |
231 | |
232 | if ( count( $fields ) == 0 ) { |
233 | throw new ErrorPageError( 'specialmute', 'specialmute-error-no-options' ); |
234 | } |
235 | |
236 | return $fields; |
237 | } |
238 | |
239 | /** |
240 | * @param string|null $username |
241 | */ |
242 | private function loadTarget( $username ) { |
243 | $target = null; |
244 | if ( $username !== null ) { |
245 | $target = $this->userIdentityLookup->getUserIdentityByName( $username ); |
246 | } |
247 | if ( !$target || !$target->isRegistered() ) { |
248 | throw new ErrorPageError( 'specialmute', 'specialmute-error-invalid-user' ); |
249 | } else { |
250 | $this->target = $target; |
251 | $this->targetCentralId = $this->centralIdLookup->centralIdFromLocalUser( $target ); |
252 | } |
253 | } |
254 | |
255 | /** |
256 | * @param string $userOption |
257 | * @return bool |
258 | */ |
259 | public function isTargetMuted( $userOption ) { |
260 | $muteList = $this->getMuteList( $userOption ); |
261 | return in_array( $this->targetCentralId, $muteList, true ); |
262 | } |
263 | |
264 | /** |
265 | * @param string $userOption |
266 | * @return array |
267 | */ |
268 | private function getMuteList( $userOption ) { |
269 | $muteList = $this->userOptionsManager->getOption( $this->getUser(), $userOption ); |
270 | if ( !$muteList ) { |
271 | return []; |
272 | } |
273 | |
274 | return MultiUsernameFilter::splitIds( $muteList ); |
275 | } |
276 | } |
277 | |
278 | /** |
279 | * Retain the old class name for backwards compatibility. |
280 | * @deprecated since 1.41 |
281 | */ |
282 | class_alias( SpecialMute::class, 'SpecialMute' ); |