Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
72.06% |
196 / 272 |
|
28.57% |
4 / 14 |
CRAP | |
0.00% |
0 / 1 |
SpecialPageFactory | |
72.06% |
196 / 272 |
|
28.57% |
4 / 14 |
193.45 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
1 | |||
getNames | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getPageList | |
67.57% |
50 / 74 |
|
0.00% |
0 / 1 |
11.76 | |||
getAliasList | |
100.00% |
29 / 29 |
|
100.00% |
1 / 1 |
11 | |||
resolveAlias | |
88.89% |
8 / 9 |
|
0.00% |
0 / 1 |
2.01 | |||
exists | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
getPage | |
85.00% |
17 / 20 |
|
0.00% |
0 / 1 |
6.12 | |||
getUsablePages | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
42 | |||
getRegularPages | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
30 | |||
getListedPages | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
20 | |||
executePath | |
58.33% |
21 / 36 |
|
0.00% |
0 / 1 |
22.42 | |||
capturePath | |
100.00% |
41 / 41 |
|
100.00% |
1 / 1 |
3 | |||
getLocalNameFor | |
70.37% |
19 / 27 |
|
0.00% |
0 / 1 |
14.15 | |||
getTitleForAlias | |
75.00% |
3 / 4 |
|
0.00% |
0 / 1 |
2.06 |
1 | <?php |
2 | /** |
3 | * Factory for handling the special page list and generating SpecialPage objects. |
4 | * |
5 | * This program is free software; you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License as published by |
7 | * the Free Software Foundation; either version 2 of the License, or |
8 | * (at your option) any later version. |
9 | * |
10 | * This program is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | * GNU General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU General Public License along |
16 | * with this program; if not, write to the Free Software Foundation, Inc., |
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
18 | * http://www.gnu.org/copyleft/gpl.html |
19 | * |
20 | * @file |
21 | * @ingroup SpecialPage |
22 | * @defgroup SpecialPage SpecialPage |
23 | */ |
24 | |
25 | namespace MediaWiki\SpecialPage; |
26 | |
27 | use Language; |
28 | use MediaWiki\Config\ServiceOptions; |
29 | use MediaWiki\Context\IContextSource; |
30 | use MediaWiki\Context\RequestContext; |
31 | use MediaWiki\HookContainer\HookContainer; |
32 | use MediaWiki\HookContainer\HookRunner; |
33 | use MediaWiki\Linker\LinkRenderer; |
34 | use MediaWiki\MainConfigNames; |
35 | use MediaWiki\Page\PageReference; |
36 | use MediaWiki\Profiler\ProfilingContext; |
37 | use MediaWiki\Specials\Redirects\SpecialAllMyUploads; |
38 | use MediaWiki\Specials\Redirects\SpecialListAdmins; |
39 | use MediaWiki\Specials\Redirects\SpecialListBots; |
40 | use MediaWiki\Specials\Redirects\SpecialMycontributions; |
41 | use MediaWiki\Specials\Redirects\SpecialMylog; |
42 | use MediaWiki\Specials\Redirects\SpecialMypage; |
43 | use MediaWiki\Specials\Redirects\SpecialMytalk; |
44 | use MediaWiki\Specials\Redirects\SpecialMyuploads; |
45 | use MediaWiki\Specials\SpecialActiveUsers; |
46 | use MediaWiki\Specials\SpecialAllMessages; |
47 | use MediaWiki\Specials\SpecialAllPages; |
48 | use MediaWiki\Specials\SpecialAncientPages; |
49 | use MediaWiki\Specials\SpecialApiHelp; |
50 | use MediaWiki\Specials\SpecialApiSandbox; |
51 | use MediaWiki\Specials\SpecialAutoblockList; |
52 | use MediaWiki\Specials\SpecialBlankpage; |
53 | use MediaWiki\Specials\SpecialBlock; |
54 | use MediaWiki\Specials\SpecialBlockList; |
55 | use MediaWiki\Specials\SpecialBookSources; |
56 | use MediaWiki\Specials\SpecialBotPasswords; |
57 | use MediaWiki\Specials\SpecialBrokenRedirects; |
58 | use MediaWiki\Specials\SpecialCategories; |
59 | use MediaWiki\Specials\SpecialChangeContentModel; |
60 | use MediaWiki\Specials\SpecialChangeCredentials; |
61 | use MediaWiki\Specials\SpecialChangeEmail; |
62 | use MediaWiki\Specials\SpecialChangePassword; |
63 | use MediaWiki\Specials\SpecialComparePages; |
64 | use MediaWiki\Specials\SpecialConfirmEmail; |
65 | use MediaWiki\Specials\SpecialContribute; |
66 | use MediaWiki\Specials\SpecialContributions; |
67 | use MediaWiki\Specials\SpecialCreateAccount; |
68 | use MediaWiki\Specials\SpecialDeadendPages; |
69 | use MediaWiki\Specials\SpecialDeletedContributions; |
70 | use MediaWiki\Specials\SpecialDeletePage; |
71 | use MediaWiki\Specials\SpecialDiff; |
72 | use MediaWiki\Specials\SpecialDoubleRedirects; |
73 | use MediaWiki\Specials\SpecialEditPage; |
74 | use MediaWiki\Specials\SpecialEditRecovery; |
75 | use MediaWiki\Specials\SpecialEditTags; |
76 | use MediaWiki\Specials\SpecialEditWatchlist; |
77 | use MediaWiki\Specials\SpecialEmailInvalidate; |
78 | use MediaWiki\Specials\SpecialEmailUser; |
79 | use MediaWiki\Specials\SpecialExpandTemplates; |
80 | use MediaWiki\Specials\SpecialExport; |
81 | use MediaWiki\Specials\SpecialFewestRevisions; |
82 | use MediaWiki\Specials\SpecialFileDuplicateSearch; |
83 | use MediaWiki\Specials\SpecialFilepath; |
84 | use MediaWiki\Specials\SpecialGoToInterwiki; |
85 | use MediaWiki\Specials\SpecialImport; |
86 | use MediaWiki\Specials\SpecialJavaScriptTest; |
87 | use MediaWiki\Specials\SpecialLinkAccounts; |
88 | use MediaWiki\Specials\SpecialLinkSearch; |
89 | use MediaWiki\Specials\SpecialListDuplicatedFiles; |
90 | use MediaWiki\Specials\SpecialListFiles; |
91 | use MediaWiki\Specials\SpecialListGrants; |
92 | use MediaWiki\Specials\SpecialListGroupRights; |
93 | use MediaWiki\Specials\SpecialListRedirects; |
94 | use MediaWiki\Specials\SpecialListUsers; |
95 | use MediaWiki\Specials\SpecialLockdb; |
96 | use MediaWiki\Specials\SpecialLog; |
97 | use MediaWiki\Specials\SpecialLonelyPages; |
98 | use MediaWiki\Specials\SpecialLongPages; |
99 | use MediaWiki\Specials\SpecialMediaStatistics; |
100 | use MediaWiki\Specials\SpecialMergeHistory; |
101 | use MediaWiki\Specials\SpecialMIMESearch; |
102 | use MediaWiki\Specials\SpecialMostCategories; |
103 | use MediaWiki\Specials\SpecialMostImages; |
104 | use MediaWiki\Specials\SpecialMostInterwikis; |
105 | use MediaWiki\Specials\SpecialMostLinked; |
106 | use MediaWiki\Specials\SpecialMostLinkedCategories; |
107 | use MediaWiki\Specials\SpecialMostLinkedTemplates; |
108 | use MediaWiki\Specials\SpecialMostRevisions; |
109 | use MediaWiki\Specials\SpecialMovePage; |
110 | use MediaWiki\Specials\SpecialMute; |
111 | use MediaWiki\Specials\SpecialMyLanguage; |
112 | use MediaWiki\Specials\SpecialNewFiles; |
113 | use MediaWiki\Specials\SpecialNewPages; |
114 | use MediaWiki\Specials\SpecialNewSection; |
115 | use MediaWiki\Specials\SpecialPageData; |
116 | use MediaWiki\Specials\SpecialPageHistory; |
117 | use MediaWiki\Specials\SpecialPageInfo; |
118 | use MediaWiki\Specials\SpecialPageLanguage; |
119 | use MediaWiki\Specials\SpecialPagesWithProp; |
120 | use MediaWiki\Specials\SpecialPasswordPolicies; |
121 | use MediaWiki\Specials\SpecialPasswordReset; |
122 | use MediaWiki\Specials\SpecialPermanentLink; |
123 | use MediaWiki\Specials\SpecialPreferences; |
124 | use MediaWiki\Specials\SpecialPrefixIndex; |
125 | use MediaWiki\Specials\SpecialProtectedPages; |
126 | use MediaWiki\Specials\SpecialProtectedTitles; |
127 | use MediaWiki\Specials\SpecialProtectPage; |
128 | use MediaWiki\Specials\SpecialPurge; |
129 | use MediaWiki\Specials\SpecialRandomInCategory; |
130 | use MediaWiki\Specials\SpecialRandomPage; |
131 | use MediaWiki\Specials\SpecialRandomRedirect; |
132 | use MediaWiki\Specials\SpecialRandomRootPage; |
133 | use MediaWiki\Specials\SpecialRecentChanges; |
134 | use MediaWiki\Specials\SpecialRecentChangesLinked; |
135 | use MediaWiki\Specials\SpecialRedirect; |
136 | use MediaWiki\Specials\SpecialRemoveCredentials; |
137 | use MediaWiki\Specials\SpecialRenameUser; |
138 | use MediaWiki\Specials\SpecialResetTokens; |
139 | use MediaWiki\Specials\SpecialRevisionDelete; |
140 | use MediaWiki\Specials\SpecialRunJobs; |
141 | use MediaWiki\Specials\SpecialSearch; |
142 | use MediaWiki\Specials\SpecialShortPages; |
143 | use MediaWiki\Specials\SpecialSpecialPages; |
144 | use MediaWiki\Specials\SpecialStatistics; |
145 | use MediaWiki\Specials\SpecialTags; |
146 | use MediaWiki\Specials\SpecialTrackingCategories; |
147 | use MediaWiki\Specials\SpecialUnblock; |
148 | use MediaWiki\Specials\SpecialUncategorizedCategories; |
149 | use MediaWiki\Specials\SpecialUncategorizedImages; |
150 | use MediaWiki\Specials\SpecialUncategorizedPages; |
151 | use MediaWiki\Specials\SpecialUncategorizedTemplates; |
152 | use MediaWiki\Specials\SpecialUndelete; |
153 | use MediaWiki\Specials\SpecialUnlinkAccounts; |
154 | use MediaWiki\Specials\SpecialUnlockdb; |
155 | use MediaWiki\Specials\SpecialUnusedCategories; |
156 | use MediaWiki\Specials\SpecialUnusedImages; |
157 | use MediaWiki\Specials\SpecialUnusedTemplates; |
158 | use MediaWiki\Specials\SpecialUnwatchedPages; |
159 | use MediaWiki\Specials\SpecialUpload; |
160 | use MediaWiki\Specials\SpecialUploadStash; |
161 | use MediaWiki\Specials\SpecialUserLogin; |
162 | use MediaWiki\Specials\SpecialUserLogout; |
163 | use MediaWiki\Specials\SpecialUserRights; |
164 | use MediaWiki\Specials\SpecialVersion; |
165 | use MediaWiki\Specials\SpecialWantedCategories; |
166 | use MediaWiki\Specials\SpecialWantedFiles; |
167 | use MediaWiki\Specials\SpecialWantedPages; |
168 | use MediaWiki\Specials\SpecialWantedTemplates; |
169 | use MediaWiki\Specials\SpecialWatchlist; |
170 | use MediaWiki\Specials\SpecialWhatLinksHere; |
171 | use MediaWiki\Specials\SpecialWithoutInterwiki; |
172 | use MediaWiki\Title\Title; |
173 | use MediaWiki\Title\TitleFactory; |
174 | use MediaWiki\User\User; |
175 | use Profiler; |
176 | use Wikimedia\DebugInfo\DebugInfoTrait; |
177 | use Wikimedia\ObjectFactory\ObjectFactory; |
178 | |
179 | /** |
180 | * Factory for handling the special page list and generating SpecialPage objects. |
181 | * |
182 | * To add a special page in an extension, add to $wgSpecialPages either |
183 | * an object instance or an array containing the name and constructor |
184 | * parameters. The latter is preferred for performance reasons. |
185 | * |
186 | * The object instantiated must be either an instance of SpecialPage or a |
187 | * sub-class thereof. It must have an execute() method, which sends the HTML |
188 | * for the special page to $wgOut. The parent class has an execute() method |
189 | * which distributes the call to the historical global functions. Additionally, |
190 | * execute() also checks if the user has the necessary access privileges |
191 | * and bails out if not. |
192 | * |
193 | * To add a core special page, use the similar static list in |
194 | * SpecialPageFactory::$list. To remove a core static special page at runtime, use |
195 | * a SpecialPage_initList hook. |
196 | * |
197 | * @ingroup SpecialPage |
198 | * @since 1.17 |
199 | */ |
200 | class SpecialPageFactory { |
201 | use DebugInfoTrait; |
202 | |
203 | /** |
204 | * List of special page names to the subclass of SpecialPage which handles them. |
205 | */ |
206 | private const CORE_LIST = [ |
207 | // Maintenance Reports |
208 | 'BrokenRedirects' => [ |
209 | 'class' => SpecialBrokenRedirects::class, |
210 | 'services' => [ |
211 | 'ContentHandlerFactory', |
212 | 'ConnectionProvider', |
213 | 'LinkBatchFactory', |
214 | ] |
215 | ], |
216 | 'Deadendpages' => [ |
217 | 'class' => SpecialDeadendPages::class, |
218 | 'services' => [ |
219 | 'NamespaceInfo', |
220 | 'ConnectionProvider', |
221 | 'LinkBatchFactory', |
222 | 'LanguageConverterFactory', |
223 | ] |
224 | ], |
225 | 'DoubleRedirects' => [ |
226 | 'class' => SpecialDoubleRedirects::class, |
227 | 'services' => [ |
228 | 'ContentHandlerFactory', |
229 | 'LinkBatchFactory', |
230 | 'ConnectionProvider', |
231 | ] |
232 | ], |
233 | 'Longpages' => [ |
234 | 'class' => SpecialLongPages::class, |
235 | 'services' => [ |
236 | // Same as for Shortpages |
237 | 'NamespaceInfo', |
238 | 'ConnectionProvider', |
239 | 'LinkBatchFactory', |
240 | ] |
241 | ], |
242 | 'Ancientpages' => [ |
243 | 'class' => SpecialAncientPages::class, |
244 | 'services' => [ |
245 | 'NamespaceInfo', |
246 | 'ConnectionProvider', |
247 | 'LinkBatchFactory', |
248 | 'LanguageConverterFactory', |
249 | ] |
250 | ], |
251 | 'Lonelypages' => [ |
252 | 'class' => SpecialLonelyPages::class, |
253 | 'services' => [ |
254 | 'NamespaceInfo', |
255 | 'ConnectionProvider', |
256 | 'LinkBatchFactory', |
257 | 'LanguageConverterFactory', |
258 | 'LinksMigration', |
259 | ] |
260 | ], |
261 | 'Fewestrevisions' => [ |
262 | 'class' => SpecialFewestRevisions::class, |
263 | 'services' => [ |
264 | // Same as for Mostrevisions |
265 | 'NamespaceInfo', |
266 | 'ConnectionProvider', |
267 | 'LinkBatchFactory', |
268 | 'LanguageConverterFactory', |
269 | ] |
270 | ], |
271 | 'Withoutinterwiki' => [ |
272 | 'class' => SpecialWithoutInterwiki::class, |
273 | 'services' => [ |
274 | 'NamespaceInfo', |
275 | 'ConnectionProvider', |
276 | 'LinkBatchFactory', |
277 | 'LanguageConverterFactory', |
278 | ] |
279 | ], |
280 | 'Protectedpages' => [ |
281 | 'class' => SpecialProtectedPages::class, |
282 | 'services' => [ |
283 | 'LinkBatchFactory', |
284 | 'ConnectionProvider', |
285 | 'CommentStore', |
286 | 'UserCache', |
287 | 'RowCommentFormatter', |
288 | 'RestrictionStore', |
289 | ] |
290 | ], |
291 | 'Protectedtitles' => [ |
292 | 'class' => SpecialProtectedTitles::class, |
293 | 'services' => [ |
294 | 'LinkBatchFactory', |
295 | 'ConnectionProvider', |
296 | ] |
297 | ], |
298 | 'Shortpages' => [ |
299 | 'class' => SpecialShortPages::class, |
300 | 'services' => [ |
301 | // Same as for Longpages |
302 | 'NamespaceInfo', |
303 | 'ConnectionProvider', |
304 | 'LinkBatchFactory', |
305 | ] |
306 | ], |
307 | 'Uncategorizedcategories' => [ |
308 | 'class' => SpecialUncategorizedCategories::class, |
309 | 'services' => [ |
310 | // Same as for SpecialUncategorizedPages and SpecialUncategorizedTemplates |
311 | 'NamespaceInfo', |
312 | 'ConnectionProvider', |
313 | 'LinkBatchFactory', |
314 | 'LanguageConverterFactory', |
315 | ] |
316 | ], |
317 | 'Uncategorizedimages' => [ |
318 | 'class' => SpecialUncategorizedImages::class, |
319 | 'services' => [ |
320 | 'ConnectionProvider', |
321 | ] |
322 | ], |
323 | 'Uncategorizedpages' => [ |
324 | 'class' => SpecialUncategorizedPages::class, |
325 | 'services' => [ |
326 | // Same as for SpecialUncategorizedCategories and SpecialUncategorizedTemplates |
327 | 'NamespaceInfo', |
328 | 'ConnectionProvider', |
329 | 'LinkBatchFactory', |
330 | 'LanguageConverterFactory', |
331 | ] |
332 | ], |
333 | 'Uncategorizedtemplates' => [ |
334 | 'class' => SpecialUncategorizedTemplates::class, |
335 | 'services' => [ |
336 | // Same as for SpecialUncategorizedCategories and SpecialUncategorizedPages |
337 | 'NamespaceInfo', |
338 | 'ConnectionProvider', |
339 | 'LinkBatchFactory', |
340 | 'LanguageConverterFactory', |
341 | ] |
342 | ], |
343 | 'Unusedcategories' => [ |
344 | 'class' => SpecialUnusedCategories::class, |
345 | 'services' => [ |
346 | 'ConnectionProvider', |
347 | 'LinkBatchFactory', |
348 | ] |
349 | ], |
350 | 'Unusedimages' => [ |
351 | 'class' => SpecialUnusedImages::class, |
352 | 'services' => [ |
353 | 'ConnectionProvider', |
354 | ] |
355 | ], |
356 | 'Unusedtemplates' => [ |
357 | 'class' => SpecialUnusedTemplates::class, |
358 | 'services' => [ |
359 | 'ConnectionProvider', |
360 | 'LinksMigration', |
361 | ] |
362 | ], |
363 | 'Unwatchedpages' => [ |
364 | 'class' => SpecialUnwatchedPages::class, |
365 | 'services' => [ |
366 | 'LinkBatchFactory', |
367 | 'ConnectionProvider', |
368 | 'LanguageConverterFactory', |
369 | ] |
370 | ], |
371 | 'Wantedcategories' => [ |
372 | 'class' => SpecialWantedCategories::class, |
373 | 'services' => [ |
374 | 'ConnectionProvider', |
375 | 'LinkBatchFactory', |
376 | 'LanguageConverterFactory', |
377 | ] |
378 | ], |
379 | 'Wantedfiles' => [ |
380 | 'class' => SpecialWantedFiles::class, |
381 | 'services' => [ |
382 | 'RepoGroup', |
383 | 'ConnectionProvider', |
384 | 'LinkBatchFactory', |
385 | ] |
386 | ], |
387 | 'Wantedpages' => [ |
388 | 'class' => SpecialWantedPages::class, |
389 | 'services' => [ |
390 | 'ConnectionProvider', |
391 | 'LinkBatchFactory', |
392 | 'LinksMigration', |
393 | ] |
394 | ], |
395 | 'Wantedtemplates' => [ |
396 | 'class' => SpecialWantedTemplates::class, |
397 | 'services' => [ |
398 | 'ConnectionProvider', |
399 | 'LinkBatchFactory', |
400 | 'LinksMigration', |
401 | ] |
402 | ], |
403 | |
404 | // List of pages |
405 | 'Allpages' => [ |
406 | 'class' => SpecialAllPages::class, |
407 | 'services' => [ |
408 | 'ConnectionProvider', |
409 | 'SearchEngineFactory', |
410 | 'PageStore', |
411 | ] |
412 | ], |
413 | 'Prefixindex' => [ |
414 | 'class' => SpecialPrefixIndex::class, |
415 | 'services' => [ |
416 | 'ConnectionProvider', |
417 | 'LinkCache', |
418 | ] |
419 | ], |
420 | 'Categories' => [ |
421 | 'class' => SpecialCategories::class, |
422 | 'services' => [ |
423 | 'LinkBatchFactory', |
424 | 'ConnectionProvider', |
425 | ] |
426 | ], |
427 | 'Listredirects' => [ |
428 | 'class' => SpecialListRedirects::class, |
429 | 'services' => [ |
430 | 'LinkBatchFactory', |
431 | 'ConnectionProvider', |
432 | 'WikiPageFactory', |
433 | 'RedirectLookup' |
434 | ] |
435 | ], |
436 | 'PagesWithProp' => [ |
437 | 'class' => SpecialPagesWithProp::class, |
438 | 'services' => [ |
439 | 'ConnectionProvider', |
440 | ] |
441 | ], |
442 | 'TrackingCategories' => [ |
443 | 'class' => SpecialTrackingCategories::class, |
444 | 'services' => [ |
445 | 'LinkBatchFactory', |
446 | 'TrackingCategories', |
447 | ] |
448 | ], |
449 | |
450 | // Authentication |
451 | 'Userlogin' => [ |
452 | 'class' => SpecialUserLogin::class, |
453 | 'services' => [ |
454 | 'AuthManager', |
455 | ] |
456 | ], |
457 | 'Userlogout' => [ |
458 | 'class' => SpecialUserLogout::class, |
459 | ], |
460 | 'CreateAccount' => [ |
461 | 'class' => SpecialCreateAccount::class, |
462 | 'services' => [ |
463 | 'AuthManager', |
464 | 'FormatterFactory', |
465 | ] |
466 | ], |
467 | 'LinkAccounts' => [ |
468 | 'class' => SpecialLinkAccounts::class, |
469 | 'services' => [ |
470 | 'AuthManager', |
471 | ] |
472 | ], |
473 | 'UnlinkAccounts' => [ |
474 | 'class' => SpecialUnlinkAccounts::class, |
475 | 'services' => [ |
476 | 'AuthManager', |
477 | ] |
478 | ], |
479 | 'ChangeCredentials' => [ |
480 | 'class' => SpecialChangeCredentials::class, |
481 | 'services' => [ |
482 | 'AuthManager', |
483 | ] |
484 | ], |
485 | 'RemoveCredentials' => [ |
486 | 'class' => SpecialRemoveCredentials::class, |
487 | 'services' => [ |
488 | 'AuthManager', |
489 | ] |
490 | ], |
491 | |
492 | // Users and rights |
493 | 'Activeusers' => [ |
494 | 'class' => SpecialActiveUsers::class, |
495 | 'services' => [ |
496 | 'LinkBatchFactory', |
497 | 'ConnectionProvider', |
498 | 'UserGroupManager', |
499 | 'UserIdentityLookup', |
500 | 'HideUserUtils', |
501 | ] |
502 | ], |
503 | 'Block' => [ |
504 | 'class' => SpecialBlock::class, |
505 | 'services' => [ |
506 | 'BlockUtils', |
507 | 'BlockPermissionCheckerFactory', |
508 | 'BlockUserFactory', |
509 | 'DatabaseBlockStore', |
510 | 'UserNameUtils', |
511 | 'UserNamePrefixSearch', |
512 | 'BlockActionInfo', |
513 | 'TitleFormatter', |
514 | 'NamespaceInfo' |
515 | ] |
516 | ], |
517 | 'Unblock' => [ |
518 | 'class' => SpecialUnblock::class, |
519 | 'services' => [ |
520 | 'UnblockUserFactory', |
521 | 'BlockUtils', |
522 | 'DatabaseBlockStore', |
523 | 'UserNameUtils', |
524 | 'UserNamePrefixSearch', |
525 | 'WatchlistManager', |
526 | ] |
527 | ], |
528 | 'BlockList' => [ |
529 | 'class' => SpecialBlockList::class, |
530 | 'services' => [ |
531 | 'LinkBatchFactory', |
532 | 'DatabaseBlockStore', |
533 | 'BlockRestrictionStore', |
534 | 'ConnectionProvider', |
535 | 'CommentStore', |
536 | 'BlockUtils', |
537 | 'HideUserUtils', |
538 | 'BlockActionInfo', |
539 | 'RowCommentFormatter', |
540 | ], |
541 | ], |
542 | 'AutoblockList' => [ |
543 | 'class' => SpecialAutoblockList::class, |
544 | 'services' => [ |
545 | 'LinkBatchFactory', |
546 | 'BlockRestrictionStore', |
547 | 'ConnectionProvider', |
548 | 'CommentStore', |
549 | 'BlockUtils', |
550 | 'HideUserUtils', |
551 | 'BlockActionInfo', |
552 | 'RowCommentFormatter', |
553 | ], |
554 | ], |
555 | 'ChangePassword' => [ |
556 | 'class' => SpecialChangePassword::class, |
557 | ], |
558 | 'BotPasswords' => [ |
559 | 'class' => SpecialBotPasswords::class, |
560 | 'services' => [ |
561 | 'PasswordFactory', |
562 | 'AuthManager', |
563 | 'CentralIdLookup', |
564 | 'GrantsInfo', |
565 | 'GrantsLocalization', |
566 | ] |
567 | ], |
568 | 'PasswordReset' => [ |
569 | 'class' => SpecialPasswordReset::class, |
570 | 'services' => [ |
571 | 'PasswordReset' |
572 | ] |
573 | ], |
574 | 'DeletedContributions' => [ |
575 | 'class' => SpecialDeletedContributions::class, |
576 | 'services' => [ |
577 | 'PermissionManager', |
578 | 'ConnectionProvider', |
579 | 'RevisionFactory', |
580 | 'NamespaceInfo', |
581 | 'UserFactory', |
582 | 'UserNameUtils', |
583 | 'UserNamePrefixSearch', |
584 | 'CommentFormatter', |
585 | 'LinkBatchFactory', |
586 | 'DatabaseBlockStore', |
587 | ] |
588 | ], |
589 | 'Preferences' => [ |
590 | 'class' => SpecialPreferences::class, |
591 | 'services' => [ |
592 | 'PreferencesFactory', |
593 | 'UserOptionsManager', |
594 | ] |
595 | ], |
596 | 'ResetTokens' => [ |
597 | 'class' => SpecialResetTokens::class, |
598 | ], |
599 | 'Contributions' => [ |
600 | 'class' => SpecialContributions::class, |
601 | 'services' => [ |
602 | 'LinkBatchFactory', |
603 | 'PermissionManager', |
604 | 'ConnectionProvider', |
605 | 'RevisionStore', |
606 | 'NamespaceInfo', |
607 | 'UserNameUtils', |
608 | 'UserNamePrefixSearch', |
609 | 'UserOptionsLookup', |
610 | 'CommentFormatter', |
611 | 'UserFactory', |
612 | 'UserIdentityLookup', |
613 | 'DatabaseBlockStore', |
614 | ] |
615 | ], |
616 | 'Listgrouprights' => [ |
617 | 'class' => SpecialListGroupRights::class, |
618 | 'services' => [ |
619 | 'NamespaceInfo', |
620 | 'UserGroupManager', |
621 | 'LanguageConverterFactory', |
622 | 'GroupPermissionsLookup', |
623 | ] |
624 | ], |
625 | 'Listgrants' => [ |
626 | 'class' => SpecialListGrants::class, |
627 | 'services' => [ |
628 | 'GrantsLocalization', |
629 | ] |
630 | ], |
631 | 'Listusers' => [ |
632 | 'class' => SpecialListUsers::class, |
633 | 'services' => [ |
634 | 'LinkBatchFactory', |
635 | 'ConnectionProvider', |
636 | 'UserGroupManager', |
637 | 'UserIdentityLookup', |
638 | 'HideUserUtils', |
639 | ] |
640 | ], |
641 | 'Listadmins' => [ |
642 | 'class' => SpecialListAdmins::class, |
643 | ], |
644 | 'Listbots' => [ |
645 | 'class' => SpecialListBots::class, |
646 | ], |
647 | 'Userrights' => [ |
648 | 'class' => SpecialUserRights::class, |
649 | 'services' => [ |
650 | 'UserGroupManagerFactory', |
651 | 'UserNameUtils', |
652 | 'UserNamePrefixSearch', |
653 | 'UserFactory', |
654 | 'ActorStoreFactory', |
655 | 'WatchlistManager', |
656 | ] |
657 | ], |
658 | 'EditWatchlist' => [ |
659 | 'class' => SpecialEditWatchlist::class, |
660 | 'services' => [ |
661 | 'WatchedItemStore', |
662 | 'TitleParser', |
663 | 'GenderCache', |
664 | 'LinkBatchFactory', |
665 | 'NamespaceInfo', |
666 | 'WikiPageFactory', |
667 | 'WatchlistManager', |
668 | ] |
669 | ], |
670 | 'PasswordPolicies' => [ |
671 | 'class' => SpecialPasswordPolicies::class, |
672 | 'services' => [ |
673 | 'UserGroupManager', |
674 | ] |
675 | ], |
676 | |
677 | // Recent changes and logs |
678 | 'Newimages' => [ |
679 | 'class' => SpecialNewFiles::class, |
680 | 'services' => [ |
681 | 'MimeAnalyzer', |
682 | 'GroupPermissionsLookup', |
683 | 'ConnectionProvider', |
684 | 'LinkBatchFactory', |
685 | ] |
686 | ], |
687 | 'Log' => [ |
688 | 'class' => SpecialLog::class, |
689 | 'services' => [ |
690 | 'LinkBatchFactory', |
691 | 'ConnectionProvider', |
692 | 'ActorNormalization', |
693 | 'UserIdentityLookup', |
694 | 'UserNameUtils', |
695 | ] |
696 | ], |
697 | 'Watchlist' => [ |
698 | 'class' => SpecialWatchlist::class, |
699 | 'services' => [ |
700 | 'WatchedItemStore', |
701 | 'WatchlistManager', |
702 | 'UserOptionsLookup', |
703 | 'ChangeTagsStore', |
704 | 'UserIdentityUtils', |
705 | 'TempUserConfig', |
706 | ] |
707 | ], |
708 | 'Newpages' => [ |
709 | 'class' => SpecialNewPages::class, |
710 | 'services' => [ |
711 | 'LinkBatchFactory', |
712 | 'ContentHandlerFactory', |
713 | 'GroupPermissionsLookup', |
714 | 'RevisionLookup', |
715 | 'NamespaceInfo', |
716 | 'UserOptionsLookup', |
717 | 'RowCommentFormatter', |
718 | 'ChangeTagsStore', |
719 | ] |
720 | ], |
721 | 'Recentchanges' => [ |
722 | 'class' => SpecialRecentChanges::class, |
723 | 'services' => [ |
724 | 'WatchedItemStore', |
725 | 'MessageCache', |
726 | 'UserOptionsLookup', |
727 | 'ChangeTagsStore', |
728 | 'UserIdentityUtils', |
729 | 'TempUserConfig', |
730 | ] |
731 | ], |
732 | 'Recentchangeslinked' => [ |
733 | 'class' => SpecialRecentChangesLinked::class, |
734 | 'services' => [ |
735 | 'WatchedItemStore', |
736 | 'MessageCache', |
737 | 'UserOptionsLookup', |
738 | 'SearchEngineFactory', |
739 | 'ChangeTagsStore', |
740 | 'UserIdentityUtils', |
741 | 'TempUserConfig', |
742 | ] |
743 | ], |
744 | 'Tags' => [ |
745 | 'class' => SpecialTags::class, |
746 | 'services' => [ |
747 | 'ChangeTagsStore', |
748 | ] |
749 | ], |
750 | |
751 | // Media reports and uploads |
752 | 'Listfiles' => [ |
753 | 'class' => SpecialListFiles::class, |
754 | 'services' => [ |
755 | 'RepoGroup', |
756 | 'ConnectionProvider', |
757 | 'CommentStore', |
758 | 'UserNameUtils', |
759 | 'UserNamePrefixSearch', |
760 | 'UserCache', |
761 | 'CommentFormatter', |
762 | ] |
763 | ], |
764 | 'Filepath' => [ |
765 | 'class' => SpecialFilepath::class, |
766 | 'services' => [ |
767 | 'SearchEngineFactory', |
768 | ] |
769 | ], |
770 | 'MediaStatistics' => [ |
771 | 'class' => SpecialMediaStatistics::class, |
772 | 'services' => [ |
773 | 'MimeAnalyzer', |
774 | 'ConnectionProvider', |
775 | 'LinkBatchFactory', |
776 | ] |
777 | ], |
778 | 'MIMEsearch' => [ |
779 | 'class' => SpecialMIMESearch::class, |
780 | 'services' => [ |
781 | 'ConnectionProvider', |
782 | 'LinkBatchFactory', |
783 | 'LanguageConverterFactory', |
784 | ] |
785 | ], |
786 | 'FileDuplicateSearch' => [ |
787 | 'class' => SpecialFileDuplicateSearch::class, |
788 | 'services' => [ |
789 | 'LinkBatchFactory', |
790 | 'RepoGroup', |
791 | 'SearchEngineFactory', |
792 | 'LanguageConverterFactory', |
793 | ] |
794 | ], |
795 | 'Upload' => [ |
796 | 'class' => SpecialUpload::class, |
797 | 'services' => [ |
798 | 'RepoGroup', |
799 | 'UserOptionsLookup', |
800 | 'NamespaceInfo', |
801 | ] |
802 | ], |
803 | 'UploadStash' => [ |
804 | 'class' => SpecialUploadStash::class, |
805 | 'services' => [ |
806 | 'RepoGroup', |
807 | 'HttpRequestFactory', |
808 | 'UrlUtils', |
809 | 'ConnectionProvider', |
810 | ] |
811 | ], |
812 | 'ListDuplicatedFiles' => [ |
813 | 'class' => SpecialListDuplicatedFiles::class, |
814 | 'services' => [ |
815 | 'ConnectionProvider', |
816 | 'LinkBatchFactory', |
817 | ] |
818 | ], |
819 | |
820 | // Data and tools |
821 | 'ApiSandbox' => [ |
822 | 'class' => SpecialApiSandbox::class, |
823 | ], |
824 | 'Statistics' => [ |
825 | 'class' => SpecialStatistics::class, |
826 | 'services' => [ |
827 | 'UserGroupManager', |
828 | ] |
829 | ], |
830 | 'Allmessages' => [ |
831 | 'class' => SpecialAllMessages::class, |
832 | 'services' => [ |
833 | 'LanguageFactory', |
834 | 'LanguageNameUtils', |
835 | 'LocalisationCache', |
836 | 'ConnectionProvider', |
837 | ] |
838 | ], |
839 | 'Version' => [ |
840 | 'class' => SpecialVersion::class, |
841 | 'services' => [ |
842 | 'ParserFactory', |
843 | 'UrlUtils', |
844 | 'ConnectionProvider', |
845 | ] |
846 | ], |
847 | 'Lockdb' => [ |
848 | 'class' => SpecialLockdb::class, |
849 | ], |
850 | 'Unlockdb' => [ |
851 | 'class' => SpecialUnlockdb::class, |
852 | ], |
853 | |
854 | // Redirecting special pages |
855 | 'LinkSearch' => [ |
856 | 'class' => SpecialLinkSearch::class, |
857 | 'services' => [ |
858 | 'ConnectionProvider', |
859 | 'LinkBatchFactory', |
860 | 'UrlUtils', |
861 | ] |
862 | ], |
863 | 'Randompage' => [ |
864 | 'class' => SpecialRandomPage::class, |
865 | 'services' => [ |
866 | 'ConnectionProvider', |
867 | 'NamespaceInfo', |
868 | ] |
869 | ], |
870 | 'RandomInCategory' => [ |
871 | 'class' => SpecialRandomInCategory::class, |
872 | 'services' => [ |
873 | 'ConnectionProvider', |
874 | ] |
875 | ], |
876 | 'Randomredirect' => [ |
877 | 'class' => SpecialRandomRedirect::class, |
878 | 'services' => [ |
879 | 'ConnectionProvider', |
880 | 'NamespaceInfo', |
881 | ] |
882 | ], |
883 | 'Randomrootpage' => [ |
884 | 'class' => SpecialRandomRootPage::class, |
885 | 'services' => [ |
886 | 'ConnectionProvider', |
887 | 'NamespaceInfo', |
888 | ] |
889 | ], |
890 | 'GoToInterwiki' => [ |
891 | 'class' => SpecialGoToInterwiki::class, |
892 | ], |
893 | |
894 | // High use pages |
895 | 'Mostlinkedcategories' => [ |
896 | 'class' => SpecialMostLinkedCategories::class, |
897 | 'services' => [ |
898 | 'ConnectionProvider', |
899 | 'LinkBatchFactory', |
900 | 'LanguageConverterFactory', |
901 | ] |
902 | ], |
903 | 'Mostimages' => [ |
904 | 'class' => SpecialMostImages::class, |
905 | 'services' => [ |
906 | 'ConnectionProvider', |
907 | 'LanguageConverterFactory', |
908 | ] |
909 | ], |
910 | 'Mostinterwikis' => [ |
911 | 'class' => SpecialMostInterwikis::class, |
912 | 'services' => [ |
913 | 'NamespaceInfo', |
914 | 'ConnectionProvider', |
915 | 'LinkBatchFactory', |
916 | ] |
917 | ], |
918 | 'Mostlinked' => [ |
919 | 'class' => SpecialMostLinked::class, |
920 | 'services' => [ |
921 | 'ConnectionProvider', |
922 | 'LinkBatchFactory', |
923 | 'LinksMigration', |
924 | ] |
925 | ], |
926 | 'Mostlinkedtemplates' => [ |
927 | 'class' => SpecialMostLinkedTemplates::class, |
928 | 'services' => [ |
929 | 'ConnectionProvider', |
930 | 'LinkBatchFactory', |
931 | 'LinksMigration', |
932 | ] |
933 | ], |
934 | 'Mostcategories' => [ |
935 | 'class' => SpecialMostCategories::class, |
936 | 'services' => [ |
937 | 'NamespaceInfo', |
938 | 'ConnectionProvider', |
939 | 'LinkBatchFactory', |
940 | ] |
941 | ], |
942 | 'Mostrevisions' => [ |
943 | 'class' => SpecialMostRevisions::class, |
944 | 'services' => [ |
945 | // Same as for Fewestrevisions |
946 | 'NamespaceInfo', |
947 | 'ConnectionProvider', |
948 | 'LinkBatchFactory', |
949 | 'LanguageConverterFactory', |
950 | ] |
951 | ], |
952 | |
953 | // Page tools |
954 | 'ComparePages' => [ |
955 | 'class' => SpecialComparePages::class, |
956 | 'services' => [ |
957 | 'RevisionLookup', |
958 | 'ContentHandlerFactory', |
959 | ] |
960 | ], |
961 | 'Export' => [ |
962 | 'class' => SpecialExport::class, |
963 | 'services' => [ |
964 | 'ConnectionProvider', |
965 | 'WikiExporterFactory', |
966 | 'TitleFormatter', |
967 | 'LinksMigration', |
968 | ] |
969 | ], |
970 | 'Import' => [ |
971 | 'class' => SpecialImport::class, |
972 | 'services' => [ |
973 | 'PermissionManager', |
974 | 'WikiImporterFactory', |
975 | ] |
976 | ], |
977 | 'Undelete' => [ |
978 | 'class' => SpecialUndelete::class, |
979 | 'services' => [ |
980 | 'PermissionManager', |
981 | 'RevisionStore', |
982 | 'RevisionRenderer', |
983 | 'ContentHandlerFactory', |
984 | 'ChangeTagDefStore', |
985 | 'LinkBatchFactory', |
986 | 'RepoGroup', |
987 | 'ConnectionProvider', |
988 | 'UserOptionsLookup', |
989 | 'WikiPageFactory', |
990 | 'SearchEngineFactory', |
991 | 'UndeletePageFactory', |
992 | 'ArchivedRevisionLookup', |
993 | 'CommentFormatter', |
994 | 'WatchlistManager', |
995 | ], |
996 | ], |
997 | 'Whatlinkshere' => [ |
998 | 'class' => SpecialWhatLinksHere::class, |
999 | 'services' => [ |
1000 | 'ConnectionProvider', |
1001 | 'LinkBatchFactory', |
1002 | 'ContentHandlerFactory', |
1003 | 'SearchEngineFactory', |
1004 | 'NamespaceInfo', |
1005 | 'TitleFactory', |
1006 | 'LinksMigration', |
1007 | ] |
1008 | ], |
1009 | 'MergeHistory' => [ |
1010 | 'class' => SpecialMergeHistory::class, |
1011 | 'services' => [ |
1012 | 'MergeHistoryFactory', |
1013 | 'LinkBatchFactory', |
1014 | 'ConnectionProvider', |
1015 | 'RevisionStore', |
1016 | 'CommentFormatter', |
1017 | ] |
1018 | ], |
1019 | 'ExpandTemplates' => [ |
1020 | 'class' => SpecialExpandTemplates::class, |
1021 | 'services' => [ |
1022 | 'ParserFactory', |
1023 | 'UserOptionsLookup', |
1024 | 'Tidy', |
1025 | ], |
1026 | ], |
1027 | 'ChangeContentModel' => [ |
1028 | 'class' => SpecialChangeContentModel::class, |
1029 | 'services' => [ |
1030 | 'ContentHandlerFactory', |
1031 | 'ContentModelChangeFactory', |
1032 | 'SpamChecker', |
1033 | 'RevisionLookup', |
1034 | 'WikiPageFactory', |
1035 | 'SearchEngineFactory', |
1036 | 'CollationFactory', |
1037 | ], |
1038 | ], |
1039 | |
1040 | // Other |
1041 | 'Booksources' => [ |
1042 | 'class' => SpecialBookSources::class, |
1043 | 'services' => [ |
1044 | 'RevisionLookup', |
1045 | 'TitleFactory', |
1046 | ] |
1047 | ], |
1048 | |
1049 | // Unlisted / redirects |
1050 | 'ApiHelp' => [ |
1051 | 'class' => SpecialApiHelp::class, |
1052 | 'services' => [ |
1053 | 'UrlUtils', |
1054 | ] |
1055 | ], |
1056 | 'Blankpage' => [ |
1057 | 'class' => SpecialBlankpage::class, |
1058 | ], |
1059 | 'DeletePage' => [ |
1060 | 'class' => SpecialDeletePage::class, |
1061 | 'services' => [ |
1062 | 'SearchEngineFactory', |
1063 | ] |
1064 | ], |
1065 | 'Diff' => [ |
1066 | 'class' => SpecialDiff::class, |
1067 | ], |
1068 | 'EditPage' => [ |
1069 | 'class' => SpecialEditPage::class, |
1070 | 'services' => [ |
1071 | 'SearchEngineFactory', |
1072 | ] |
1073 | ], |
1074 | 'EditTags' => [ |
1075 | 'class' => SpecialEditTags::class, |
1076 | 'services' => [ |
1077 | 'PermissionManager', |
1078 | 'ChangeTagsStore', |
1079 | ], |
1080 | ], |
1081 | 'Emailuser' => [ |
1082 | 'class' => SpecialEmailUser::class, |
1083 | 'services' => [ |
1084 | 'UserNameUtils', |
1085 | 'UserNamePrefixSearch', |
1086 | 'UserOptionsLookup', |
1087 | 'EmailUserFactory', |
1088 | 'UserFactory', |
1089 | ] |
1090 | ], |
1091 | 'Movepage' => [ |
1092 | 'class' => SpecialMovePage::class, |
1093 | 'services' => [ |
1094 | 'MovePageFactory', |
1095 | 'PermissionManager', |
1096 | 'UserOptionsLookup', |
1097 | 'ConnectionProvider', |
1098 | 'ContentHandlerFactory', |
1099 | 'NamespaceInfo', |
1100 | 'LinkBatchFactory', |
1101 | 'RepoGroup', |
1102 | 'WikiPageFactory', |
1103 | 'SearchEngineFactory', |
1104 | 'WatchlistManager', |
1105 | 'RestrictionStore', |
1106 | 'TitleFactory', |
1107 | ] |
1108 | ], |
1109 | 'Mycontributions' => [ |
1110 | 'class' => SpecialMycontributions::class, |
1111 | ], |
1112 | 'MyLanguage' => [ |
1113 | 'class' => SpecialMyLanguage::class, |
1114 | 'services' => [ |
1115 | 'LanguageNameUtils', |
1116 | 'RedirectLookup' |
1117 | ] |
1118 | ], |
1119 | 'Mylog' => [ |
1120 | 'class' => SpecialMylog::class, |
1121 | ], |
1122 | 'Mypage' => [ |
1123 | 'class' => SpecialMypage::class, |
1124 | ], |
1125 | 'Mytalk' => [ |
1126 | 'class' => SpecialMytalk::class, |
1127 | ], |
1128 | 'PageHistory' => [ |
1129 | 'class' => SpecialPageHistory::class, |
1130 | 'services' => [ |
1131 | 'SearchEngineFactory', |
1132 | ] |
1133 | ], |
1134 | 'PageInfo' => [ |
1135 | 'class' => SpecialPageInfo::class, |
1136 | 'services' => [ |
1137 | 'SearchEngineFactory', |
1138 | ] |
1139 | ], |
1140 | 'ProtectPage' => [ |
1141 | 'class' => SpecialProtectPage::class, |
1142 | 'services' => [ |
1143 | 'SearchEngineFactory', |
1144 | ] |
1145 | ], |
1146 | 'Purge' => [ |
1147 | 'class' => SpecialPurge::class, |
1148 | 'services' => [ |
1149 | 'SearchEngineFactory', |
1150 | ] |
1151 | ], |
1152 | 'Myuploads' => [ |
1153 | 'class' => SpecialMyuploads::class, |
1154 | ], |
1155 | 'AllMyUploads' => [ |
1156 | 'class' => SpecialAllMyUploads::class, |
1157 | ], |
1158 | 'NewSection' => [ |
1159 | 'class' => SpecialNewSection::class, |
1160 | 'services' => [ |
1161 | 'SearchEngineFactory', |
1162 | ] |
1163 | ], |
1164 | 'PermanentLink' => [ |
1165 | 'class' => SpecialPermanentLink::class, |
1166 | ], |
1167 | 'Redirect' => [ |
1168 | 'class' => SpecialRedirect::class, |
1169 | 'services' => [ |
1170 | 'RepoGroup', |
1171 | 'UserFactory', |
1172 | ] |
1173 | ], |
1174 | 'Renameuser' => [ |
1175 | 'class' => SpecialRenameUser::class, |
1176 | 'services' => [ |
1177 | 'ConnectionProvider', |
1178 | 'ContentLanguage', |
1179 | 'MovePageFactory', |
1180 | 'PermissionManager', |
1181 | 'TitleFactory', |
1182 | 'UserFactory', |
1183 | 'UserNamePrefixSearch', |
1184 | 'UserNameUtils', |
1185 | ] |
1186 | ], |
1187 | 'Revisiondelete' => [ |
1188 | 'class' => SpecialRevisionDelete::class, |
1189 | 'services' => [ |
1190 | 'PermissionManager', |
1191 | 'RepoGroup', |
1192 | ], |
1193 | ], |
1194 | 'RunJobs' => [ |
1195 | 'class' => SpecialRunJobs::class, |
1196 | 'services' => [ |
1197 | 'JobRunner', |
1198 | 'ReadOnlyMode', |
1199 | ] |
1200 | ], |
1201 | 'Specialpages' => [ |
1202 | 'class' => SpecialSpecialPages::class, |
1203 | ], |
1204 | 'PageData' => [ |
1205 | 'class' => SpecialPageData::class, |
1206 | ], |
1207 | 'Contribute' => [ |
1208 | 'class' => SpecialContribute::class, |
1209 | ], |
1210 | ]; |
1211 | |
1212 | /** @var array Special page name => class name */ |
1213 | private $list; |
1214 | |
1215 | /** @var array */ |
1216 | private $aliases; |
1217 | |
1218 | /** @var ServiceOptions */ |
1219 | private $options; |
1220 | |
1221 | /** @var Language */ |
1222 | private $contLang; |
1223 | |
1224 | /** |
1225 | * @var ObjectFactory |
1226 | * @noVarDump |
1227 | */ |
1228 | private $objectFactory; |
1229 | |
1230 | /** |
1231 | * @var HookContainer |
1232 | * @noVarDump |
1233 | */ |
1234 | private $hookContainer; |
1235 | |
1236 | /** |
1237 | * @var HookRunner |
1238 | * @noVarDump |
1239 | */ |
1240 | private $hookRunner; |
1241 | |
1242 | /** |
1243 | * @internal For use by ServiceWiring |
1244 | */ |
1245 | public const CONSTRUCTOR_OPTIONS = [ |
1246 | MainConfigNames::DisableInternalSearch, |
1247 | MainConfigNames::EmailAuthentication, |
1248 | MainConfigNames::EnableEmail, |
1249 | MainConfigNames::EnableJavaScriptTest, |
1250 | MainConfigNames::EnableSpecialMute, |
1251 | MainConfigNames::EnableEditRecovery, |
1252 | MainConfigNames::PageLanguageUseDB, |
1253 | MainConfigNames::SpecialPages, |
1254 | ]; |
1255 | |
1256 | /** |
1257 | * @var TitleFactory |
1258 | */ |
1259 | private $titleFactory; |
1260 | |
1261 | /** |
1262 | * @param ServiceOptions $options |
1263 | * @param Language $contLang |
1264 | * @param ObjectFactory $objectFactory |
1265 | * @param TitleFactory $titleFactory |
1266 | * @param HookContainer $hookContainer |
1267 | */ |
1268 | public function __construct( |
1269 | ServiceOptions $options, |
1270 | Language $contLang, |
1271 | ObjectFactory $objectFactory, |
1272 | TitleFactory $titleFactory, |
1273 | HookContainer $hookContainer |
1274 | ) { |
1275 | $options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS ); |
1276 | $this->options = $options; |
1277 | $this->contLang = $contLang; |
1278 | $this->objectFactory = $objectFactory; |
1279 | $this->titleFactory = $titleFactory; |
1280 | $this->hookContainer = $hookContainer; |
1281 | $this->hookRunner = new HookRunner( $hookContainer ); |
1282 | } |
1283 | |
1284 | /** |
1285 | * Returns a list of canonical special page names. |
1286 | * May be used to iterate over all registered special pages. |
1287 | * |
1288 | * @return string[] |
1289 | */ |
1290 | public function getNames(): array { |
1291 | return array_keys( $this->getPageList() ); |
1292 | } |
1293 | |
1294 | /** |
1295 | * Get the special page list as an array |
1296 | * |
1297 | * @return array |
1298 | */ |
1299 | private function getPageList(): array { |
1300 | if ( !is_array( $this->list ) ) { |
1301 | $this->list = self::CORE_LIST; |
1302 | |
1303 | if ( !$this->options->get( MainConfigNames::DisableInternalSearch ) ) { |
1304 | $this->list['Search'] = [ |
1305 | 'class' => SpecialSearch::class, |
1306 | 'services' => [ |
1307 | 'SearchEngineConfig', |
1308 | 'SearchEngineFactory', |
1309 | 'NamespaceInfo', |
1310 | 'ContentHandlerFactory', |
1311 | 'InterwikiLookup', |
1312 | 'ReadOnlyMode', |
1313 | 'UserOptionsManager', |
1314 | 'LanguageConverterFactory', |
1315 | 'RepoGroup', |
1316 | 'SearchResultThumbnailProvider', |
1317 | 'TitleMatcher', |
1318 | ] |
1319 | ]; |
1320 | } |
1321 | |
1322 | if ( $this->options->get( MainConfigNames::EmailAuthentication ) ) { |
1323 | $this->list['Confirmemail'] = [ |
1324 | 'class' => SpecialConfirmEmail::class, |
1325 | 'services' => [ |
1326 | 'UserFactory', |
1327 | ] |
1328 | ]; |
1329 | $this->list['Invalidateemail'] = [ |
1330 | 'class' => SpecialEmailInvalidate::class, |
1331 | 'services' => [ |
1332 | 'UserFactory', |
1333 | ] |
1334 | ]; |
1335 | } |
1336 | |
1337 | if ( $this->options->get( MainConfigNames::EnableEmail ) ) { |
1338 | $this->list['ChangeEmail'] = [ |
1339 | 'class' => SpecialChangeEmail::class, |
1340 | 'services' => [ |
1341 | 'AuthManager', |
1342 | ], |
1343 | ]; |
1344 | } |
1345 | |
1346 | if ( $this->options->get( MainConfigNames::EnableJavaScriptTest ) ) { |
1347 | $this->list['JavaScriptTest'] = [ |
1348 | 'class' => SpecialJavaScriptTest::class |
1349 | ]; |
1350 | } |
1351 | |
1352 | if ( $this->options->get( MainConfigNames::EnableSpecialMute ) ) { |
1353 | $this->list['Mute'] = [ |
1354 | 'class' => SpecialMute::class, |
1355 | 'services' => [ |
1356 | 'CentralIdLookup', |
1357 | 'UserOptionsManager', |
1358 | 'UserIdentityLookup', |
1359 | 'UserIdentityUtils', |
1360 | ] |
1361 | ]; |
1362 | } |
1363 | |
1364 | if ( $this->options->get( MainConfigNames::PageLanguageUseDB ) ) { |
1365 | $this->list['PageLanguage'] = [ |
1366 | 'class' => SpecialPageLanguage::class, |
1367 | 'services' => [ |
1368 | 'ContentHandlerFactory', |
1369 | 'LanguageNameUtils', |
1370 | 'ConnectionProvider', |
1371 | 'SearchEngineFactory', |
1372 | ] |
1373 | ]; |
1374 | } |
1375 | |
1376 | if ( $this->options->get( MainConfigNames::EnableEditRecovery ) ) { |
1377 | $this->list['EditRecovery'] = [ |
1378 | 'class' => SpecialEditRecovery::class, |
1379 | 'services' => [ |
1380 | 'UserOptionsLookup', |
1381 | ], |
1382 | ]; |
1383 | } |
1384 | |
1385 | // Add extension special pages |
1386 | $this->list = array_merge( $this->list, |
1387 | $this->options->get( MainConfigNames::SpecialPages ) ); |
1388 | |
1389 | // This hook can be used to disable unwanted core special pages |
1390 | // or conditionally register special pages. |
1391 | $this->hookRunner->onSpecialPage_initList( $this->list ); |
1392 | } |
1393 | |
1394 | return $this->list; |
1395 | } |
1396 | |
1397 | /** |
1398 | * Initialise and return the list of special page aliases. Returns an array where |
1399 | * the key is an alias, and the value is the canonical name of the special page. |
1400 | * All registered special pages are guaranteed to map to themselves. |
1401 | * @return array |
1402 | */ |
1403 | private function getAliasList(): array { |
1404 | if ( $this->aliases === null ) { |
1405 | $aliases = $this->contLang->getSpecialPageAliases(); |
1406 | $pageList = $this->getPageList(); |
1407 | |
1408 | $this->aliases = []; |
1409 | $keepAlias = []; |
1410 | |
1411 | // Force every canonical name to be an alias for itself. |
1412 | foreach ( $pageList as $name => $stuff ) { |
1413 | $caseFoldedAlias = $this->contLang->caseFold( $name ); |
1414 | $this->aliases[$caseFoldedAlias] = $name; |
1415 | $keepAlias[$caseFoldedAlias] = 'canonical'; |
1416 | } |
1417 | |
1418 | // Check for $aliases being an array since Language::getSpecialPageAliases can return null |
1419 | if ( is_array( $aliases ) ) { |
1420 | foreach ( $aliases as $realName => $aliasList ) { |
1421 | $first = true; |
1422 | foreach ( $aliasList as $alias ) { |
1423 | $caseFoldedAlias = $this->contLang->caseFold( $alias ); |
1424 | |
1425 | if ( isset( $this->aliases[$caseFoldedAlias] ) && |
1426 | $realName === $this->aliases[$caseFoldedAlias] |
1427 | ) { |
1428 | $first = false; |
1429 | // Ignore same-realName conflicts |
1430 | continue; |
1431 | } |
1432 | |
1433 | if ( !isset( $keepAlias[$caseFoldedAlias] ) ) { |
1434 | $this->aliases[$caseFoldedAlias] = $realName; |
1435 | if ( $first ) { |
1436 | $keepAlias[$caseFoldedAlias] = 'first'; |
1437 | } |
1438 | } elseif ( $first ) { |
1439 | wfWarn( "First alias '$alias' for $realName conflicts with " . |
1440 | "{$keepAlias[$caseFoldedAlias]} alias for " . |
1441 | $this->aliases[$caseFoldedAlias] |
1442 | ); |
1443 | } |
1444 | $first = false; |
1445 | } |
1446 | } |
1447 | } |
1448 | } |
1449 | |
1450 | return $this->aliases; |
1451 | } |
1452 | |
1453 | /** |
1454 | * Given a special page name with a possible subpage, return an array |
1455 | * where the first element is the special page name and the second is the |
1456 | * subpage. |
1457 | * |
1458 | * @param string $alias |
1459 | * @return array [ String, String|null ], or [ null, null ] if the page is invalid |
1460 | */ |
1461 | public function resolveAlias( $alias ) { |
1462 | $bits = explode( '/', $alias, 2 ); |
1463 | |
1464 | $caseFoldedAlias = $this->contLang->caseFold( $bits[0] ); |
1465 | $caseFoldedAlias = str_replace( ' ', '_', $caseFoldedAlias ); |
1466 | $aliases = $this->getAliasList(); |
1467 | if ( !isset( $aliases[$caseFoldedAlias] ) ) { |
1468 | return [ null, null ]; |
1469 | } |
1470 | $name = $aliases[$caseFoldedAlias]; |
1471 | $par = $bits[1] ?? null; // T4087 |
1472 | |
1473 | return [ $name, $par ]; |
1474 | } |
1475 | |
1476 | /** |
1477 | * Check if a given name exist as a special page or as a special page alias |
1478 | * |
1479 | * @param string $name Name of a special page |
1480 | * @return bool True if a special page exists with this name |
1481 | */ |
1482 | public function exists( $name ) { |
1483 | [ $title, /*...*/ ] = $this->resolveAlias( $name ); |
1484 | |
1485 | $specialPageList = $this->getPageList(); |
1486 | return isset( $specialPageList[$title] ); |
1487 | } |
1488 | |
1489 | /** |
1490 | * Find the object with a given name and return it (or NULL) |
1491 | * |
1492 | * @param string $name Special page name, may be localised and/or an alias |
1493 | * @return SpecialPage|null SpecialPage object or null if the page doesn't exist |
1494 | */ |
1495 | public function getPage( $name ) { |
1496 | [ $realName, /*...*/ ] = $this->resolveAlias( $name ); |
1497 | |
1498 | $specialPageList = $this->getPageList(); |
1499 | |
1500 | if ( isset( $specialPageList[$realName] ) ) { |
1501 | $rec = $specialPageList[$realName]; |
1502 | |
1503 | if ( is_array( $rec ) || is_string( $rec ) || is_callable( $rec ) ) { |
1504 | $page = $this->objectFactory->createObject( |
1505 | $rec, |
1506 | [ |
1507 | 'allowClassName' => true, |
1508 | 'allowCallable' => true |
1509 | ] |
1510 | ); |
1511 | } else { |
1512 | $page = null; |
1513 | } |
1514 | |
1515 | if ( $page instanceof SpecialPage ) { |
1516 | $page->setHookContainer( $this->hookContainer ); |
1517 | $page->setContentLanguage( $this->contLang ); |
1518 | $page->setSpecialPageFactory( $this ); |
1519 | return $page; |
1520 | } |
1521 | |
1522 | // It's not a classname, nor a callback, nor a legacy constructor array, |
1523 | // nor a special page object. Give up. |
1524 | wfLogWarning( "Cannot instantiate special page $realName: bad spec!" ); |
1525 | } |
1526 | |
1527 | return null; |
1528 | } |
1529 | |
1530 | /** |
1531 | * Get listed special pages available to the current user. |
1532 | * |
1533 | * This includes both unrestricted pages, and restricted pages |
1534 | * that the current user has the required permissions for. |
1535 | * |
1536 | * @param User $user User object to check permissions provided |
1537 | * @return SpecialPage[] |
1538 | */ |
1539 | public function getUsablePages( User $user ): array { |
1540 | $pages = []; |
1541 | foreach ( $this->getPageList() as $name => $rec ) { |
1542 | $page = $this->getPage( $name ); |
1543 | if ( $page ) { // not null |
1544 | $page->setContext( RequestContext::getMain() ); |
1545 | if ( $page->isListed() |
1546 | && ( !$page->isRestricted() || $page->userCanExecute( $user ) ) |
1547 | ) { |
1548 | $pages[$name] = $page; |
1549 | } |
1550 | } |
1551 | } |
1552 | |
1553 | return $pages; |
1554 | } |
1555 | |
1556 | /** |
1557 | * Get listed special pages available to everyone by default. |
1558 | * |
1559 | * @return array<string,SpecialPage> |
1560 | */ |
1561 | public function getRegularPages(): array { |
1562 | $pages = []; |
1563 | foreach ( $this->getPageList() as $name => $rec ) { |
1564 | $page = $this->getPage( $name ); |
1565 | if ( $page && $page->isListed() && !$page->isRestricted() ) { |
1566 | $pages[$name] = $page; |
1567 | } |
1568 | } |
1569 | |
1570 | return $pages; |
1571 | } |
1572 | |
1573 | /** |
1574 | * Get listed special pages, including those that may require user rights. |
1575 | * |
1576 | * @since 1.42 |
1577 | * @return array<string,SpecialPage> |
1578 | */ |
1579 | public function getListedPages(): array { |
1580 | $pages = []; |
1581 | foreach ( $this->getPageList() as $name => $rec ) { |
1582 | $page = $this->getPage( $name ); |
1583 | if ( $page && $page->isListed() ) { |
1584 | $pages[$name] = $page; |
1585 | } |
1586 | } |
1587 | return $pages; |
1588 | } |
1589 | |
1590 | /** |
1591 | * Execute a special page path. |
1592 | * The path may contain parameters, e.g. Special:Name/Params |
1593 | * Extracts the special page name and call the execute method, passing the parameters |
1594 | * |
1595 | * Returns a title object if the page is redirected, false if there was no such special |
1596 | * page, and true if it was successful. |
1597 | * |
1598 | * @param PageReference|string $path |
1599 | * @param IContextSource $context |
1600 | * @param bool $including Bool output is being captured for use in {{special:whatever}} |
1601 | * @param LinkRenderer|null $linkRenderer (since 1.28) |
1602 | * |
1603 | * @return bool|Title |
1604 | */ |
1605 | public function executePath( $path, IContextSource $context, $including = false, |
1606 | LinkRenderer $linkRenderer = null |
1607 | ) { |
1608 | if ( $path instanceof PageReference ) { |
1609 | $path = $path->getDBkey(); |
1610 | } |
1611 | |
1612 | $bits = explode( '/', $path, 2 ); |
1613 | $name = $bits[0]; |
1614 | $par = $bits[1] ?? null; // T4087 |
1615 | |
1616 | $page = $this->getPage( $name ); |
1617 | if ( !$page ) { |
1618 | // Emulate SpecialPage::setHeaders() |
1619 | $context->getOutput()->setArticleRelated( false ); |
1620 | $context->getOutput()->setRobotPolicy( 'noindex,nofollow' ); |
1621 | |
1622 | if ( $context->getConfig()->get( MainConfigNames::Send404Code ) ) { |
1623 | $context->getOutput()->setStatusCode( 404 ); |
1624 | } |
1625 | |
1626 | $context->getOutput()->showErrorPage( 'nosuchspecialpage', 'nospecialpagetext' ); |
1627 | |
1628 | return false; |
1629 | } |
1630 | |
1631 | if ( !$including ) { |
1632 | ProfilingContext::singleton()->init( MW_ENTRY_POINT, $page->getName() ); |
1633 | // Narrow DB query expectations for this HTTP request |
1634 | $trxLimits = $context->getConfig()->get( MainConfigNames::TrxProfilerLimits ); |
1635 | $trxProfiler = Profiler::instance()->getTransactionProfiler(); |
1636 | if ( $context->getRequest()->wasPosted() && !$page->doesWrites() ) { |
1637 | $trxProfiler->setExpectations( $trxLimits['POST-nonwrite'], __METHOD__ ); |
1638 | } |
1639 | } |
1640 | |
1641 | // Page exists, set the context |
1642 | $page->setContext( $context ); |
1643 | |
1644 | if ( !$including ) { |
1645 | // Redirect to canonical alias for GET commands |
1646 | // Not for POST, we'd lose the post data, so it's best to just distribute |
1647 | // the request. Such POST requests are possible for old extensions that |
1648 | // generate self-links without being aware that their default name has |
1649 | // changed. |
1650 | if ( $name != $page->getLocalName() && !$context->getRequest()->wasPosted() ) { |
1651 | $query = $context->getRequest()->getQueryValues(); |
1652 | unset( $query['title'] ); |
1653 | $title = $page->getPageTitle( $par ?? false ); |
1654 | $url = $title->getFullURL( $query ); |
1655 | $context->getOutput()->redirect( $url ); |
1656 | |
1657 | return $title; |
1658 | } |
1659 | |
1660 | // @phan-suppress-next-line PhanUndeclaredMethod |
1661 | $context->setTitle( $page->getPageTitle( $par ?? false ) ); |
1662 | } elseif ( !$page->isIncludable() ) { |
1663 | return false; |
1664 | } |
1665 | |
1666 | $page->including( $including ); |
1667 | if ( $linkRenderer ) { |
1668 | $page->setLinkRenderer( $linkRenderer ); |
1669 | } |
1670 | |
1671 | // Execute special page |
1672 | $page->run( $par ); |
1673 | |
1674 | return true; |
1675 | } |
1676 | |
1677 | /** |
1678 | * Just like executePath() but will override global variables and execute |
1679 | * the page in "inclusion" mode. Returns true if the execution was |
1680 | * successful or false if there was no such special page, or a title object |
1681 | * if it was a redirect. |
1682 | * |
1683 | * Also saves the current $wgTitle, $wgOut, $wgRequest, $wgUser and $wgLang |
1684 | * variables so that the special page will get the context it'd expect on a |
1685 | * normal request, and then restores them to their previous values after. |
1686 | * |
1687 | * @param PageReference $page |
1688 | * @param IContextSource $context |
1689 | * @param LinkRenderer|null $linkRenderer (since 1.28) |
1690 | * @return bool|Title |
1691 | */ |
1692 | public function capturePath( |
1693 | PageReference $page, IContextSource $context, LinkRenderer $linkRenderer = null |
1694 | ) { |
1695 | // phpcs:ignore MediaWiki.Usage.DeprecatedGlobalVariables.Deprecated$wgUser |
1696 | global $wgTitle, $wgOut, $wgRequest, $wgUser, $wgLang; |
1697 | $main = RequestContext::getMain(); |
1698 | |
1699 | // Save current globals and main context |
1700 | $glob = [ |
1701 | 'title' => $wgTitle, |
1702 | 'output' => $wgOut, |
1703 | 'request' => $wgRequest, |
1704 | 'user' => $wgUser, |
1705 | 'language' => $wgLang, |
1706 | ]; |
1707 | $ctx = [ |
1708 | 'title' => $main->getTitle(), |
1709 | 'output' => $main->getOutput(), |
1710 | 'request' => $main->getRequest(), |
1711 | 'user' => $main->getUser(), |
1712 | 'language' => $main->getLanguage(), |
1713 | ]; |
1714 | if ( $main->canUseWikiPage() ) { |
1715 | $ctx['wikipage'] = $main->getWikiPage(); |
1716 | } |
1717 | |
1718 | // just needed for $wgTitle and RequestContext::setTitle |
1719 | $title = $this->titleFactory->castFromPageReference( $page ); |
1720 | |
1721 | // Override |
1722 | $wgTitle = $title; |
1723 | $wgOut = $context->getOutput(); |
1724 | $wgRequest = $context->getRequest(); |
1725 | $wgUser = $context->getUser(); |
1726 | $wgLang = $context->getLanguage(); |
1727 | // FIXME: Once reasonably certain that no SpecialPage subclasses |
1728 | // rely on direct RequestContext::getMain instead of their local |
1729 | // context getters, these can be removed (T323184) |
1730 | // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged |
1731 | @$main->setTitle( $title ); |
1732 | $main->setOutput( $context->getOutput() ); |
1733 | $main->setRequest( $context->getRequest() ); |
1734 | $main->setUser( $context->getUser() ); |
1735 | $main->setLanguage( $context->getLanguage() ); |
1736 | |
1737 | try { |
1738 | // The useful part |
1739 | return $this->executePath( $page, $context, true, $linkRenderer ); |
1740 | } finally { |
1741 | // Restore old globals and context |
1742 | $wgTitle = $glob['title']; |
1743 | $wgOut = $glob['output']; |
1744 | $wgRequest = $glob['request']; |
1745 | $wgUser = $glob['user']; |
1746 | $wgLang = $glob['language']; |
1747 | // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged |
1748 | @$main->setTitle( $ctx['title'] ); |
1749 | $main->setOutput( $ctx['output'] ); |
1750 | $main->setRequest( $ctx['request'] ); |
1751 | $main->setUser( $ctx['user'] ); |
1752 | $main->setLanguage( $ctx['language'] ); |
1753 | if ( isset( $ctx['wikipage'] ) ) { |
1754 | $main->setWikiPage( $ctx['wikipage'] ); |
1755 | } |
1756 | } |
1757 | } |
1758 | |
1759 | /** |
1760 | * Get the local name for a specified canonical name |
1761 | * |
1762 | * @param string $name |
1763 | * @param string|false|null $subpage |
1764 | * @return string |
1765 | */ |
1766 | public function getLocalNameFor( $name, $subpage = false ) { |
1767 | $aliases = $this->contLang->getSpecialPageAliases(); |
1768 | $aliasList = $this->getAliasList(); |
1769 | |
1770 | // Find the first alias that maps back to $name |
1771 | if ( isset( $aliases[$name] ) ) { |
1772 | $found = false; |
1773 | foreach ( $aliases[$name] as $alias ) { |
1774 | $caseFoldedAlias = $this->contLang->caseFold( $alias ); |
1775 | $caseFoldedAlias = str_replace( ' ', '_', $caseFoldedAlias ); |
1776 | if ( isset( $aliasList[$caseFoldedAlias] ) && |
1777 | $aliasList[$caseFoldedAlias] === $name |
1778 | ) { |
1779 | $name = $alias; |
1780 | $found = true; |
1781 | break; |
1782 | } |
1783 | } |
1784 | if ( !$found ) { |
1785 | wfWarn( "Did not find a usable alias for special page '$name'. " . |
1786 | "It seems all defined aliases conflict?" ); |
1787 | } |
1788 | } else { |
1789 | // Check if someone misspelled the correct casing |
1790 | if ( is_array( $aliases ) ) { |
1791 | foreach ( $aliases as $n => $values ) { |
1792 | if ( strcasecmp( $name, $n ) === 0 ) { |
1793 | wfWarn( "Found alias defined for $n when searching for " . |
1794 | "special page aliases for $name. Case mismatch?" ); |
1795 | return $this->getLocalNameFor( $n, $subpage ); |
1796 | } |
1797 | } |
1798 | } |
1799 | |
1800 | wfWarn( "Did not find alias for special page '$name'. " . |
1801 | "Perhaps no aliases are defined for it?" ); |
1802 | } |
1803 | |
1804 | if ( $subpage !== false && $subpage !== null ) { |
1805 | // Make sure it's in dbkey form |
1806 | $subpage = str_replace( ' ', '_', $subpage ); |
1807 | $name = "$name/$subpage"; |
1808 | } |
1809 | |
1810 | return $this->contLang->ucfirst( $name ); |
1811 | } |
1812 | |
1813 | /** |
1814 | * Get a title for a given alias |
1815 | * |
1816 | * @param string $alias |
1817 | * @return Title|null Title or null if there is no such alias |
1818 | */ |
1819 | public function getTitleForAlias( $alias ) { |
1820 | [ $name, $subpage ] = $this->resolveAlias( $alias ); |
1821 | if ( $name != null ) { |
1822 | return SpecialPage::getTitleFor( $name, $subpage ); |
1823 | } |
1824 | |
1825 | return null; |
1826 | } |
1827 | } |