MediaWiki  master
SpecialPageFactory.php
Go to the documentation of this file.
1 <?php
25 namespace MediaWiki\SpecialPage;
26 
27 use IContextSource;
28 use Language;
34 use Profiler;
35 use RequestContext;
36 use SpecialPage;
37 use Title;
38 use TitleFactory;
39 use User;
40 use Wikimedia\ObjectFactory;
41 
67  private const CORE_LIST = [
68  // Maintenance Reports
69  'BrokenRedirects' => [
70  'class' => \SpecialBrokenRedirects::class,
71  'services' => [
72  'ContentHandlerFactory',
73  'DBLoadBalancer',
74  'LinkBatchFactory',
75  ]
76  ],
77  'Deadendpages' => [
78  'class' => \SpecialDeadendPages::class,
79  'services' => [
80  'NamespaceInfo',
81  'DBLoadBalancer',
82  'LinkBatchFactory',
83  'LanguageConverterFactory',
84  ]
85  ],
86  'DoubleRedirects' => [
87  'class' => \SpecialDoubleRedirects::class,
88  'services' => [
89  'ContentHandlerFactory',
90  'LinkBatchFactory',
91  'DBLoadBalancer',
92  ]
93  ],
94  'Longpages' => [
95  'class' => \SpecialLongPages::class,
96  'services' => [
97  // Same as for Shortpages
98  'NamespaceInfo',
99  'DBLoadBalancer',
100  'LinkBatchFactory',
101  ]
102  ],
103  'Ancientpages' => [
104  'class' => \SpecialAncientPages::class,
105  'services' => [
106  'NamespaceInfo',
107  'DBLoadBalancer',
108  'LinkBatchFactory',
109  'LanguageConverterFactory',
110  ]
111  ],
112  'Lonelypages' => [
113  'class' => \SpecialLonelyPages::class,
114  'services' => [
115  'NamespaceInfo',
116  'DBLoadBalancer',
117  'LinkBatchFactory',
118  'LanguageConverterFactory',
119  ]
120  ],
121  'Fewestrevisions' => [
122  'class' => \SpecialFewestRevisions::class,
123  'services' => [
124  // Same as for Mostrevisions
125  'NamespaceInfo',
126  'DBLoadBalancer',
127  'LinkBatchFactory',
128  'LanguageConverterFactory',
129  ]
130  ],
131  'Withoutinterwiki' => [
132  'class' => \SpecialWithoutInterwiki::class,
133  'services' => [
134  'NamespaceInfo',
135  'DBLoadBalancer',
136  'LinkBatchFactory',
137  'LanguageConverterFactory',
138  ]
139  ],
140  'Protectedpages' => [
141  'class' => \SpecialProtectedpages::class,
142  'services' => [
143  'LinkBatchFactory',
144  'DBLoadBalancer',
145  'CommentStore',
146  'UserCache',
147  'RowCommentFormatter',
148  ]
149  ],
150  'Protectedtitles' => [
151  'class' => \SpecialProtectedtitles::class,
152  'services' => [
153  'LinkBatchFactory',
154  'DBLoadBalancer',
155  ]
156  ],
157  'Shortpages' => [
158  'class' => \SpecialShortPages::class,
159  'services' => [
160  // Same as for Longpages
161  'NamespaceInfo',
162  'DBLoadBalancer',
163  'LinkBatchFactory',
164  ]
165  ],
166  'Uncategorizedcategories' => [
167  'class' => \SpecialUncategorizedCategories::class,
168  'services' => [
169  // Same as for SpecialUncategorizedPages and SpecialUncategorizedTemplates
170  'NamespaceInfo',
171  'DBLoadBalancer',
172  'LinkBatchFactory',
173  'LanguageConverterFactory',
174  ]
175  ],
176  'Uncategorizedimages' => [
177  'class' => \SpecialUncategorizedImages::class,
178  'services' => [
179  'DBLoadBalancer',
180  ]
181  ],
182  'Uncategorizedpages' => [
183  'class' => \SpecialUncategorizedPages::class,
184  'services' => [
185  // Same as for SpecialUncategorizedCategories and SpecialUncategorizedTemplates
186  'NamespaceInfo',
187  'DBLoadBalancer',
188  'LinkBatchFactory',
189  'LanguageConverterFactory',
190  ]
191  ],
192  'Uncategorizedtemplates' => [
193  'class' => \SpecialUncategorizedTemplates::class,
194  'services' => [
195  // Same as for SpecialUncategorizedCategories and SpecialUncategorizedPages
196  'NamespaceInfo',
197  'DBLoadBalancer',
198  'LinkBatchFactory',
199  'LanguageConverterFactory',
200  ]
201  ],
202  'Unusedcategories' => [
203  'class' => \SpecialUnusedCategories::class,
204  'services' => [
205  'DBLoadBalancer',
206  'LinkBatchFactory',
207  ]
208  ],
209  'Unusedimages' => [
210  'class' => \SpecialUnusedImages::class,
211  'services' => [
212  'DBLoadBalancer',
213  ]
214  ],
215  'Unusedtemplates' => [
216  'class' => \SpecialUnusedTemplates::class,
217  'services' => [
218  'DBLoadBalancer',
219  ]
220  ],
221  'Unwatchedpages' => [
222  'class' => \SpecialUnwatchedPages::class,
223  'services' => [
224  'LinkBatchFactory',
225  'DBLoadBalancer',
226  'LanguageConverterFactory',
227  ]
228  ],
229  'Wantedcategories' => [
230  'class' => \SpecialWantedCategories::class,
231  'services' => [
232  'DBLoadBalancer',
233  'LinkBatchFactory',
234  'LanguageConverterFactory',
235  ]
236  ],
237  'Wantedfiles' => [
238  'class' => \WantedFilesPage::class,
239  'services' => [
240  'RepoGroup',
241  'DBLoadBalancer',
242  'LinkBatchFactory',
243  ]
244  ],
245  'Wantedpages' => [
246  'class' => \WantedPagesPage::class,
247  'services' => [
248  'DBLoadBalancer',
249  'LinkBatchFactory',
250  ]
251  ],
252  'Wantedtemplates' => [
253  'class' => \SpecialWantedTemplates::class,
254  'services' => [
255  'DBLoadBalancer',
256  'LinkBatchFactory',
257  ]
258  ],
259 
260  // List of pages
261  'Allpages' => [
262  'class' => \SpecialAllPages::class,
263  'services' => [
264  'DBLoadBalancer',
265  'SearchEngineFactory',
266  ]
267  ],
268  'Prefixindex' => [
269  'class' => \SpecialPrefixindex::class,
270  'services' => [
271  'DBLoadBalancer',
272  'LinkCache',
273  ]
274  ],
275  'Categories' => [
276  'class' => \SpecialCategories::class,
277  'services' => [
278  'LinkBatchFactory',
279  'DBLoadBalancer',
280  ]
281  ],
282  'Listredirects' => [
283  'class' => \SpecialListRedirects::class,
284  'services' => [
285  'LinkBatchFactory',
286  'DBLoadBalancer',
287  'WikiPageFactory',
288  'RedirectLookup'
289  ]
290  ],
291  'PagesWithProp' => [
292  'class' => \SpecialPagesWithProp::class,
293  'services' => [
294  'DBLoadBalancer',
295  ]
296  ],
297  'TrackingCategories' => [
298  'class' => \SpecialTrackingCategories::class,
299  'services' => [
300  'LinkBatchFactory',
301  'TrackingCategories',
302  ]
303  ],
304 
305  // Authentication
306  'Userlogin' => [
307  'class' => \SpecialUserLogin::class,
308  'services' => [
309  'AuthManager',
310  ]
311  ],
312  'Userlogout' => [
313  'class' => \SpecialUserLogout::class,
314  ],
315  'CreateAccount' => [
316  'class' => \SpecialCreateAccount::class,
317  'services' => [
318  'AuthManager',
319  ]
320  ],
321  'LinkAccounts' => [
322  'class' => \SpecialLinkAccounts::class,
323  'services' => [
324  'AuthManager',
325  ]
326  ],
327  'UnlinkAccounts' => [
328  'class' => \SpecialUnlinkAccounts::class,
329  'services' => [
330  'AuthManager',
331  ]
332  ],
333  'ChangeCredentials' => [
334  'class' => \SpecialChangeCredentials::class,
335  'services' => [
336  'AuthManager',
337  ]
338  ],
339  'RemoveCredentials' => [
340  'class' => \SpecialRemoveCredentials::class,
341  'services' => [
342  'AuthManager',
343  ]
344  ],
345 
346  // Users and rights
347  'Activeusers' => [
348  'class' => \SpecialActiveUsers::class,
349  'services' => [
350  'LinkBatchFactory',
351  'DBLoadBalancer',
352  'UserGroupManager',
353  ]
354  ],
355  'Block' => [
356  'class' => \SpecialBlock::class,
357  'services' => [
358  'BlockUtils',
359  'BlockPermissionCheckerFactory',
360  'BlockUserFactory',
361  'UserNameUtils',
362  'UserNamePrefixSearch',
363  'BlockActionInfo',
364  'TitleFormatter',
365  'NamespaceInfo'
366  ]
367  ],
368  'Unblock' => [
369  'class' => \SpecialUnblock::class,
370  'services' => [
371  'UnblockUserFactory',
372  'BlockUtils',
373  'UserNameUtils',
374  'UserNamePrefixSearch',
375  ]
376  ],
377  'BlockList' => [
378  'class' => \SpecialBlockList::class,
379  'services' => [
380  'LinkBatchFactory',
381  'BlockRestrictionStore',
382  'DBLoadBalancer',
383  'CommentStore',
384  'BlockUtils',
385  'BlockActionInfo',
386  'RowCommentFormatter',
387  ],
388  ],
389  'AutoblockList' => [
390  'class' => \SpecialAutoblockList::class,
391  'services' => [
392  'LinkBatchFactory',
393  'BlockRestrictionStore',
394  'DBLoadBalancer',
395  'CommentStore',
396  'BlockUtils',
397  'BlockActionInfo',
398  'RowCommentFormatter',
399  ],
400  ],
401  'ChangePassword' => [
402  'class' => \SpecialChangePassword::class,
403  ],
404  'BotPasswords' => [
405  'class' => \SpecialBotPasswords::class,
406  'services' => [
407  'PasswordFactory',
408  'AuthManager',
409  'CentralIdLookup',
410  ]
411  ],
412  'PasswordReset' => [
413  'class' => \SpecialPasswordReset::class,
414  'services' => [
415  'PasswordReset'
416  ]
417  ],
418  'DeletedContributions' => [
419  'class' => \SpecialDeletedContributions::class,
420  'services' => [
421  'PermissionManager',
422  'DBLoadBalancer',
423  'CommentStore',
424  'RevisionFactory',
425  'NamespaceInfo',
426  'UserFactory',
427  'UserNameUtils',
428  'UserNamePrefixSearch',
429  ]
430  ],
431  'Preferences' => [
432  'class' => \SpecialPreferences::class,
433  'services' => [
434  'PreferencesFactory',
435  'UserOptionsManager',
436  ]
437  ],
438  'ResetTokens' => [
439  'class' => \SpecialResetTokens::class,
440  ],
441  'Contributions' => [
442  'class' => \SpecialContributions::class,
443  'services' => [
444  'LinkBatchFactory',
445  'PermissionManager',
446  'DBLoadBalancer',
447  'ActorMigration',
448  'RevisionStore',
449  'NamespaceInfo',
450  'UserNameUtils',
451  'UserNamePrefixSearch',
452  'UserOptionsLookup',
453  'CommentFormatter',
454  'UserFactory',
455  ]
456  ],
457  'Listgrouprights' => [
458  'class' => \SpecialListGroupRights::class,
459  'services' => [
460  'NamespaceInfo',
461  'UserGroupManager',
462  'LanguageConverterFactory',
463  'GroupPermissionsLookup',
464  ]
465  ],
466  'Listgrants' => [
467  'class' => \SpecialListGrants::class,
468  ],
469  'Listusers' => [
470  'class' => \SpecialListUsers::class,
471  'services' => [
472  'LinkBatchFactory',
473  'DBLoadBalancer',
474  'UserGroupManager',
475  ]
476  ],
477  'Listadmins' => [
478  'class' => \SpecialListAdmins::class,
479  ],
480  'Listbots' => [
481  'class' => \SpecialListBots::class,
482  ],
483  'Userrights' => [
484  'class' => \UserrightsPage::class,
485  'services' => [
486  'UserGroupManagerFactory',
487  'UserNameUtils',
488  'UserNamePrefixSearch',
489  'UserFactory',
490  ]
491  ],
492  'EditWatchlist' => [
493  'class' => \SpecialEditWatchlist::class,
494  'services' => [
495  'WatchedItemStore',
496  'TitleParser',
497  'GenderCache',
498  'LinkBatchFactory',
499  'NamespaceInfo',
500  'WikiPageFactory',
501  'WatchlistManager',
502  ]
503  ],
504  'PasswordPolicies' => [
505  'class' => \SpecialPasswordPolicies::class
506  ],
507 
508  // Recent changes and logs
509  'Newimages' => [
510  'class' => \SpecialNewFiles::class,
511  'services' => [
512  'MimeAnalyzer',
513  'GroupPermissionsLookup',
514  'DBLoadBalancer',
515  'LinkBatchFactory',
516  ]
517  ],
518  'Log' => [
519  'class' => \SpecialLog::class,
520  'services' => [
521  'LinkBatchFactory',
522  'DBLoadBalancer',
523  'ActorNormalization',
524  'UserIdentityLookup',
525  ]
526  ],
527  'Watchlist' => [
528  'class' => \SpecialWatchlist::class,
529  'services' => [
530  'WatchedItemStore',
531  'WatchlistManager',
532  'DBLoadBalancer',
533  'UserOptionsLookup',
534  ]
535  ],
536  'Newpages' => [
537  'class' => \SpecialNewpages::class,
538  'services' => [
539  'LinkBatchFactory',
540  'CommentStore',
541  'ContentHandlerFactory',
542  'GroupPermissionsLookup',
543  'DBLoadBalancer',
544  'RevisionLookup',
545  'NamespaceInfo',
546  'UserOptionsLookup',
547  ]
548  ],
549  'Recentchanges' => [
550  'class' => \SpecialRecentChanges::class,
551  'services' => [
552  'WatchedItemStore',
553  'MessageCache',
554  'DBLoadBalancer',
555  'UserOptionsLookup',
556  ]
557  ],
558  'Recentchangeslinked' => [
559  'class' => \SpecialRecentChangesLinked::class,
560  'services' => [
561  'WatchedItemStore',
562  'MessageCache',
563  'DBLoadBalancer',
564  'UserOptionsLookup',
565  'SearchEngineFactory',
566  ]
567  ],
568  'Tags' => [
569  'class' => \SpecialTags::class,
570  ],
571 
572  // Media reports and uploads
573  'Listfiles' => [
574  'class' => \SpecialListFiles::class,
575  'services' => [
576  'RepoGroup',
577  'DBLoadBalancer',
578  'CommentStore',
579  'UserNameUtils',
580  'UserNamePrefixSearch',
581  'UserCache',
582  ]
583  ],
584  'Filepath' => [
585  'class' => \SpecialFilepath::class,
586  ],
587  'MediaStatistics' => [
588  'class' => \SpecialMediaStatistics::class,
589  'services' => [
590  'MimeAnalyzer',
591  'DBLoadBalancer',
592  'LinkBatchFactory',
593  ]
594  ],
595  'MIMEsearch' => [
596  'class' => \SpecialMIMESearch::class,
597  'services' => [
598  'DBLoadBalancer',
599  'LinkBatchFactory',
600  'LanguageConverterFactory',
601  ]
602  ],
603  'FileDuplicateSearch' => [
604  'class' => \SpecialFileDuplicateSearch::class,
605  'services' => [
606  'LinkBatchFactory',
607  'RepoGroup',
608  'SearchEngineFactory',
609  'LanguageConverterFactory',
610  ]
611  ],
612  'Upload' => [
613  'class' => \SpecialUpload::class,
614  'services' => [
615  'RepoGroup',
616  'UserOptionsLookup',
617  'NamespaceInfo',
618  ]
619  ],
620  'UploadStash' => [
621  'class' => \SpecialUploadStash::class,
622  'services' => [
623  'RepoGroup',
624  'HttpRequestFactory',
625  ]
626  ],
627  'ListDuplicatedFiles' => [
628  'class' => \SpecialListDuplicatedFiles::class,
629  'services' => [
630  'DBLoadBalancer',
631  'LinkBatchFactory',
632  ]
633  ],
634 
635  // Data and tools
636  'ApiSandbox' => [
637  'class' => \SpecialApiSandbox::class,
638  ],
639  'Statistics' => [
640  'class' => \SpecialStatistics::class
641  ],
642  'Allmessages' => [
643  'class' => \SpecialAllMessages::class,
644  'services' => [
645  'LocalisationCache',
646  'DBLoadBalancer',
647  ]
648  ],
649  'Version' => [
650  'class' => \SpecialVersion::class,
651  'services' => [
652  'Parser',
653  ]
654  ],
655  'Lockdb' => [
656  'class' => \SpecialLockdb::class,
657  ],
658  'Unlockdb' => [
659  'class' => \SpecialUnlockdb::class,
660  ],
661 
662  // Redirecting special pages
663  'LinkSearch' => [
664  'class' => \SpecialLinkSearch::class,
665  'services' => [
666  'DBLoadBalancer',
667  'LinkBatchFactory',
668  ]
669  ],
670  'Randompage' => [
671  'class' => \SpecialRandomPage::class,
672  'services' => [
673  'DBLoadBalancer',
674  'NamespaceInfo',
675  ]
676  ],
677  'RandomInCategory' => [
678  'class' => \SpecialRandomInCategory::class,
679  'services' => [
680  'DBLoadBalancer',
681  ]
682  ],
683  'Randomredirect' => [
684  'class' => \SpecialRandomRedirect::class,
685  'services' => [
686  'DBLoadBalancer',
687  'NamespaceInfo',
688  ]
689  ],
690  'Randomrootpage' => [
691  'class' => \SpecialRandomRootPage::class,
692  'services' => [
693  'DBLoadBalancer',
694  'NamespaceInfo',
695  ]
696  ],
697  'GoToInterwiki' => [
698  'class' => \SpecialGoToInterwiki::class,
699  ],
700 
701  // High use pages
702  'Mostlinkedcategories' => [
703  'class' => \SpecialMostLinkedCategories::class,
704  'services' => [
705  'DBLoadBalancer',
706  'LinkBatchFactory',
707  'LanguageConverterFactory',
708  ]
709  ],
710  'Mostimages' => [
711  'class' => \MostimagesPage::class,
712  'services' => [
713  'DBLoadBalancer',
714  'LanguageConverterFactory',
715  ]
716  ],
717  'Mostinterwikis' => [
718  'class' => \SpecialMostInterwikis::class,
719  'services' => [
720  'NamespaceInfo',
721  'DBLoadBalancer',
722  'LinkBatchFactory',
723  ]
724  ],
725  'Mostlinked' => [
726  'class' => \SpecialMostLinked::class,
727  'services' => [
728  'DBLoadBalancer',
729  'LinkBatchFactory',
730  ]
731  ],
732  'Mostlinkedtemplates' => [
733  'class' => \SpecialMostLinkedTemplates::class,
734  'services' => [
735  'DBLoadBalancer',
736  'LinkBatchFactory',
737  ]
738  ],
739  'Mostcategories' => [
740  'class' => \SpecialMostCategories::class,
741  'services' => [
742  'NamespaceInfo',
743  'DBLoadBalancer',
744  'LinkBatchFactory',
745  ]
746  ],
747  'Mostrevisions' => [
748  'class' => \SpecialMostRevisions::class,
749  'services' => [
750  // Same as for Fewestrevisions
751  'NamespaceInfo',
752  'DBLoadBalancer',
753  'LinkBatchFactory',
754  'LanguageConverterFactory',
755  ]
756  ],
757 
758  // Page tools
759  'ComparePages' => [
760  'class' => \SpecialComparePages::class,
761  'services' => [
762  'RevisionLookup',
763  'ContentHandlerFactory',
764  ]
765  ],
766  'Export' => [
767  'class' => \SpecialExport::class,
768  'services' => [
769  'DBLoadBalancer',
770  'WikiExporterFactory',
771  'TitleFormatter',
772  ]
773  ],
774  'Import' => [
775  'class' => \SpecialImport::class,
776  'services' => [
777  'PermissionManager',
778  'WikiImporterFactory',
779  ]
780  ],
781  'Undelete' => [
782  'class' => \SpecialUndelete::class,
783  'services' => [
784  'PermissionManager',
785  'RevisionStore',
786  'RevisionRenderer',
787  'ContentHandlerFactory',
788  'ChangeTagDefStore',
789  'LinkBatchFactory',
790  'RepoGroup',
791  'DBLoadBalancer',
792  'UserOptionsLookup',
793  'WikiPageFactory',
794  'SearchEngineFactory',
795  'UndeletePageFactory',
796  'ArchivedRevisionLookup',
797  ],
798  ],
799  'Whatlinkshere' => [
800  'class' => \SpecialWhatLinksHere::class,
801  'services' => [
802  'DBLoadBalancer',
803  'LinkBatchFactory',
804  'ContentHandlerFactory',
805  'SearchEngineFactory',
806  'NamespaceInfo',
807  ]
808  ],
809  'MergeHistory' => [
810  'class' => \SpecialMergeHistory::class,
811  'services' => [
812  'MergeHistoryFactory',
813  'LinkBatchFactory',
814  'DBLoadBalancer',
815  'RevisionStore',
816  ]
817  ],
818  'ExpandTemplates' => [
819  'class' => \SpecialExpandTemplates::class,
820  'services' => [
821  'Parser',
822  'UserOptionsLookup',
823  'Tidy',
824  ],
825  ],
826  'ChangeContentModel' => [
827  'class' => \SpecialChangeContentModel::class,
828  'services' => [
829  'ContentHandlerFactory',
830  'ContentModelChangeFactory',
831  'SpamChecker',
832  'RevisionLookup',
833  'WikiPageFactory',
834  'SearchEngineFactory',
835  ],
836  ],
837 
838  // Other
839  'Booksources' => [
840  'class' => \SpecialBookSources::class,
841  'services' => [
842  'RevisionLookup',
843  ]
844  ],
845 
846  // Unlisted / redirects
847  'ApiHelp' => [
848  'class' => \SpecialApiHelp::class,
849  ],
850  'Blankpage' => [
851  'class' => \SpecialBlankpage::class,
852  ],
853  'DeletePage' => [
854  'class' => \SpecialDeletePage::class,
855  ],
856  'Diff' => [
857  'class' => \SpecialDiff::class,
858  ],
859  'EditPage' => [
860  'class' => \SpecialEditPage::class,
861  ],
862  'EditTags' => [
863  'class' => \SpecialEditTags::class,
864  'services' => [
865  'PermissionManager',
866  ],
867  ],
868  'Emailuser' => [
869  'class' => \SpecialEmailUser::class,
870  'services' => [
871  'UserNameUtils',
872  'UserNamePrefixSearch',
873  'UserOptionsLookup',
874  ]
875  ],
876  'Movepage' => [
877  'class' => \MovePageForm::class,
878  'services' => [
879  'MovePageFactory',
880  'PermissionManager',
881  'UserOptionsLookup',
882  'DBLoadBalancer',
883  'ContentHandlerFactory',
884  'NamespaceInfo',
885  'LinkBatchFactory',
886  'RepoGroup',
887  'WikiPageFactory',
888  'SearchEngineFactory',
889  'WatchlistManager',
890  ]
891  ],
892  'Mycontributions' => [
893  'class' => \SpecialMycontributions::class,
894  ],
895  'MyLanguage' => [
896  'class' => \SpecialMyLanguage::class,
897  'services' => [
898  'LanguageNameUtils',
899  'RedirectLookup'
900  ]
901  ],
902  'Mypage' => [
903  'class' => \SpecialMypage::class,
904  ],
905  'Mytalk' => [
906  'class' => \SpecialMytalk::class,
907  ],
908  'PageHistory' => [
909  'class' => \SpecialPageHistory::class,
910  ],
911  'PageInfo' => [
912  'class' => \SpecialPageInfo::class,
913  ],
914  'ProtectPage' => [
915  'class' => \SpecialProtectPage::class,
916  ],
917  'Purge' => [
918  'class' => \SpecialPurge::class,
919  ],
920  'Myuploads' => [
921  'class' => \SpecialMyuploads::class,
922  ],
923  'AllMyUploads' => [
924  'class' => \SpecialAllMyUploads::class,
925  ],
926  'NewSection' => [
927  'class' => \SpecialNewSection::class,
928  ],
929  'PermanentLink' => [
930  'class' => \SpecialPermanentLink::class,
931  ],
932  'Redirect' => [
933  'class' => \SpecialRedirect::class,
934  'services' => [
935  'RepoGroup',
936  'UserFactory',
937  ]
938  ],
939  'Revisiondelete' => [
940  'class' => \SpecialRevisionDelete::class,
941  'services' => [
942  'PermissionManager',
943  'RepoGroup',
944  ],
945  ],
946  'RunJobs' => [
947  'class' => \SpecialRunJobs::class,
948  'services' => [
949  'JobRunner',
950  'ReadOnlyMode',
951  ]
952  ],
953  'Specialpages' => [
954  'class' => \SpecialSpecialpages::class,
955  ],
956  'PageData' => [
957  'class' => \SpecialPageData::class,
958  ],
959  ];
960 
962  private $list;
963 
965  private $aliases;
966 
968  private $options;
969 
971  private $contLang;
972 
974  private $objectFactory;
975 
977  private $hookContainer;
978 
980  private $hookRunner;
981 
985  public const CONSTRUCTOR_OPTIONS = [
986  'DisableInternalSearch',
987  'EmailAuthentication',
988  'EnableEmail',
989  'EnableJavaScriptTest',
990  'EnableSpecialMute',
991  'PageLanguageUseDB',
992  'SpecialPages',
993  ];
994 
998  private $titleFactory;
999 
1007  public function __construct(
1010  ObjectFactory $objectFactory,
1013  ) {
1014  $options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS );
1015  $this->options = $options;
1016  $this->contLang = $contLang;
1017  $this->objectFactory = $objectFactory;
1018  $this->titleFactory = $titleFactory;
1019  $this->hookContainer = $hookContainer;
1020  $this->hookRunner = new HookRunner( $hookContainer );
1021  }
1022 
1029  public function getNames(): array {
1030  return array_keys( $this->getPageList() );
1031  }
1032 
1038  private function getPageList(): array {
1039  if ( !is_array( $this->list ) ) {
1040  $this->list = self::CORE_LIST;
1041 
1042  if ( !$this->options->get( 'DisableInternalSearch' ) ) {
1043  $this->list['Search'] = [
1044  'class' => \SpecialSearch::class,
1045  'services' => [
1046  'SearchEngineConfig',
1047  'SearchEngineFactory',
1048  'NamespaceInfo',
1049  'ContentHandlerFactory',
1050  'InterwikiLookup',
1051  'ReadOnlyMode',
1052  'UserOptionsManager',
1053  'LanguageConverterFactory'
1054  ]
1055  ];
1056  }
1057 
1058  if ( $this->options->get( 'EmailAuthentication' ) ) {
1059  $this->list['Confirmemail'] = [
1060  'class' => \SpecialConfirmEmail::class,
1061  'services' => [
1062  'UserFactory',
1063  ]
1064  ];
1065  $this->list['Invalidateemail'] = [
1066  'class' => \SpecialEmailInvalidate::class,
1067  'services' => [
1068  'UserFactory',
1069  ]
1070  ];
1071  }
1072 
1073  if ( $this->options->get( 'EnableEmail' ) ) {
1074  $this->list['ChangeEmail'] = [
1075  'class' => \SpecialChangeEmail::class,
1076  'services' => [
1077  'AuthManager',
1078  ],
1079  ];
1080  }
1081 
1082  if ( $this->options->get( 'EnableJavaScriptTest' ) ) {
1083  $this->list['JavaScriptTest'] = [
1084  'class' => \SpecialJavaScriptTest::class
1085  ];
1086  }
1087 
1088  if ( $this->options->get( 'EnableSpecialMute' ) ) {
1089  $this->list['Mute'] = [
1090  'class' => \SpecialMute::class,
1091  'services' => [
1092  'CentralIdLookup',
1093  'UserOptionsManager',
1094  'UserIdentityLookup',
1095  ]
1096  ];
1097  }
1098 
1099  if ( $this->options->get( 'PageLanguageUseDB' ) ) {
1100  $this->list['PageLanguage'] = [
1101  'class' => \SpecialPageLanguage::class,
1102  'services' => [
1103  'ContentHandlerFactory',
1104  'LanguageNameUtils',
1105  'DBLoadBalancer',
1106  'SearchEngineFactory',
1107  ]
1108  ];
1109  }
1110 
1111  // Add extension special pages
1112  $this->list = array_merge( $this->list, $this->options->get( 'SpecialPages' ) );
1113 
1114  // This hook can be used to disable unwanted core special pages
1115  // or conditionally register special pages.
1116  $this->hookRunner->onSpecialPage_initList( $this->list );
1117  }
1118 
1119  return $this->list;
1120  }
1121 
1128  private function getAliasList(): array {
1129  if ( $this->aliases === null ) {
1130  $aliases = $this->contLang->getSpecialPageAliases();
1131  $pageList = $this->getPageList();
1132 
1133  $this->aliases = [];
1134  $keepAlias = [];
1135 
1136  // Force every canonical name to be an alias for itself.
1137  foreach ( $pageList as $name => $stuff ) {
1138  $caseFoldedAlias = $this->contLang->caseFold( $name );
1139  $this->aliases[$caseFoldedAlias] = $name;
1140  $keepAlias[$caseFoldedAlias] = 'canonical';
1141  }
1142 
1143  // Check for $aliases being an array since Language::getSpecialPageAliases can return null
1144  if ( is_array( $aliases ) ) {
1145  foreach ( $aliases as $realName => $aliasList ) {
1146  $aliasList = array_values( $aliasList );
1147  foreach ( $aliasList as $i => $alias ) {
1148  $caseFoldedAlias = $this->contLang->caseFold( $alias );
1149 
1150  if ( isset( $this->aliases[$caseFoldedAlias] ) &&
1151  $realName === $this->aliases[$caseFoldedAlias]
1152  ) {
1153  // Ignore same-realName conflicts
1154  continue;
1155  }
1156 
1157  if ( !isset( $keepAlias[$caseFoldedAlias] ) ) {
1158  $this->aliases[$caseFoldedAlias] = $realName;
1159  if ( !$i ) {
1160  $keepAlias[$caseFoldedAlias] = 'first';
1161  }
1162  } elseif ( !$i ) {
1163  wfWarn( "First alias '$alias' for $realName conflicts with " .
1164  "{$keepAlias[$caseFoldedAlias]} alias for " .
1165  $this->aliases[$caseFoldedAlias]
1166  );
1167  }
1168  }
1169  }
1170  }
1171  }
1172 
1173  return $this->aliases;
1174  }
1175 
1184  public function resolveAlias( $alias ) {
1185  $bits = explode( '/', $alias, 2 );
1186 
1187  $caseFoldedAlias = $this->contLang->caseFold( $bits[0] );
1188  $caseFoldedAlias = str_replace( ' ', '_', $caseFoldedAlias );
1189  $aliases = $this->getAliasList();
1190  if ( !isset( $aliases[$caseFoldedAlias] ) ) {
1191  return [ null, null ];
1192  }
1193  $name = $aliases[$caseFoldedAlias];
1194  $par = $bits[1] ?? null; // T4087
1195 
1196  return [ $name, $par ];
1197  }
1198 
1205  public function exists( $name ) {
1206  list( $title, /*...*/ ) = $this->resolveAlias( $name );
1207 
1208  $specialPageList = $this->getPageList();
1209  return isset( $specialPageList[$title] );
1210  }
1211 
1218  public function getPage( $name ) {
1219  list( $realName, /*...*/ ) = $this->resolveAlias( $name );
1220 
1221  $specialPageList = $this->getPageList();
1222 
1223  if ( isset( $specialPageList[$realName] ) ) {
1224  $rec = $specialPageList[$realName];
1225 
1226  if ( is_array( $rec ) || is_string( $rec ) || is_callable( $rec ) ) {
1227  $page = $this->objectFactory->createObject(
1228  $rec,
1229  [
1230  'allowClassName' => true,
1231  'allowCallable' => true
1232  ]
1233  );
1234  } else {
1235  $page = null;
1236  }
1237 
1238  if ( $page instanceof SpecialPage ) {
1239  $page->setHookContainer( $this->hookContainer );
1240  $page->setContentLanguage( $this->contLang );
1241  $page->setSpecialPageFactory( $this );
1242  return $page;
1243  }
1244 
1245  // It's not a classname, nor a callback, nor a legacy constructor array,
1246  // nor a special page object. Give up.
1247  wfLogWarning( "Cannot instantiate special page $realName: bad spec!" );
1248  }
1249 
1250  return null;
1251  }
1252 
1262  public function getUsablePages( User $user ): array {
1263  $pages = [];
1264  foreach ( $this->getPageList() as $name => $rec ) {
1265  $page = $this->getPage( $name );
1266  if ( $page ) { // not null
1267  $page->setContext( RequestContext::getMain() );
1268  if ( $page->isListed()
1269  && ( !$page->isRestricted() || $page->userCanExecute( $user ) )
1270  ) {
1271  $pages[$name] = $page;
1272  }
1273  }
1274  }
1275 
1276  return $pages;
1277  }
1278 
1284  public function getRegularPages(): array {
1285  $pages = [];
1286  foreach ( $this->getPageList() as $name => $rec ) {
1287  $page = $this->getPage( $name );
1288  if ( $page && $page->isListed() && !$page->isRestricted() ) {
1289  $pages[$name] = $page;
1290  }
1291  }
1292 
1293  return $pages;
1294  }
1295 
1311  public function executePath( $path, IContextSource $context, $including = false,
1312  LinkRenderer $linkRenderer = null
1313  ) {
1314  if ( $path instanceof PageReference ) {
1315  $path = $path->getDBkey();
1316  }
1317 
1318  $bits = explode( '/', $path, 2 );
1319  $name = $bits[0];
1320  $par = $bits[1] ?? null; // T4087
1321 
1322  $page = $this->getPage( $name );
1323  if ( !$page ) {
1324  $context->getOutput()->setArticleRelated( false );
1325  $context->getOutput()->setRobotPolicy( 'noindex,nofollow' );
1326 
1327  global $wgSend404Code;
1328  if ( $wgSend404Code ) {
1329  $context->getOutput()->setStatusCode( 404 );
1330  }
1331 
1332  $context->getOutput()->showErrorPage( 'nosuchspecialpage', 'nospecialpagetext' );
1333 
1334  return false;
1335  }
1336 
1337  if ( !$including ) {
1338  // Narrow DB query expectations for this HTTP request
1339  $trxLimits = $context->getConfig()->get( 'TrxProfilerLimits' );
1340  $trxProfiler = Profiler::instance()->getTransactionProfiler();
1341  if ( $context->getRequest()->wasPosted() && !$page->doesWrites() ) {
1342  $trxProfiler->setExpectations( $trxLimits['POST-nonwrite'], __METHOD__ );
1343  $context->getRequest()->markAsSafeRequest();
1344  }
1345  }
1346 
1347  // Page exists, set the context
1348  $page->setContext( $context );
1349 
1350  if ( !$including ) {
1351  // Redirect to canonical alias for GET commands
1352  // Not for POST, we'd lose the post data, so it's best to just distribute
1353  // the request. Such POST requests are possible for old extensions that
1354  // generate self-links without being aware that their default name has
1355  // changed.
1356  if ( $name != $page->getLocalName() && !$context->getRequest()->wasPosted() ) {
1357  $query = $context->getRequest()->getQueryValues();
1358  unset( $query['title'] );
1359  $title = $page->getPageTitle( $par );
1360  $url = $title->getFullURL( $query );
1361  $context->getOutput()->redirect( $url );
1362 
1363  return $title;
1364  }
1365 
1366  // @phan-suppress-next-line PhanUndeclaredMethod
1367  $context->setTitle( $page->getPageTitle( $par ) );
1368  } elseif ( !$page->isIncludable() ) {
1369  return false;
1370  }
1371 
1372  $page->including( $including );
1373  if ( $linkRenderer ) {
1374  $page->setLinkRenderer( $linkRenderer );
1375  }
1376 
1377  // Execute special page
1378  $page->run( $par );
1379 
1380  return true;
1381  }
1382 
1398  public function capturePath(
1399  PageReference $page, IContextSource $context, LinkRenderer $linkRenderer = null
1400  ) {
1401  // phpcs:ignore MediaWiki.Usage.DeprecatedGlobalVariables.Deprecated$wgUser
1402  global $wgTitle, $wgOut, $wgRequest, $wgUser, $wgLang;
1403  $main = RequestContext::getMain();
1404 
1405  // Save current globals and main context
1406  $glob = [
1407  'title' => $wgTitle,
1408  'output' => $wgOut,
1409  'request' => $wgRequest,
1410  'user' => $wgUser,
1411  'language' => $wgLang,
1412  ];
1413  $ctx = [
1414  'title' => $main->getTitle(),
1415  'output' => $main->getOutput(),
1416  'request' => $main->getRequest(),
1417  'user' => $main->getUser(),
1418  'language' => $main->getLanguage(),
1419  ];
1420  if ( $main->canUseWikiPage() ) {
1421  $ctx['wikipage'] = $main->getWikiPage();
1422  }
1423 
1424  // just needed for $wgTitle and RequestContext::setTitle
1425  $title = $this->titleFactory->castFromPageReference( $page );
1426 
1427  // Override
1428  $wgTitle = $title;
1429  $wgOut = $context->getOutput();
1431  $wgUser = $context->getUser();
1433  $main->setTitle( $title );
1434  $main->setOutput( $context->getOutput() );
1435  $main->setRequest( $context->getRequest() );
1436  $main->setUser( $context->getUser() );
1437  $main->setLanguage( $context->getLanguage() );
1438 
1439  // The useful part
1440  $ret = $this->executePath( $page, $context, true, $linkRenderer );
1441 
1442  // Restore old globals and context
1443  $wgTitle = $glob['title'];
1444  $wgOut = $glob['output'];
1445  $wgRequest = $glob['request'];
1446  $wgUser = $glob['user'];
1447  $wgLang = $glob['language'];
1448  $main->setTitle( $ctx['title'] );
1449  $main->setOutput( $ctx['output'] );
1450  $main->setRequest( $ctx['request'] );
1451  $main->setUser( $ctx['user'] );
1452  $main->setLanguage( $ctx['language'] );
1453  if ( isset( $ctx['wikipage'] ) ) {
1454  $main->setWikiPage( $ctx['wikipage'] );
1455  }
1456 
1457  return $ret;
1458  }
1459 
1467  public function getLocalNameFor( $name, $subpage = false ) {
1468  $aliases = $this->contLang->getSpecialPageAliases();
1469  $aliasList = $this->getAliasList();
1470 
1471  // Find the first alias that maps back to $name
1472  if ( isset( $aliases[$name] ) ) {
1473  $found = false;
1474  foreach ( $aliases[$name] as $alias ) {
1475  $caseFoldedAlias = $this->contLang->caseFold( $alias );
1476  $caseFoldedAlias = str_replace( ' ', '_', $caseFoldedAlias );
1477  if ( isset( $aliasList[$caseFoldedAlias] ) &&
1478  $aliasList[$caseFoldedAlias] === $name
1479  ) {
1480  $name = $alias;
1481  $found = true;
1482  break;
1483  }
1484  }
1485  if ( !$found ) {
1486  wfWarn( "Did not find a usable alias for special page '$name'. " .
1487  "It seems all defined aliases conflict?" );
1488  }
1489  } else {
1490  // Check if someone misspelled the correct casing
1491  if ( is_array( $aliases ) ) {
1492  foreach ( $aliases as $n => $values ) {
1493  if ( strcasecmp( $name, $n ) === 0 ) {
1494  wfWarn( "Found alias defined for $n when searching for " .
1495  "special page aliases for $name. Case mismatch?" );
1496  return $this->getLocalNameFor( $n, $subpage );
1497  }
1498  }
1499  }
1500 
1501  wfWarn( "Did not find alias for special page '$name'. " .
1502  "Perhaps no aliases are defined for it?" );
1503  }
1504 
1505  if ( $subpage !== false && $subpage !== null ) {
1506  // Make sure it's in dbkey form
1507  $subpage = str_replace( ' ', '_', $subpage );
1508  $name = "$name/$subpage";
1509  }
1510 
1511  return $this->contLang->ucfirst( $name );
1512  }
1513 
1520  public function getTitleForAlias( $alias ) {
1521  list( $name, $subpage ) = $this->resolveAlias( $alias );
1522  if ( $name != null ) {
1523  return SpecialPage::getTitleFor( $name, $subpage );
1524  }
1525 
1526  return null;
1527  }
1528 }
1529 
1531 class_alias( SpecialPageFactory::class, 'MediaWiki\\Special\\SpecialPageFactory' );
$wgSend404Code
$wgSend404Code
Some web hosts attempt to rewrite all responses with a 404 (not found) status code,...
Definition: DefaultSettings.php:3972
MediaWiki\SpecialPage\SpecialPageFactory\capturePath
capturePath(PageReference $page, IContextSource $context, LinkRenderer $linkRenderer=null)
Just like executePath() but will override global variables and execute the page in "inclusion" mode.
Definition: SpecialPageFactory.php:1398
MediaWiki\SpecialPage\SpecialPageFactory\getUsablePages
getUsablePages(User $user)
Get listed special pages available to the current user.
Definition: SpecialPageFactory.php:1262
MediaWiki\SpecialPage\SpecialPageFactory\$options
ServiceOptions $options
Definition: SpecialPageFactory.php:968
Profiler\instance
static instance()
Singleton.
Definition: Profiler.php:69
$wgRequest
$wgRequest
Definition: Setup.php:732
MediaWiki\SpecialPage\SpecialPageFactory\CORE_LIST
const CORE_LIST
List of special page names to the subclass of SpecialPage which handles them.
Definition: SpecialPageFactory.php:67
MediaWiki\Linker\LinkRenderer
Class that generates HTML links for pages.
Definition: LinkRenderer.php:43
MediaWiki\SpecialPage\SpecialPageFactory
Factory for handling the special page list and generating SpecialPage objects.
Definition: SpecialPageFactory.php:63
MediaWiki\SpecialPage\SpecialPageFactory\resolveAlias
resolveAlias( $alias)
Given a special page name with a possible subpage, return an array where the first element is the spe...
Definition: SpecialPageFactory.php:1184
MediaWiki\SpecialPage\SpecialPageFactory\$hookRunner
HookRunner $hookRunner
Definition: SpecialPageFactory.php:980
SpecialPage\getTitleFor
static getTitleFor( $name, $subpage=false, $fragment='')
Get a localised Title object for a specified special page name If you don't need a full Title object,...
Definition: SpecialPage.php:131
wfLogWarning
wfLogWarning( $msg, $callerOffset=1, $level=E_USER_WARNING)
Send a warning as a PHP error and the debug log.
Definition: GlobalFunctions.php:1056
MediaWiki\SpecialPage\SpecialPageFactory\getLocalNameFor
getLocalNameFor( $name, $subpage=false)
Get the local name for a specified canonical name.
Definition: SpecialPageFactory.php:1467
MediaWiki\SpecialPage\SpecialPageFactory\$titleFactory
TitleFactory $titleFactory
Definition: SpecialPageFactory.php:998
MediaWiki\SpecialPage\SpecialPageFactory\$list
array $list
Special page name => class name.
Definition: SpecialPageFactory.php:962
$wgLang
$wgLang
Definition: Setup.php:861
Page\PageReference
Interface for objects (potentially) representing a page that can be viewable and linked to on a wiki.
Definition: PageReference.php:49
MediaWiki\SpecialPage\SpecialPageFactory\__construct
__construct(ServiceOptions $options, Language $contLang, ObjectFactory $objectFactory, TitleFactory $titleFactory, HookContainer $hookContainer)
Definition: SpecialPageFactory.php:1007
MediaWiki\SpecialPage\SpecialPageFactory\$contLang
Language $contLang
Definition: SpecialPageFactory.php:971
MediaWiki\Config\ServiceOptions
A class for passing options to services.
Definition: ServiceOptions.php:27
Profiler
Profiler base class that defines the interface and some shared functionality.
Definition: Profiler.php:36
$title
$title
Definition: testCompression.php:38
RequestContext
Group all the pieces relevant to the context of a request into one instance.
Definition: RequestContext.php:41
MediaWiki\SpecialPage\SpecialPageFactory\executePath
executePath( $path, IContextSource $context, $including=false, LinkRenderer $linkRenderer=null)
Execute a special page path.
Definition: SpecialPageFactory.php:1311
$wgTitle
$wgTitle
Definition: Setup.php:879
MediaWiki\SpecialPage\SpecialPageFactory\getNames
getNames()
Returns a list of canonical special page names.
Definition: SpecialPageFactory.php:1029
MediaWiki\SpecialPage\SpecialPageFactory\exists
exists( $name)
Check if a given name exist as a special page or as a special page alias.
Definition: SpecialPageFactory.php:1205
SpecialPage
Parent class for all special pages.
Definition: SpecialPage.php:43
MediaWiki\SpecialPage\SpecialPageFactory\getRegularPages
getRegularPages()
Get listed special pages available to everyone by default.
Definition: SpecialPageFactory.php:1284
MediaWiki\SpecialPage\SpecialPageFactory\$objectFactory
ObjectFactory $objectFactory
Definition: SpecialPageFactory.php:974
IContextSource\getUser
getUser()
MediaWiki\SpecialPage\SpecialPageFactory\getPage
getPage( $name)
Find the object with a given name and return it (or NULL)
Definition: SpecialPageFactory.php:1218
RequestContext\getMain
static getMain()
Get the RequestContext object associated with the main request.
Definition: RequestContext.php:484
IContextSource
Interface for objects which can provide a MediaWiki context on request.
Definition: IContextSource.php:58
Title
Represents a title within MediaWiki.
Definition: Title.php:47
IContextSource\getConfig
getConfig()
Get the site configuration.
$path
$path
Definition: NoLocalSettings.php:25
TitleFactory
Creates Title objects.
Definition: TitleFactory.php:35
MediaWiki\SpecialPage\SpecialPageFactory\$hookContainer
HookContainer $hookContainer
Definition: SpecialPageFactory.php:977
IContextSource\getRequest
getRequest()
MediaWiki\HookContainer\HookContainer
HookContainer class.
Definition: HookContainer.php:45
wfWarn
wfWarn( $msg, $callerOffset=1, $level=E_USER_NOTICE)
Send a warning either to the debug log or in a PHP error depending on $wgDevelopmentWarnings.
Definition: GlobalFunctions.php:1043
MediaWiki\HookContainer\HookRunner
This class provides an implementation of the core hook interfaces, forwarding hook calls to HookConta...
Definition: HookRunner.php:557
MediaWiki\SpecialPage\SpecialPageFactory\getTitleForAlias
getTitleForAlias( $alias)
Get a title for a given alias.
Definition: SpecialPageFactory.php:1520
MediaWiki\SpecialPage\SpecialPageFactory\CONSTRUCTOR_OPTIONS
const CONSTRUCTOR_OPTIONS
Definition: SpecialPageFactory.php:985
MediaWiki\$context
IContextSource $context
Definition: MediaWiki.php:40
$wgOut
$wgOut
Definition: Setup.php:866
MediaWiki\SpecialPage\SpecialPageFactory\$aliases
array $aliases
Definition: SpecialPageFactory.php:965
MediaWiki\SpecialPage\SpecialPageFactory\getAliasList
getAliasList()
Initialise and return the list of special page aliases.
Definition: SpecialPageFactory.php:1128
MediaWiki\SpecialPage\SpecialPageFactory\getPageList
getPageList()
Get the special page list as an array.
Definition: SpecialPageFactory.php:1038
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:67
MediaWiki\SpecialPage
IContextSource\getOutput
getOutput()
Language
Internationalisation code See https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation for more...
Definition: Language.php:42
IContextSource\getLanguage
getLanguage()
MediaWiki\Config\ServiceOptions\assertRequiredOptions
assertRequiredOptions(array $expectedKeys)
Assert that the list of options provided in this instance exactly match $expectedKeys,...
Definition: ServiceOptions.php:71