Localisation updates from https://translatewiki.net.
[mediawiki.git] / includes / specialpage / SpecialPageFactory.php
blobe91d174049e9a2fee1d98372ed3e1c2aaeaa45a7
1 <?php
2 /**
3 * Factory for handling the special page list and generating SpecialPage objects.
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.
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.
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
20 * @file
21 * @ingroup SpecialPage
22 * @defgroup SpecialPage SpecialPage
25 namespace MediaWiki\SpecialPage;
27 use MediaWiki\Config\ServiceOptions;
28 use MediaWiki\Context\IContextSource;
29 use MediaWiki\Context\RequestContext;
30 use MediaWiki\HookContainer\HookContainer;
31 use MediaWiki\HookContainer\HookRunner;
32 use MediaWiki\Language\Language;
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\Redirects\SpecialTalkPage;
46 use MediaWiki\Specials\SpecialActiveUsers;
47 use MediaWiki\Specials\SpecialAllMessages;
48 use MediaWiki\Specials\SpecialAllPages;
49 use MediaWiki\Specials\SpecialAncientPages;
50 use MediaWiki\Specials\SpecialApiHelp;
51 use MediaWiki\Specials\SpecialApiSandbox;
52 use MediaWiki\Specials\SpecialAuthenticationPopupSuccess;
53 use MediaWiki\Specials\SpecialAutoblockList;
54 use MediaWiki\Specials\SpecialBlankpage;
55 use MediaWiki\Specials\SpecialBlock;
56 use MediaWiki\Specials\SpecialBlockList;
57 use MediaWiki\Specials\SpecialBookSources;
58 use MediaWiki\Specials\SpecialBotPasswords;
59 use MediaWiki\Specials\SpecialBrokenRedirects;
60 use MediaWiki\Specials\SpecialCategories;
61 use MediaWiki\Specials\SpecialChangeContentModel;
62 use MediaWiki\Specials\SpecialChangeCredentials;
63 use MediaWiki\Specials\SpecialChangeEmail;
64 use MediaWiki\Specials\SpecialChangePassword;
65 use MediaWiki\Specials\SpecialComparePages;
66 use MediaWiki\Specials\SpecialConfirmEmail;
67 use MediaWiki\Specials\SpecialContribute;
68 use MediaWiki\Specials\SpecialContributions;
69 use MediaWiki\Specials\SpecialCreateAccount;
70 use MediaWiki\Specials\SpecialDeadendPages;
71 use MediaWiki\Specials\SpecialDeletedContributions;
72 use MediaWiki\Specials\SpecialDeletePage;
73 use MediaWiki\Specials\SpecialDiff;
74 use MediaWiki\Specials\SpecialDoubleRedirects;
75 use MediaWiki\Specials\SpecialEditPage;
76 use MediaWiki\Specials\SpecialEditRecovery;
77 use MediaWiki\Specials\SpecialEditTags;
78 use MediaWiki\Specials\SpecialEditWatchlist;
79 use MediaWiki\Specials\SpecialEmailInvalidate;
80 use MediaWiki\Specials\SpecialEmailUser;
81 use MediaWiki\Specials\SpecialExpandTemplates;
82 use MediaWiki\Specials\SpecialExport;
83 use MediaWiki\Specials\SpecialFewestRevisions;
84 use MediaWiki\Specials\SpecialFileDuplicateSearch;
85 use MediaWiki\Specials\SpecialFilepath;
86 use MediaWiki\Specials\SpecialGoToInterwiki;
87 use MediaWiki\Specials\SpecialImport;
88 use MediaWiki\Specials\SpecialInterwiki;
89 use MediaWiki\Specials\SpecialJavaScriptTest;
90 use MediaWiki\Specials\SpecialLinkAccounts;
91 use MediaWiki\Specials\SpecialLinkSearch;
92 use MediaWiki\Specials\SpecialListDuplicatedFiles;
93 use MediaWiki\Specials\SpecialListFiles;
94 use MediaWiki\Specials\SpecialListGrants;
95 use MediaWiki\Specials\SpecialListGroupRights;
96 use MediaWiki\Specials\SpecialListRedirects;
97 use MediaWiki\Specials\SpecialListUsers;
98 use MediaWiki\Specials\SpecialLockdb;
99 use MediaWiki\Specials\SpecialLog;
100 use MediaWiki\Specials\SpecialLonelyPages;
101 use MediaWiki\Specials\SpecialLongPages;
102 use MediaWiki\Specials\SpecialMediaStatistics;
103 use MediaWiki\Specials\SpecialMergeHistory;
104 use MediaWiki\Specials\SpecialMIMESearch;
105 use MediaWiki\Specials\SpecialMostCategories;
106 use MediaWiki\Specials\SpecialMostImages;
107 use MediaWiki\Specials\SpecialMostInterwikis;
108 use MediaWiki\Specials\SpecialMostLinked;
109 use MediaWiki\Specials\SpecialMostLinkedCategories;
110 use MediaWiki\Specials\SpecialMostLinkedTemplates;
111 use MediaWiki\Specials\SpecialMostRevisions;
112 use MediaWiki\Specials\SpecialMovePage;
113 use MediaWiki\Specials\SpecialMute;
114 use MediaWiki\Specials\SpecialMyLanguage;
115 use MediaWiki\Specials\SpecialNamespaceInfo;
116 use MediaWiki\Specials\SpecialNewFiles;
117 use MediaWiki\Specials\SpecialNewPages;
118 use MediaWiki\Specials\SpecialNewSection;
119 use MediaWiki\Specials\SpecialPageData;
120 use MediaWiki\Specials\SpecialPageHistory;
121 use MediaWiki\Specials\SpecialPageInfo;
122 use MediaWiki\Specials\SpecialPageLanguage;
123 use MediaWiki\Specials\SpecialPagesWithProp;
124 use MediaWiki\Specials\SpecialPasswordPolicies;
125 use MediaWiki\Specials\SpecialPasswordReset;
126 use MediaWiki\Specials\SpecialPermanentLink;
127 use MediaWiki\Specials\SpecialPreferences;
128 use MediaWiki\Specials\SpecialPrefixIndex;
129 use MediaWiki\Specials\SpecialProtectedPages;
130 use MediaWiki\Specials\SpecialProtectedTitles;
131 use MediaWiki\Specials\SpecialProtectPage;
132 use MediaWiki\Specials\SpecialPurge;
133 use MediaWiki\Specials\SpecialRandomInCategory;
134 use MediaWiki\Specials\SpecialRandomPage;
135 use MediaWiki\Specials\SpecialRandomRedirect;
136 use MediaWiki\Specials\SpecialRandomRootPage;
137 use MediaWiki\Specials\SpecialRecentChanges;
138 use MediaWiki\Specials\SpecialRecentChangesLinked;
139 use MediaWiki\Specials\SpecialRedirect;
140 use MediaWiki\Specials\SpecialRemoveCredentials;
141 use MediaWiki\Specials\SpecialRenameUser;
142 use MediaWiki\Specials\SpecialResetTokens;
143 use MediaWiki\Specials\SpecialRestSandbox;
144 use MediaWiki\Specials\SpecialRevisionDelete;
145 use MediaWiki\Specials\SpecialRunJobs;
146 use MediaWiki\Specials\SpecialSearch;
147 use MediaWiki\Specials\SpecialShortPages;
148 use MediaWiki\Specials\SpecialSpecialPages;
149 use MediaWiki\Specials\SpecialStatistics;
150 use MediaWiki\Specials\SpecialTags;
151 use MediaWiki\Specials\SpecialTrackingCategories;
152 use MediaWiki\Specials\SpecialUnblock;
153 use MediaWiki\Specials\SpecialUncategorizedCategories;
154 use MediaWiki\Specials\SpecialUncategorizedImages;
155 use MediaWiki\Specials\SpecialUncategorizedPages;
156 use MediaWiki\Specials\SpecialUncategorizedTemplates;
157 use MediaWiki\Specials\SpecialUndelete;
158 use MediaWiki\Specials\SpecialUnlinkAccounts;
159 use MediaWiki\Specials\SpecialUnlockdb;
160 use MediaWiki\Specials\SpecialUnusedCategories;
161 use MediaWiki\Specials\SpecialUnusedImages;
162 use MediaWiki\Specials\SpecialUnusedTemplates;
163 use MediaWiki\Specials\SpecialUnwatchedPages;
164 use MediaWiki\Specials\SpecialUpload;
165 use MediaWiki\Specials\SpecialUploadStash;
166 use MediaWiki\Specials\SpecialUserLogin;
167 use MediaWiki\Specials\SpecialUserLogout;
168 use MediaWiki\Specials\SpecialUserRights;
169 use MediaWiki\Specials\SpecialVersion;
170 use MediaWiki\Specials\SpecialWantedCategories;
171 use MediaWiki\Specials\SpecialWantedFiles;
172 use MediaWiki\Specials\SpecialWantedPages;
173 use MediaWiki\Specials\SpecialWantedTemplates;
174 use MediaWiki\Specials\SpecialWatchlist;
175 use MediaWiki\Specials\SpecialWhatLinksHere;
176 use MediaWiki\Specials\SpecialWithoutInterwiki;
177 use MediaWiki\Title\Title;
178 use MediaWiki\Title\TitleFactory;
179 use MediaWiki\User\User;
180 use Profiler;
181 use Wikimedia\DebugInfo\DebugInfoTrait;
182 use Wikimedia\ObjectFactory\ObjectFactory;
185 * Factory for handling the special page list and generating SpecialPage objects.
187 * To add a special page in an extension, add to $wgSpecialPages either
188 * an object instance or an array containing the name and constructor
189 * parameters. The latter is preferred for performance reasons.
191 * The object instantiated must be either an instance of SpecialPage or a
192 * sub-class thereof. It must have an execute() method, which sends the HTML
193 * for the special page to $wgOut. The parent class has an execute() method
194 * which distributes the call to the historical global functions. Additionally,
195 * execute() also checks if the user has the necessary access privileges
196 * and bails out if not.
198 * To add a core special page, use the similar static list in
199 * SpecialPageFactory::$list. To remove a core static special page at runtime, use
200 * a SpecialPage_initList hook.
202 * @ingroup SpecialPage
203 * @since 1.17
205 class SpecialPageFactory {
206 use DebugInfoTrait;
209 * List of special page names to the subclass of SpecialPage which handles them.
211 private const CORE_LIST = [
212 // Maintenance Reports
213 'BrokenRedirects' => [
214 'class' => SpecialBrokenRedirects::class,
215 'services' => [
216 'ContentHandlerFactory',
217 'ConnectionProvider',
218 'LinkBatchFactory',
221 'Deadendpages' => [
222 'class' => SpecialDeadendPages::class,
223 'services' => [
224 'NamespaceInfo',
225 'ConnectionProvider',
226 'LinkBatchFactory',
227 'LanguageConverterFactory',
230 'DoubleRedirects' => [
231 'class' => SpecialDoubleRedirects::class,
232 'services' => [
233 'ContentHandlerFactory',
234 'LinkBatchFactory',
235 'ConnectionProvider',
238 'Longpages' => [
239 'class' => SpecialLongPages::class,
240 'services' => [
241 // Same as for Shortpages
242 'NamespaceInfo',
243 'ConnectionProvider',
244 'LinkBatchFactory',
247 'Ancientpages' => [
248 'class' => SpecialAncientPages::class,
249 'services' => [
250 'NamespaceInfo',
251 'ConnectionProvider',
252 'LinkBatchFactory',
253 'LanguageConverterFactory',
256 'Lonelypages' => [
257 'class' => SpecialLonelyPages::class,
258 'services' => [
259 'NamespaceInfo',
260 'ConnectionProvider',
261 'LinkBatchFactory',
262 'LanguageConverterFactory',
263 'LinksMigration',
266 'Fewestrevisions' => [
267 'class' => SpecialFewestRevisions::class,
268 'services' => [
269 // Same as for Mostrevisions
270 'NamespaceInfo',
271 'ConnectionProvider',
272 'LinkBatchFactory',
273 'LanguageConverterFactory',
276 'Withoutinterwiki' => [
277 'class' => SpecialWithoutInterwiki::class,
278 'services' => [
279 'NamespaceInfo',
280 'ConnectionProvider',
281 'LinkBatchFactory',
282 'LanguageConverterFactory',
285 'Protectedpages' => [
286 'class' => SpecialProtectedPages::class,
287 'services' => [
288 'LinkBatchFactory',
289 'ConnectionProvider',
290 'CommentStore',
291 'RowCommentFormatter',
292 'RestrictionStore',
295 'Protectedtitles' => [
296 'class' => SpecialProtectedTitles::class,
297 'services' => [
298 'LinkBatchFactory',
299 'ConnectionProvider',
302 'Shortpages' => [
303 'class' => SpecialShortPages::class,
304 'services' => [
305 // Same as for Longpages
306 'NamespaceInfo',
307 'ConnectionProvider',
308 'LinkBatchFactory',
311 'Uncategorizedcategories' => [
312 'class' => SpecialUncategorizedCategories::class,
313 'services' => [
314 // Same as for SpecialUncategorizedPages and SpecialUncategorizedTemplates
315 'NamespaceInfo',
316 'ConnectionProvider',
317 'LinkBatchFactory',
318 'LanguageConverterFactory',
321 'Uncategorizedimages' => [
322 'class' => SpecialUncategorizedImages::class,
323 'services' => [
324 'ConnectionProvider',
327 'Uncategorizedpages' => [
328 'class' => SpecialUncategorizedPages::class,
329 'services' => [
330 // Same as for SpecialUncategorizedCategories and SpecialUncategorizedTemplates
331 'NamespaceInfo',
332 'ConnectionProvider',
333 'LinkBatchFactory',
334 'LanguageConverterFactory',
337 'Uncategorizedtemplates' => [
338 'class' => SpecialUncategorizedTemplates::class,
339 'services' => [
340 // Same as for SpecialUncategorizedCategories and SpecialUncategorizedPages
341 'NamespaceInfo',
342 'ConnectionProvider',
343 'LinkBatchFactory',
344 'LanguageConverterFactory',
347 'Unusedcategories' => [
348 'class' => SpecialUnusedCategories::class,
349 'services' => [
350 'ConnectionProvider',
351 'LinkBatchFactory',
354 'Unusedimages' => [
355 'class' => SpecialUnusedImages::class,
356 'services' => [
357 'ConnectionProvider',
360 'Unusedtemplates' => [
361 'class' => SpecialUnusedTemplates::class,
362 'services' => [
363 'ConnectionProvider',
364 'LinksMigration',
367 'Unwatchedpages' => [
368 'class' => SpecialUnwatchedPages::class,
369 'services' => [
370 'LinkBatchFactory',
371 'ConnectionProvider',
372 'LanguageConverterFactory',
375 'Wantedcategories' => [
376 'class' => SpecialWantedCategories::class,
377 'services' => [
378 'ConnectionProvider',
379 'LinkBatchFactory',
380 'LanguageConverterFactory',
383 'Wantedfiles' => [
384 'class' => SpecialWantedFiles::class,
385 'services' => [
386 'RepoGroup',
387 'ConnectionProvider',
388 'LinkBatchFactory',
391 'Wantedpages' => [
392 'class' => SpecialWantedPages::class,
393 'services' => [
394 'ConnectionProvider',
395 'LinkBatchFactory',
396 'LinksMigration',
399 'Wantedtemplates' => [
400 'class' => SpecialWantedTemplates::class,
401 'services' => [
402 'ConnectionProvider',
403 'LinkBatchFactory',
404 'LinksMigration',
408 // List of pages
409 'Allpages' => [
410 'class' => SpecialAllPages::class,
411 'services' => [
412 'ConnectionProvider',
413 'SearchEngineFactory',
414 'PageStore',
417 'Prefixindex' => [
418 'class' => SpecialPrefixIndex::class,
419 'services' => [
420 'ConnectionProvider',
421 'LinkCache',
424 'Categories' => [
425 'class' => SpecialCategories::class,
426 'services' => [
427 'LinkBatchFactory',
428 'ConnectionProvider',
431 'Listredirects' => [
432 'class' => SpecialListRedirects::class,
433 'services' => [
434 'LinkBatchFactory',
435 'ConnectionProvider',
436 'WikiPageFactory',
437 'RedirectLookup'
440 'PagesWithProp' => [
441 'class' => SpecialPagesWithProp::class,
442 'services' => [
443 'ConnectionProvider',
446 'TrackingCategories' => [
447 'class' => SpecialTrackingCategories::class,
448 'services' => [
449 'LinkBatchFactory',
450 'TrackingCategories',
454 // Authentication
455 'Userlogin' => [
456 'class' => SpecialUserLogin::class,
457 'services' => [
458 'AuthManager',
459 'UserIdentityUtils',
462 'Userlogout' => [
463 'class' => SpecialUserLogout::class,
464 'services' => [
465 'TempUserConfig',
468 'CreateAccount' => [
469 'class' => SpecialCreateAccount::class,
470 'services' => [
471 'AuthManager',
472 'FormatterFactory',
473 'UserIdentityUtils',
476 'LinkAccounts' => [
477 'class' => SpecialLinkAccounts::class,
478 'services' => [
479 'AuthManager',
482 'UnlinkAccounts' => [
483 'class' => SpecialUnlinkAccounts::class,
484 'services' => [
485 'AuthManager',
488 'ChangeCredentials' => [
489 'class' => SpecialChangeCredentials::class,
490 'services' => [
491 'AuthManager',
494 'RemoveCredentials' => [
495 'class' => SpecialRemoveCredentials::class,
496 'services' => [
497 'AuthManager',
500 'AuthenticationPopupSuccess' => [
501 'class' => SpecialAuthenticationPopupSuccess::class,
502 'services' => [
503 'SkinFactory',
507 // Users and rights
508 'Activeusers' => [
509 'class' => SpecialActiveUsers::class,
510 'services' => [
511 'LinkBatchFactory',
512 'ConnectionProvider',
513 'UserGroupManager',
514 'UserIdentityLookup',
515 'HideUserUtils',
518 'Block' => [
519 'class' => SpecialBlock::class,
520 'services' => [
521 'BlockUtils',
522 'BlockPermissionCheckerFactory',
523 'BlockUserFactory',
524 'DatabaseBlockStore',
525 'UserNameUtils',
526 'UserNamePrefixSearch',
527 'BlockActionInfo',
528 'TitleFormatter',
529 'NamespaceInfo'
532 'Unblock' => [
533 'class' => SpecialUnblock::class,
534 'services' => [
535 'UnblockUserFactory',
536 'BlockUtils',
537 'DatabaseBlockStore',
538 'UserNameUtils',
539 'UserNamePrefixSearch',
540 'WatchlistManager',
543 'BlockList' => [
544 'class' => SpecialBlockList::class,
545 'services' => [
546 'LinkBatchFactory',
547 'DatabaseBlockStore',
548 'BlockRestrictionStore',
549 'ConnectionProvider',
550 'CommentStore',
551 'BlockUtils',
552 'HideUserUtils',
553 'BlockActionInfo',
554 'RowCommentFormatter',
557 'AutoblockList' => [
558 'class' => SpecialAutoblockList::class,
559 'services' => [
560 'LinkBatchFactory',
561 'BlockRestrictionStore',
562 'ConnectionProvider',
563 'CommentStore',
564 'BlockUtils',
565 'HideUserUtils',
566 'BlockActionInfo',
567 'RowCommentFormatter',
570 'ChangePassword' => [
571 'class' => SpecialChangePassword::class,
573 'BotPasswords' => [
574 'class' => SpecialBotPasswords::class,
575 'services' => [
576 'PasswordFactory',
577 'AuthManager',
578 'CentralIdLookup',
579 'GrantsInfo',
580 'GrantsLocalization',
583 'PasswordReset' => [
584 'class' => SpecialPasswordReset::class,
585 'services' => [
586 'PasswordReset'
589 'DeletedContributions' => [
590 'class' => SpecialDeletedContributions::class,
591 'services' => [
592 'PermissionManager',
593 'ConnectionProvider',
594 'RevisionStore',
595 'NamespaceInfo',
596 'UserNameUtils',
597 'UserNamePrefixSearch',
598 'UserOptionsLookup',
599 'CommentFormatter',
600 'LinkBatchFactory',
601 'UserFactory',
602 'UserIdentityLookup',
603 'DatabaseBlockStore',
604 'TempUserConfig',
607 'Preferences' => [
608 'class' => SpecialPreferences::class,
609 'services' => [
610 'PreferencesFactory',
611 'UserOptionsManager',
614 'ResetTokens' => [
615 'class' => SpecialResetTokens::class,
617 'Contributions' => [
618 'class' => SpecialContributions::class,
619 'services' => [
620 'LinkBatchFactory',
621 'PermissionManager',
622 'ConnectionProvider',
623 'RevisionStore',
624 'NamespaceInfo',
625 'UserNameUtils',
626 'UserNamePrefixSearch',
627 'UserOptionsLookup',
628 'CommentFormatter',
629 'UserFactory',
630 'UserIdentityLookup',
631 'DatabaseBlockStore',
632 'TempUserConfig',
635 'Listgrouprights' => [
636 'class' => SpecialListGroupRights::class,
637 'services' => [
638 'NamespaceInfo',
639 'UserGroupManager',
640 'LanguageConverterFactory',
641 'GroupPermissionsLookup',
644 'Listgrants' => [
645 'class' => SpecialListGrants::class,
646 'services' => [
647 'GrantsLocalization',
650 'Listusers' => [
651 'class' => SpecialListUsers::class,
652 'services' => [
653 'LinkBatchFactory',
654 'ConnectionProvider',
655 'UserGroupManager',
656 'UserIdentityLookup',
657 'HideUserUtils',
660 'Listadmins' => [
661 'class' => SpecialListAdmins::class,
663 'Listbots' => [
664 'class' => SpecialListBots::class,
666 'Userrights' => [
667 'class' => SpecialUserRights::class,
668 'services' => [
669 'UserGroupManagerFactory',
670 'UserNameUtils',
671 'UserNamePrefixSearch',
672 'UserFactory',
673 'ActorStoreFactory',
674 'WatchlistManager',
675 'TempUserConfig',
678 'EditWatchlist' => [
679 'class' => SpecialEditWatchlist::class,
680 'services' => [
681 'WatchedItemStore',
682 'TitleParser',
683 'GenderCache',
684 'LinkBatchFactory',
685 'NamespaceInfo',
686 'WikiPageFactory',
687 'WatchlistManager',
690 'PasswordPolicies' => [
691 'class' => SpecialPasswordPolicies::class,
692 'services' => [
693 'UserGroupManager',
697 // Recent changes and logs
698 'Newimages' => [
699 'class' => SpecialNewFiles::class,
700 'services' => [
701 'MimeAnalyzer',
702 'GroupPermissionsLookup',
703 'ConnectionProvider',
704 'LinkBatchFactory',
707 'Log' => [
708 'class' => SpecialLog::class,
709 'services' => [
710 'LinkBatchFactory',
711 'ConnectionProvider',
712 'ActorNormalization',
713 'UserIdentityLookup',
714 'UserNameUtils',
715 'LogFormatterFactory',
718 'Watchlist' => [
719 'class' => SpecialWatchlist::class,
720 'services' => [
721 'WatchedItemStore',
722 'WatchlistManager',
723 'UserOptionsLookup',
724 'ChangeTagsStore',
725 'UserIdentityUtils',
726 'TempUserConfig',
729 'Newpages' => [
730 'class' => SpecialNewPages::class,
731 'services' => [
732 'LinkBatchFactory',
733 'ContentHandlerFactory',
734 'GroupPermissionsLookup',
735 'RevisionLookup',
736 'NamespaceInfo',
737 'UserOptionsLookup',
738 'RowCommentFormatter',
739 'ChangeTagsStore',
740 'TempUserConfig',
743 'Recentchanges' => [
744 'class' => SpecialRecentChanges::class,
745 'services' => [
746 'WatchedItemStore',
747 'MessageCache',
748 'UserOptionsLookup',
749 'ChangeTagsStore',
750 'UserIdentityUtils',
751 'TempUserConfig',
754 'Recentchangeslinked' => [
755 'class' => SpecialRecentChangesLinked::class,
756 'services' => [
757 'WatchedItemStore',
758 'MessageCache',
759 'UserOptionsLookup',
760 'SearchEngineFactory',
761 'ChangeTagsStore',
762 'UserIdentityUtils',
763 'TempUserConfig',
766 'Tags' => [
767 'class' => SpecialTags::class,
768 'services' => [
769 'ChangeTagsStore',
773 // Media reports and uploads
774 'Listfiles' => [
775 'class' => SpecialListFiles::class,
776 'services' => [
777 'RepoGroup',
778 'ConnectionProvider',
779 'CommentStore',
780 'UserNameUtils',
781 'UserNamePrefixSearch',
782 'CommentFormatter',
783 'LinkBatchFactory',
786 'Filepath' => [
787 'class' => SpecialFilepath::class,
788 'services' => [
789 'SearchEngineFactory',
792 'MediaStatistics' => [
793 'class' => SpecialMediaStatistics::class,
794 'services' => [
795 'MimeAnalyzer',
796 'ConnectionProvider',
797 'LinkBatchFactory',
800 'MIMEsearch' => [
801 'class' => SpecialMIMESearch::class,
802 'services' => [
803 'ConnectionProvider',
804 'LinkBatchFactory',
805 'LanguageConverterFactory',
808 'FileDuplicateSearch' => [
809 'class' => SpecialFileDuplicateSearch::class,
810 'services' => [
811 'LinkBatchFactory',
812 'RepoGroup',
813 'SearchEngineFactory',
814 'LanguageConverterFactory',
817 'Upload' => [
818 'class' => SpecialUpload::class,
819 'services' => [
820 'RepoGroup',
821 'UserOptionsLookup',
822 'NamespaceInfo',
825 'UploadStash' => [
826 'class' => SpecialUploadStash::class,
827 'services' => [
828 'RepoGroup',
829 'HttpRequestFactory',
830 'UrlUtils',
831 'ConnectionProvider',
834 'ListDuplicatedFiles' => [
835 'class' => SpecialListDuplicatedFiles::class,
836 'services' => [
837 'ConnectionProvider',
838 'LinkBatchFactory',
842 // Data and tools
843 'ApiSandbox' => [
844 'class' => SpecialApiSandbox::class,
846 'Interwiki' => [
847 'class' => SpecialInterwiki::class,
848 'services' => [
849 'ContentLanguage',
850 'InterwikiLookup',
851 'LanguageNameUtils',
852 'UrlUtils',
853 'ConnectionProvider',
856 'RestSandbox' => [
857 'class' => SpecialRestSandbox::class,
858 'services' => [
859 'UrlUtils',
862 'Statistics' => [
863 'class' => SpecialStatistics::class,
864 'services' => [
865 'UserGroupManager',
868 'Allmessages' => [
869 'class' => SpecialAllMessages::class,
870 'services' => [
871 'LanguageFactory',
872 'LanguageNameUtils',
873 'LocalisationCache',
874 'ConnectionProvider',
877 'Version' => [
878 'class' => SpecialVersion::class,
879 'services' => [
880 'ParserFactory',
881 'UrlUtils',
882 'ConnectionProvider',
885 'Lockdb' => [
886 'class' => SpecialLockdb::class,
888 'Unlockdb' => [
889 'class' => SpecialUnlockdb::class,
891 'NamespaceInfo' => [
892 'class' => SpecialNamespaceInfo::class,
893 'services' => [
894 'NamespaceInfo',
898 // Redirecting special pages
899 'LinkSearch' => [
900 'class' => SpecialLinkSearch::class,
901 'services' => [
902 'ConnectionProvider',
903 'LinkBatchFactory',
904 'UrlUtils',
907 'Randompage' => [
908 'class' => SpecialRandomPage::class,
909 'services' => [
910 'ConnectionProvider',
911 'NamespaceInfo',
914 'RandomInCategory' => [
915 'class' => SpecialRandomInCategory::class,
916 'services' => [
917 'ConnectionProvider',
920 'Randomredirect' => [
921 'class' => SpecialRandomRedirect::class,
922 'services' => [
923 'ConnectionProvider',
924 'NamespaceInfo',
927 'Randomrootpage' => [
928 'class' => SpecialRandomRootPage::class,
929 'services' => [
930 'ConnectionProvider',
931 'NamespaceInfo',
934 'GoToInterwiki' => [
935 'class' => SpecialGoToInterwiki::class,
938 // High use pages
939 'Mostlinkedcategories' => [
940 'class' => SpecialMostLinkedCategories::class,
941 'services' => [
942 'ConnectionProvider',
943 'LinkBatchFactory',
944 'LanguageConverterFactory',
947 'Mostimages' => [
948 'class' => SpecialMostImages::class,
949 'services' => [
950 'ConnectionProvider',
951 'LanguageConverterFactory',
954 'Mostinterwikis' => [
955 'class' => SpecialMostInterwikis::class,
956 'services' => [
957 'NamespaceInfo',
958 'ConnectionProvider',
959 'LinkBatchFactory',
962 'Mostlinked' => [
963 'class' => SpecialMostLinked::class,
964 'services' => [
965 'ConnectionProvider',
966 'LinkBatchFactory',
967 'LinksMigration',
970 'Mostlinkedtemplates' => [
971 'class' => SpecialMostLinkedTemplates::class,
972 'services' => [
973 'ConnectionProvider',
974 'LinkBatchFactory',
975 'LinksMigration',
978 'Mostcategories' => [
979 'class' => SpecialMostCategories::class,
980 'services' => [
981 'NamespaceInfo',
982 'ConnectionProvider',
983 'LinkBatchFactory',
986 'Mostrevisions' => [
987 'class' => SpecialMostRevisions::class,
988 'services' => [
989 // Same as for Fewestrevisions
990 'NamespaceInfo',
991 'ConnectionProvider',
992 'LinkBatchFactory',
993 'LanguageConverterFactory',
997 // Page tools
998 'ComparePages' => [
999 'class' => SpecialComparePages::class,
1000 'services' => [
1001 'RevisionLookup',
1002 'ContentHandlerFactory',
1005 'Export' => [
1006 'class' => SpecialExport::class,
1007 'services' => [
1008 'ConnectionProvider',
1009 'WikiExporterFactory',
1010 'TitleFormatter',
1011 'LinksMigration',
1014 'Import' => [
1015 'class' => SpecialImport::class,
1016 'services' => [
1017 'WikiImporterFactory',
1020 'Undelete' => [
1021 'class' => SpecialUndelete::class,
1022 'services' => [
1023 'PermissionManager',
1024 'RevisionStore',
1025 'RevisionRenderer',
1026 'ContentHandlerFactory',
1027 'ChangeTagDefStore',
1028 'LinkBatchFactory',
1029 'RepoGroup',
1030 'ConnectionProvider',
1031 'UserOptionsLookup',
1032 'WikiPageFactory',
1033 'SearchEngineFactory',
1034 'UndeletePageFactory',
1035 'ArchivedRevisionLookup',
1036 'CommentFormatter',
1037 'WatchlistManager',
1040 'Whatlinkshere' => [
1041 'class' => SpecialWhatLinksHere::class,
1042 'services' => [
1043 'ConnectionProvider',
1044 'LinkBatchFactory',
1045 'ContentHandlerFactory',
1046 'SearchEngineFactory',
1047 'NamespaceInfo',
1048 'TitleFactory',
1049 'LinksMigration',
1052 'MergeHistory' => [
1053 'class' => SpecialMergeHistory::class,
1054 'services' => [
1055 'MergeHistoryFactory',
1056 'LinkBatchFactory',
1057 'ConnectionProvider',
1058 'RevisionStore',
1059 'CommentFormatter',
1062 'ExpandTemplates' => [
1063 'class' => SpecialExpandTemplates::class,
1064 'services' => [
1065 'ParserFactory',
1066 'UserOptionsLookup',
1067 'Tidy',
1070 'ChangeContentModel' => [
1071 'class' => SpecialChangeContentModel::class,
1072 'services' => [
1073 'ContentHandlerFactory',
1074 'ContentModelChangeFactory',
1075 'SpamChecker',
1076 'RevisionLookup',
1077 'WikiPageFactory',
1078 'SearchEngineFactory',
1079 'CollationFactory',
1083 // Other
1084 'Booksources' => [
1085 'class' => SpecialBookSources::class,
1086 'services' => [
1087 'RevisionLookup',
1088 'TitleFactory',
1092 // Unlisted / redirects
1093 'ApiHelp' => [
1094 'class' => SpecialApiHelp::class,
1095 'services' => [
1096 'UrlUtils',
1099 'Blankpage' => [
1100 'class' => SpecialBlankpage::class,
1102 'DeletePage' => [
1103 'class' => SpecialDeletePage::class,
1104 'services' => [
1105 'SearchEngineFactory',
1108 'Diff' => [
1109 'class' => SpecialDiff::class,
1111 'EditPage' => [
1112 'class' => SpecialEditPage::class,
1113 'services' => [
1114 'SearchEngineFactory',
1117 'EditTags' => [
1118 'class' => SpecialEditTags::class,
1119 'services' => [
1120 'PermissionManager',
1121 'ChangeTagsStore',
1124 'Emailuser' => [
1125 'class' => SpecialEmailUser::class,
1126 'services' => [
1127 'UserNameUtils',
1128 'UserNamePrefixSearch',
1129 'UserOptionsLookup',
1130 'EmailUserFactory',
1131 'UserFactory',
1134 'Movepage' => [
1135 'class' => SpecialMovePage::class,
1136 'services' => [
1137 'MovePageFactory',
1138 'PermissionManager',
1139 'UserOptionsLookup',
1140 'ConnectionProvider',
1141 'ContentHandlerFactory',
1142 'NamespaceInfo',
1143 'LinkBatchFactory',
1144 'RepoGroup',
1145 'WikiPageFactory',
1146 'SearchEngineFactory',
1147 'WatchlistManager',
1148 'RestrictionStore',
1149 'TitleFactory',
1150 'DeletePageFactory',
1153 'Mycontributions' => [
1154 'class' => SpecialMycontributions::class,
1155 'services' => [
1156 'TempUserConfig',
1159 'MyLanguage' => [
1160 'class' => SpecialMyLanguage::class,
1161 'services' => [
1162 'LanguageNameUtils',
1163 'RedirectLookup'
1166 'Mylog' => [
1167 'class' => SpecialMylog::class,
1168 'services' => [
1169 'TempUserConfig',
1172 'Mypage' => [
1173 'class' => SpecialMypage::class,
1174 'services' => [
1175 'TempUserConfig',
1178 'Mytalk' => [
1179 'class' => SpecialMytalk::class,
1180 'services' => [
1181 'TempUserConfig',
1184 'PageHistory' => [
1185 'class' => SpecialPageHistory::class,
1186 'services' => [
1187 'SearchEngineFactory',
1190 'PageInfo' => [
1191 'class' => SpecialPageInfo::class,
1192 'services' => [
1193 'SearchEngineFactory',
1196 'ProtectPage' => [
1197 'class' => SpecialProtectPage::class,
1198 'services' => [
1199 'SearchEngineFactory',
1202 'Purge' => [
1203 'class' => SpecialPurge::class,
1204 'services' => [
1205 'SearchEngineFactory',
1208 'Myuploads' => [
1209 'class' => SpecialMyuploads::class,
1210 'services' => [
1211 'TempUserConfig',
1214 'AllMyUploads' => [
1215 'class' => SpecialAllMyUploads::class,
1217 'NewSection' => [
1218 'class' => SpecialNewSection::class,
1219 'services' => [
1220 'SearchEngineFactory',
1223 'PermanentLink' => [
1224 'class' => SpecialPermanentLink::class,
1226 'Redirect' => [
1227 'class' => SpecialRedirect::class,
1228 'services' => [
1229 'RepoGroup',
1230 'UserFactory',
1233 'Renameuser' => [
1234 'class' => SpecialRenameUser::class,
1235 'services' => [
1236 'ConnectionProvider',
1237 'MovePageFactory',
1238 'PermissionManager',
1239 'TitleFactory',
1240 'UserFactory',
1241 'UserNamePrefixSearch',
1242 'UserNameUtils',
1245 'Revisiondelete' => [
1246 'class' => SpecialRevisionDelete::class,
1247 'services' => [
1248 'PermissionManager',
1249 'RepoGroup',
1252 'RunJobs' => [
1253 'class' => SpecialRunJobs::class,
1254 'services' => [
1255 'JobRunner',
1256 'ReadOnlyMode',
1259 'Specialpages' => [
1260 'class' => SpecialSpecialPages::class,
1262 'PageData' => [
1263 'class' => SpecialPageData::class,
1265 'Contribute' => [
1266 'class' => SpecialContribute::class,
1268 'TalkPage' => [
1269 'class' => SpecialTalkPage::class,
1270 'services' => [
1271 'MainConfig',
1272 'TitleParser',
1277 /** @var array Special page name => class name */
1278 private $list;
1280 /** @var array */
1281 private $aliases;
1283 /** @var ServiceOptions */
1284 private $options;
1286 /** @var Language */
1287 private $contLang;
1290 * @var ObjectFactory
1291 * @noVarDump
1293 private $objectFactory;
1296 * @var HookContainer
1297 * @noVarDump
1299 private $hookContainer;
1302 * @var HookRunner
1303 * @noVarDump
1305 private $hookRunner;
1308 * @internal For use by ServiceWiring
1310 public const CONSTRUCTOR_OPTIONS = [
1311 MainConfigNames::DisableInternalSearch,
1312 MainConfigNames::EmailAuthentication,
1313 MainConfigNames::EnableEmail,
1314 MainConfigNames::EnableJavaScriptTest,
1315 MainConfigNames::EnableSpecialMute,
1316 MainConfigNames::EnableEditRecovery,
1317 MainConfigNames::PageLanguageUseDB,
1318 MainConfigNames::SpecialPages,
1322 * @var TitleFactory
1324 private $titleFactory;
1327 * @param ServiceOptions $options
1328 * @param Language $contLang
1329 * @param ObjectFactory $objectFactory
1330 * @param TitleFactory $titleFactory
1331 * @param HookContainer $hookContainer
1333 public function __construct(
1334 ServiceOptions $options,
1335 Language $contLang,
1336 ObjectFactory $objectFactory,
1337 TitleFactory $titleFactory,
1338 HookContainer $hookContainer
1340 $options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS );
1341 $this->options = $options;
1342 $this->contLang = $contLang;
1343 $this->objectFactory = $objectFactory;
1344 $this->titleFactory = $titleFactory;
1345 $this->hookContainer = $hookContainer;
1346 $this->hookRunner = new HookRunner( $hookContainer );
1350 * Returns a list of canonical special page names.
1351 * May be used to iterate over all registered special pages.
1353 * @return string[]
1355 public function getNames(): array {
1356 return array_keys( $this->getPageList() );
1360 * Get the special page list as an array
1362 * @return array
1364 private function getPageList(): array {
1365 if ( !is_array( $this->list ) ) {
1366 $this->list = self::CORE_LIST;
1368 if ( !$this->options->get( MainConfigNames::DisableInternalSearch ) ) {
1369 $this->list['Search'] = [
1370 'class' => SpecialSearch::class,
1371 'services' => [
1372 'SearchEngineConfig',
1373 'SearchEngineFactory',
1374 'NamespaceInfo',
1375 'ContentHandlerFactory',
1376 'InterwikiLookup',
1377 'ReadOnlyMode',
1378 'UserOptionsManager',
1379 'LanguageConverterFactory',
1380 'RepoGroup',
1381 'SearchResultThumbnailProvider',
1382 'TitleMatcher',
1387 if ( $this->options->get( MainConfigNames::EmailAuthentication ) ) {
1388 $this->list['Confirmemail'] = [
1389 'class' => SpecialConfirmEmail::class,
1390 'services' => [
1391 'UserFactory',
1394 $this->list['Invalidateemail'] = [
1395 'class' => SpecialEmailInvalidate::class,
1396 'services' => [
1397 'UserFactory',
1402 if ( $this->options->get( MainConfigNames::EnableEmail ) ) {
1403 $this->list['ChangeEmail'] = [
1404 'class' => SpecialChangeEmail::class,
1405 'services' => [
1406 'AuthManager',
1411 if ( $this->options->get( MainConfigNames::EnableJavaScriptTest ) ) {
1412 $this->list['JavaScriptTest'] = [
1413 'class' => SpecialJavaScriptTest::class
1417 if ( $this->options->get( MainConfigNames::EnableSpecialMute ) ) {
1418 $this->list['Mute'] = [
1419 'class' => SpecialMute::class,
1420 'services' => [
1421 'CentralIdLookup',
1422 'UserOptionsManager',
1423 'UserIdentityLookup',
1424 'UserIdentityUtils',
1429 if ( $this->options->get( MainConfigNames::PageLanguageUseDB ) ) {
1430 $this->list['PageLanguage'] = [
1431 'class' => SpecialPageLanguage::class,
1432 'services' => [
1433 'ContentHandlerFactory',
1434 'LanguageNameUtils',
1435 'ConnectionProvider',
1436 'SearchEngineFactory',
1441 if ( $this->options->get( MainConfigNames::EnableEditRecovery ) ) {
1442 $this->list['EditRecovery'] = [
1443 'class' => SpecialEditRecovery::class,
1444 'services' => [
1445 'UserOptionsLookup',
1450 // Add extension special pages
1451 $this->list = array_merge( $this->list,
1452 $this->options->get( MainConfigNames::SpecialPages ) );
1454 // This hook can be used to disable unwanted core special pages
1455 // or conditionally register special pages.
1456 $this->hookRunner->onSpecialPage_initList( $this->list );
1459 return $this->list;
1463 * Initialise and return the list of special page aliases. Returns an array where
1464 * the key is an alias, and the value is the canonical name of the special page.
1465 * All registered special pages are guaranteed to map to themselves.
1466 * @return array
1468 private function getAliasList(): array {
1469 if ( $this->aliases === null ) {
1470 $aliases = $this->contLang->getSpecialPageAliases();
1471 $pageList = $this->getPageList();
1473 $this->aliases = [];
1474 $keepAlias = [];
1476 // Force every canonical name to be an alias for itself.
1477 foreach ( $pageList as $name => $stuff ) {
1478 $caseFoldedAlias = $this->contLang->caseFold( $name );
1479 $this->aliases[$caseFoldedAlias] = $name;
1480 $keepAlias[$caseFoldedAlias] = 'canonical';
1483 // Check for $aliases being an array since Language::getSpecialPageAliases can return null
1484 if ( is_array( $aliases ) ) {
1485 foreach ( $aliases as $realName => $aliasList ) {
1486 $first = true;
1487 foreach ( $aliasList as $alias ) {
1488 $caseFoldedAlias = $this->contLang->caseFold( $alias );
1490 if ( isset( $this->aliases[$caseFoldedAlias] ) &&
1491 $realName === $this->aliases[$caseFoldedAlias]
1493 $first = false;
1494 // Ignore same-realName conflicts
1495 continue;
1498 if ( !isset( $keepAlias[$caseFoldedAlias] ) ) {
1499 $this->aliases[$caseFoldedAlias] = $realName;
1500 if ( $first ) {
1501 $keepAlias[$caseFoldedAlias] = 'first';
1503 } elseif ( $first ) {
1504 wfWarn( "First alias '$alias' for $realName conflicts with " .
1505 "{$keepAlias[$caseFoldedAlias]} alias for " .
1506 $this->aliases[$caseFoldedAlias]
1509 $first = false;
1515 return $this->aliases;
1519 * Given a special page name with a possible subpage, return an array
1520 * where the first element is the special page name and the second is the
1521 * subpage.
1523 * @param string $alias
1524 * @return array [ String, String|null ], or [ null, null ] if the page is invalid
1526 public function resolveAlias( $alias ) {
1527 $bits = explode( '/', $alias, 2 );
1529 $caseFoldedAlias = $this->contLang->caseFold( $bits[0] );
1530 $caseFoldedAlias = str_replace( ' ', '_', $caseFoldedAlias );
1531 $aliases = $this->getAliasList();
1532 if ( !isset( $aliases[$caseFoldedAlias] ) ) {
1533 return [ null, null ];
1535 $name = $aliases[$caseFoldedAlias];
1536 $par = $bits[1] ?? null; // T4087
1538 return [ $name, $par ];
1542 * Check if a given name exist as a special page or as a special page alias
1544 * @param string $name Name of a special page
1545 * @return bool True if a special page exists with this name
1547 public function exists( $name ) {
1548 [ $title, /*...*/ ] = $this->resolveAlias( $name );
1550 $specialPageList = $this->getPageList();
1551 return isset( $specialPageList[$title] );
1555 * Find the object with a given name and return it (or NULL)
1557 * @param string $name Special page name, may be localised and/or an alias
1558 * @return SpecialPage|null SpecialPage object or null if the page doesn't exist
1560 public function getPage( $name ) {
1561 [ $realName, /*...*/ ] = $this->resolveAlias( $name );
1563 $specialPageList = $this->getPageList();
1565 if ( isset( $specialPageList[$realName] ) ) {
1566 $rec = $specialPageList[$realName];
1568 if ( is_array( $rec ) || is_string( $rec ) || is_callable( $rec ) ) {
1569 $page = $this->objectFactory->createObject(
1570 $rec,
1572 'allowClassName' => true,
1573 'allowCallable' => true
1576 } else {
1577 $page = null;
1580 if ( $page instanceof SpecialPage ) {
1581 $page->setHookContainer( $this->hookContainer );
1582 $page->setContentLanguage( $this->contLang );
1583 $page->setSpecialPageFactory( $this );
1584 return $page;
1587 // It's not a classname, nor a callback, nor a legacy constructor array,
1588 // nor a special page object. Give up.
1589 wfLogWarning( "Cannot instantiate special page $realName: bad spec!" );
1592 return null;
1596 * Get listed special pages available to the current user.
1598 * This includes both unrestricted pages, and restricted pages
1599 * that the current user has the required permissions for.
1601 * @param User $user User object to check permissions provided
1602 * @return SpecialPage[]
1604 public function getUsablePages( User $user ): array {
1605 $pages = [];
1606 foreach ( $this->getPageList() as $name => $rec ) {
1607 $page = $this->getPage( $name );
1608 if ( $page ) { // not null
1609 $page->setContext( RequestContext::getMain() );
1610 if ( $page->isListed()
1611 && ( !$page->isRestricted() || $page->userCanExecute( $user ) )
1613 $pages[$name] = $page;
1618 return $pages;
1622 * Get listed special pages available to everyone by default.
1624 * @return array<string,SpecialPage>
1626 public function getRegularPages(): array {
1627 $pages = [];
1628 foreach ( $this->getPageList() as $name => $rec ) {
1629 $page = $this->getPage( $name );
1630 if ( $page && $page->isListed() && !$page->isRestricted() ) {
1631 $pages[$name] = $page;
1635 return $pages;
1639 * Get listed special pages, including those that may require user rights.
1641 * @since 1.42
1642 * @return array<string,SpecialPage>
1644 public function getListedPages(): array {
1645 $pages = [];
1646 foreach ( $this->getPageList() as $name => $rec ) {
1647 $page = $this->getPage( $name );
1648 if ( $page && $page->isListed() ) {
1649 $pages[$name] = $page;
1652 return $pages;
1656 * Execute a special page path.
1657 * The path may contain parameters, e.g. Special:Name/Params
1658 * Extracts the special page name and call the execute method, passing the parameters
1660 * Returns a title object if the page is redirected, false if there was no such special
1661 * page, and true if it was successful.
1663 * @param PageReference|string $path
1664 * @param IContextSource $context
1665 * @param bool $including Bool output is being captured for use in {{special:whatever}}
1666 * @param LinkRenderer|null $linkRenderer (since 1.28)
1668 * @return bool|Title
1670 public function executePath( $path, IContextSource $context, $including = false,
1671 ?LinkRenderer $linkRenderer = null
1673 if ( $path instanceof PageReference ) {
1674 $path = $path->getDBkey();
1677 $bits = explode( '/', $path, 2 );
1678 $name = $bits[0];
1679 $par = $bits[1] ?? null; // T4087
1681 $page = $this->getPage( $name );
1682 if ( !$page ) {
1683 // Emulate SpecialPage::setHeaders()
1684 $context->getOutput()->setArticleRelated( false );
1685 $context->getOutput()->setRobotPolicy( 'noindex,nofollow' );
1687 if ( $context->getConfig()->get( MainConfigNames::Send404Code ) ) {
1688 $context->getOutput()->setStatusCode( 404 );
1691 $context->getOutput()->showErrorPage( 'nosuchspecialpage', 'nospecialpagetext' );
1693 return false;
1696 if ( !$including ) {
1697 ProfilingContext::singleton()->init( MW_ENTRY_POINT, $page->getName() );
1698 // Narrow DB query expectations for this HTTP request
1699 $trxLimits = $context->getConfig()->get( MainConfigNames::TrxProfilerLimits );
1700 $trxProfiler = Profiler::instance()->getTransactionProfiler();
1701 if ( $context->getRequest()->wasPosted() && !$page->doesWrites() ) {
1702 $trxProfiler->setExpectations( $trxLimits['POST-nonwrite'], __METHOD__ );
1706 // Page exists, set the context
1707 $page->setContext( $context );
1709 if ( !$including ) {
1710 // Redirect to canonical alias for GET commands
1711 // Not for POST, we'd lose the post data, so it's best to just distribute
1712 // the request. Such POST requests are possible for old extensions that
1713 // generate self-links without being aware that their default name has
1714 // changed.
1715 if ( $name != $page->getLocalName() && !$context->getRequest()->wasPosted() ) {
1716 $query = $context->getRequest()->getQueryValues();
1717 unset( $query['title'] );
1718 $title = $page->getPageTitle( $par ?? false );
1719 $url = $title->getFullURL( $query );
1720 $context->getOutput()->redirect( $url );
1722 return $title;
1725 // @phan-suppress-next-line PhanUndeclaredMethod
1726 $context->setTitle( $page->getPageTitle( $par ?? false ) );
1727 } elseif ( !$page->isIncludable() ) {
1728 return false;
1731 $page->including( $including );
1732 if ( $linkRenderer ) {
1733 $page->setLinkRenderer( $linkRenderer );
1736 // Execute special page
1737 $page->run( $par );
1739 return true;
1743 * Just like executePath() but will override global variables and execute
1744 * the page in "inclusion" mode. Returns true if the execution was
1745 * successful or false if there was no such special page, or a title object
1746 * if it was a redirect.
1748 * Also saves the current $wgTitle, $wgOut, $wgRequest, $wgUser and $wgLang
1749 * variables so that the special page will get the context it'd expect on a
1750 * normal request, and then restores them to their previous values after.
1752 * @param PageReference $page
1753 * @param IContextSource $context
1754 * @param LinkRenderer|null $linkRenderer (since 1.28)
1755 * @return bool|Title
1757 public function capturePath(
1758 PageReference $page, IContextSource $context, ?LinkRenderer $linkRenderer = null
1760 // phpcs:ignore MediaWiki.Usage.DeprecatedGlobalVariables.Deprecated$wgUser,MediaWiki.Usage.DeprecatedGlobalVariables.Deprecated$wgTitle
1761 global $wgTitle, $wgOut, $wgRequest, $wgUser, $wgLang;
1762 $main = RequestContext::getMain();
1764 // Save current globals and main context
1765 $glob = [
1766 'title' => $wgTitle,
1767 'output' => $wgOut,
1768 'request' => $wgRequest,
1769 'user' => $wgUser,
1770 'language' => $wgLang,
1772 $ctx = [
1773 'title' => $main->getTitle(),
1774 'output' => $main->getOutput(),
1775 'request' => $main->getRequest(),
1776 'user' => $main->getUser(),
1777 'language' => $main->getLanguage(),
1779 if ( $main->canUseWikiPage() ) {
1780 $ctx['wikipage'] = $main->getWikiPage();
1783 // just needed for $wgTitle and RequestContext::setTitle
1784 $title = $this->titleFactory->castFromPageReference( $page );
1786 // Override
1787 $wgTitle = $title;
1788 $wgOut = $context->getOutput();
1789 $wgRequest = $context->getRequest();
1790 $wgUser = $context->getUser();
1791 $wgLang = $context->getLanguage();
1792 // FIXME: Once reasonably certain that no SpecialPage subclasses
1793 // rely on direct RequestContext::getMain instead of their local
1794 // context getters, these can be removed (T323184)
1795 // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
1796 @$main->setTitle( $title );
1797 $main->setOutput( $context->getOutput() );
1798 $main->setRequest( $context->getRequest() );
1799 $main->setUser( $context->getUser() );
1800 $main->setLanguage( $context->getLanguage() );
1802 try {
1803 // The useful part
1804 return $this->executePath( $page, $context, true, $linkRenderer );
1805 } finally {
1806 // Restore old globals and context
1807 $wgTitle = $glob['title'];
1808 $wgOut = $glob['output'];
1809 $wgRequest = $glob['request'];
1810 $wgUser = $glob['user'];
1811 $wgLang = $glob['language'];
1812 // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
1813 @$main->setTitle( $ctx['title'] );
1814 $main->setOutput( $ctx['output'] );
1815 $main->setRequest( $ctx['request'] );
1816 $main->setUser( $ctx['user'] );
1817 $main->setLanguage( $ctx['language'] );
1818 if ( isset( $ctx['wikipage'] ) ) {
1819 $main->setWikiPage( $ctx['wikipage'] );
1825 * Get the local name for a specified canonical name
1827 * @param string $name
1828 * @param string|false|null $subpage
1829 * @return string
1831 public function getLocalNameFor( $name, $subpage = false ) {
1832 $aliases = $this->contLang->getSpecialPageAliases();
1833 $aliasList = $this->getAliasList();
1835 // Find the first alias that maps back to $name
1836 if ( isset( $aliases[$name] ) ) {
1837 $found = false;
1838 foreach ( $aliases[$name] as $alias ) {
1839 $caseFoldedAlias = $this->contLang->caseFold( $alias );
1840 $caseFoldedAlias = str_replace( ' ', '_', $caseFoldedAlias );
1841 if ( isset( $aliasList[$caseFoldedAlias] ) &&
1842 $aliasList[$caseFoldedAlias] === $name
1844 $name = $alias;
1845 $found = true;
1846 break;
1849 if ( !$found ) {
1850 wfWarn( "Did not find a usable alias for special page '$name'. " .
1851 "It seems all defined aliases conflict?" );
1853 } else {
1854 // Check if someone misspelled the correct casing
1855 if ( is_array( $aliases ) ) {
1856 foreach ( $aliases as $n => $values ) {
1857 if ( strcasecmp( $name, $n ) === 0 ) {
1858 wfWarn( "Found alias defined for $n when searching for " .
1859 "special page aliases for $name. Case mismatch?" );
1860 return $this->getLocalNameFor( $n, $subpage );
1865 wfWarn( "Did not find alias for special page '$name'. " .
1866 "Perhaps no aliases are defined for it?" );
1869 if ( $subpage !== false && $subpage !== null ) {
1870 // Make sure it's in dbkey form
1871 $subpage = str_replace( ' ', '_', $subpage );
1872 $name = "$name/$subpage";
1875 return $this->contLang->ucfirst( $name );
1879 * Get a title for a given alias
1881 * @param string $alias
1882 * @return Title|null Title or null if there is no such alias
1884 public function getTitleForAlias( $alias ) {
1885 [ $name, $subpage ] = $this->resolveAlias( $alias );
1886 if ( $name != null ) {
1887 return SpecialPage::getTitleFor( $name, $subpage );
1890 return null;