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