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  ]
148  ],
149  'Protectedtitles' => [
150  'class' => \SpecialProtectedtitles::class,
151  'services' => [
152  'LinkBatchFactory',
153  'DBLoadBalancer',
154  ]
155  ],
156  'Shortpages' => [
157  'class' => \SpecialShortPages::class,
158  'services' => [
159  // Same as for Longpages
160  'NamespaceInfo',
161  'DBLoadBalancer',
162  'LinkBatchFactory',
163  ]
164  ],
165  'Uncategorizedcategories' => [
166  'class' => \SpecialUncategorizedCategories::class,
167  'services' => [
168  // Same as for SpecialUncategorizedPages and SpecialUncategorizedTemplates
169  'NamespaceInfo',
170  'DBLoadBalancer',
171  'LinkBatchFactory',
172  'LanguageConverterFactory',
173  ]
174  ],
175  'Uncategorizedimages' => [
176  'class' => \SpecialUncategorizedImages::class,
177  'services' => [
178  'DBLoadBalancer',
179  ]
180  ],
181  'Uncategorizedpages' => [
182  'class' => \SpecialUncategorizedPages::class,
183  'services' => [
184  // Same as for SpecialUncategorizedCategories and SpecialUncategorizedTemplates
185  'NamespaceInfo',
186  'DBLoadBalancer',
187  'LinkBatchFactory',
188  'LanguageConverterFactory',
189  ]
190  ],
191  'Uncategorizedtemplates' => [
192  'class' => \SpecialUncategorizedTemplates::class,
193  'services' => [
194  // Same as for SpecialUncategorizedCategories and SpecialUncategorizedPages
195  'NamespaceInfo',
196  'DBLoadBalancer',
197  'LinkBatchFactory',
198  'LanguageConverterFactory',
199  ]
200  ],
201  'Unusedcategories' => [
202  'class' => \SpecialUnusedCategories::class,
203  'services' => [
204  'DBLoadBalancer',
205  'LinkBatchFactory',
206  ]
207  ],
208  'Unusedimages' => [
209  'class' => \SpecialUnusedImages::class,
210  'services' => [
211  'DBLoadBalancer',
212  ]
213  ],
214  'Unusedtemplates' => [
215  'class' => \SpecialUnusedTemplates::class,
216  'services' => [
217  'DBLoadBalancer',
218  ]
219  ],
220  'Unwatchedpages' => [
221  'class' => \SpecialUnwatchedPages::class,
222  'services' => [
223  'LinkBatchFactory',
224  'DBLoadBalancer',
225  'LanguageConverterFactory',
226  ]
227  ],
228  'Wantedcategories' => [
229  'class' => \SpecialWantedCategories::class,
230  'services' => [
231  'DBLoadBalancer',
232  'LinkBatchFactory',
233  'LanguageConverterFactory',
234  ]
235  ],
236  'Wantedfiles' => [
237  'class' => \WantedFilesPage::class,
238  'services' => [
239  'RepoGroup',
240  'DBLoadBalancer',
241  'LinkBatchFactory',
242  ]
243  ],
244  'Wantedpages' => [
245  'class' => \WantedPagesPage::class,
246  'services' => [
247  'DBLoadBalancer',
248  'LinkBatchFactory',
249  ]
250  ],
251  'Wantedtemplates' => [
252  'class' => \SpecialWantedTemplates::class,
253  'services' => [
254  'DBLoadBalancer',
255  'LinkBatchFactory',
256  ]
257  ],
258 
259  // List of pages
260  'Allpages' => [
261  'class' => \SpecialAllPages::class,
262  'services' => [
263  'DBLoadBalancer',
264  'SearchEngineFactory',
265  ]
266  ],
267  'Prefixindex' => [
268  'class' => \SpecialPrefixindex::class,
269  'services' => [
270  'DBLoadBalancer',
271  'LinkCache',
272  ]
273  ],
274  'Categories' => [
275  'class' => \SpecialCategories::class,
276  'services' => [
277  'LinkBatchFactory',
278  'DBLoadBalancer',
279  ]
280  ],
281  'Listredirects' => [
282  'class' => \SpecialListRedirects::class,
283  'services' => [
284  'LinkBatchFactory',
285  'DBLoadBalancer',
286  'WikiPageFactory',
287  ]
288  ],
289  'PagesWithProp' => [
290  'class' => \SpecialPagesWithProp::class,
291  'services' => [
292  'DBLoadBalancer',
293  ]
294  ],
295  'TrackingCategories' => [
296  'class' => \SpecialTrackingCategories::class,
297  'services' => [
298  'LinkBatchFactory',
299  ]
300  ],
301 
302  // Authentication
303  'Userlogin' => [
304  'class' => \SpecialUserLogin::class,
305  'services' => [
306  'AuthManager',
307  ]
308  ],
309  'Userlogout' => [
310  'class' => \SpecialUserLogout::class,
311  ],
312  'CreateAccount' => [
313  'class' => \SpecialCreateAccount::class,
314  'services' => [
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  'BlockActionInfo',
361  'TitleFormatter',
362  ]
363  ],
364  'Unblock' => [
365  'class' => \SpecialUnblock::class,
366  'services' => [
367  'UnblockUserFactory',
368  'BlockUtils',
369  'UserNameUtils',
370  'UserNamePrefixSearch',
371  ]
372  ],
373  'BlockList' => [
374  'class' => \SpecialBlockList::class,
375  'services' => [
376  'LinkBatchFactory',
377  'BlockRestrictionStore',
378  'DBLoadBalancer',
379  'CommentStore',
380  'BlockUtils',
381  'BlockActionInfo',
382  ],
383  ],
384  'AutoblockList' => [
385  'class' => \SpecialAutoblockList::class,
386  'services' => [
387  'LinkBatchFactory',
388  'BlockRestrictionStore',
389  'DBLoadBalancer',
390  'CommentStore',
391  'BlockUtils',
392  'BlockActionInfo',
393  ],
394  ],
395  'ChangePassword' => [
396  'class' => \SpecialChangePassword::class,
397  ],
398  'BotPasswords' => [
399  'class' => \SpecialBotPasswords::class,
400  'services' => [
401  'PasswordFactory',
402  'AuthManager',
403  'CentralIdLookup',
404  ]
405  ],
406  'PasswordReset' => [
407  'class' => \SpecialPasswordReset::class,
408  'services' => [
409  'PasswordReset'
410  ]
411  ],
412  'DeletedContributions' => [
413  'class' => \SpecialDeletedContributions::class,
414  'services' => [
415  'PermissionManager',
416  'DBLoadBalancer',
417  'CommentStore',
418  'RevisionFactory',
419  'NamespaceInfo',
420  'UserNameUtils',
421  'UserNamePrefixSearch',
422  ]
423  ],
424  'Preferences' => [
425  'class' => \SpecialPreferences::class,
426  'services' => [
427  'PreferencesFactory',
428  'UserOptionsManager',
429  ]
430  ],
431  'ResetTokens' => [
432  'class' => \SpecialResetTokens::class,
433  ],
434  'Contributions' => [
435  'class' => \SpecialContributions::class,
436  'services' => [
437  'LinkBatchFactory',
438  'PermissionManager',
439  'DBLoadBalancer',
440  'ActorMigration',
441  'RevisionStore',
442  'NamespaceInfo',
443  'UserNameUtils',
444  'UserNamePrefixSearch',
445  'UserOptionsLookup',
446  ]
447  ],
448  'Listgrouprights' => [
449  'class' => \SpecialListGroupRights::class,
450  'services' => [
451  'NamespaceInfo',
452  'UserGroupManager',
453  'LanguageConverterFactory',
454  ]
455  ],
456  'Listgrants' => [
457  'class' => \SpecialListGrants::class,
458  ],
459  'Listusers' => [
460  'class' => \SpecialListUsers::class,
461  'services' => [
462  'LinkBatchFactory',
463  'DBLoadBalancer',
464  'UserGroupManager',
465  ]
466  ],
467  'Listadmins' => [
468  'class' => \SpecialListAdmins::class,
469  ],
470  'Listbots' => [
471  'class' => \SpecialListBots::class,
472  ],
473  'Userrights' => [
474  'class' => \UserrightsPage::class,
475  'services' => [
476  'UserGroupManagerFactory',
477  'UserNameUtils',
478  'UserNamePrefixSearch',
479  ]
480  ],
481  'EditWatchlist' => [
482  'class' => \SpecialEditWatchlist::class,
483  'services' => [
484  'WatchedItemStore',
485  'TitleParser',
486  'GenderCache',
487  'LinkBatchFactory',
488  'NamespaceInfo',
489  'WikiPageFactory',
490  'WatchlistManager',
491  ]
492  ],
493  'PasswordPolicies' => [
494  'class' => \SpecialPasswordPolicies::class
495  ],
496 
497  // Recent changes and logs
498  'Newimages' => [
499  'class' => \SpecialNewFiles::class,
500  'services' => [
501  'MimeAnalyzer',
502  'GroupPermissionsLookup',
503  'DBLoadBalancer',
504  'LinkBatchFactory',
505  ]
506  ],
507  'Log' => [
508  'class' => \SpecialLog::class,
509  'services' => [
510  'LinkBatchFactory',
511  'DBLoadBalancer',
512  'ActorNormalization',
513  ]
514  ],
515  'Watchlist' => [
516  'class' => \SpecialWatchlist::class,
517  'services' => [
518  'WatchedItemStore',
519  'WatchlistManager',
520  'DBLoadBalancer',
521  'UserOptionsLookup',
522  ]
523  ],
524  'Newpages' => [
525  'class' => \SpecialNewpages::class,
526  'services' => [
527  'LinkBatchFactory',
528  'CommentStore',
529  'ContentHandlerFactory',
530  'GroupPermissionsLookup',
531  'DBLoadBalancer',
532  'RevisionLookup',
533  'NamespaceInfo',
534  'UserOptionsLookup',
535  ]
536  ],
537  'Recentchanges' => [
538  'class' => \SpecialRecentChanges::class,
539  'services' => [
540  'WatchedItemStore',
541  'MessageCache',
542  'DBLoadBalancer',
543  'UserOptionsLookup',
544  ]
545  ],
546  'Recentchangeslinked' => [
547  'class' => \SpecialRecentChangesLinked::class,
548  'services' => [
549  'WatchedItemStore',
550  'MessageCache',
551  'DBLoadBalancer',
552  'UserOptionsLookup',
553  'SearchEngineFactory',
554  ]
555  ],
556  'Tags' => [
557  'class' => \SpecialTags::class,
558  ],
559 
560  // Media reports and uploads
561  'Listfiles' => [
562  'class' => \SpecialListFiles::class,
563  'services' => [
564  'RepoGroup',
565  'DBLoadBalancer',
566  'CommentStore',
567  'UserNameUtils',
568  'UserNamePrefixSearch',
569  'UserCache',
570  ]
571  ],
572  'Filepath' => [
573  'class' => \SpecialFilepath::class,
574  ],
575  'MediaStatistics' => [
576  'class' => \SpecialMediaStatistics::class,
577  'services' => [
578  'MimeAnalyzer',
579  'DBLoadBalancer',
580  'LinkBatchFactory',
581  ]
582  ],
583  'MIMEsearch' => [
584  'class' => \SpecialMIMESearch::class,
585  'services' => [
586  'DBLoadBalancer',
587  'LinkBatchFactory',
588  'LanguageConverterFactory',
589  ]
590  ],
591  'FileDuplicateSearch' => [
592  'class' => \SpecialFileDuplicateSearch::class,
593  'services' => [
594  'LinkBatchFactory',
595  'RepoGroup',
596  'SearchEngineFactory',
597  'LanguageConverterFactory',
598  ]
599  ],
600  'Upload' => [
601  'class' => \SpecialUpload::class,
602  'services' => [
603  'RepoGroup',
604  'UserOptionsLookup',
605  'NamespaceInfo',
606  ]
607  ],
608  'UploadStash' => [
609  'class' => \SpecialUploadStash::class,
610  'services' => [
611  'RepoGroup',
612  'HttpRequestFactory',
613  ]
614  ],
615  'ListDuplicatedFiles' => [
616  'class' => \SpecialListDuplicatedFiles::class,
617  'services' => [
618  'DBLoadBalancer',
619  'LinkBatchFactory',
620  ]
621  ],
622 
623  // Data and tools
624  'ApiSandbox' => [
625  'class' => \SpecialApiSandbox::class,
626  ],
627  'Statistics' => [
628  'class' => \SpecialStatistics::class
629  ],
630  'Allmessages' => [
631  'class' => \SpecialAllMessages::class,
632  'services' => [
633  'LocalisationCache',
634  'DBLoadBalancer',
635  ]
636  ],
637  'Version' => [
638  'class' => \SpecialVersion::class,
639  'services' => [
640  'Parser',
641  ]
642  ],
643  'Lockdb' => [
644  'class' => \SpecialLockdb::class,
645  ],
646  'Unlockdb' => [
647  'class' => \SpecialUnlockdb::class,
648  ],
649 
650  // Redirecting special pages
651  'LinkSearch' => [
652  'class' => \SpecialLinkSearch::class,
653  'services' => [
654  'DBLoadBalancer',
655  'LinkBatchFactory',
656  ]
657  ],
658  'Randompage' => [
659  'class' => \RandomPage::class,
660  'services' => [
661  'DBLoadBalancer',
662  'NamespaceInfo',
663  ]
664  ],
665  'RandomInCategory' => [
666  'class' => \SpecialRandomInCategory::class,
667  'services' => [
668  'DBLoadBalancer',
669  ]
670  ],
671  'Randomredirect' => [
672  'class' => \SpecialRandomredirect::class,
673  'services' => [
674  'DBLoadBalancer',
675  'NamespaceInfo',
676  ]
677  ],
678  'Randomrootpage' => [
679  'class' => \SpecialRandomrootpage::class,
680  'services' => [
681  'DBLoadBalancer',
682  'NamespaceInfo',
683  ]
684  ],
685  'GoToInterwiki' => [
686  'class' => \SpecialGoToInterwiki::class,
687  ],
688 
689  // High use pages
690  'Mostlinkedcategories' => [
691  'class' => \SpecialMostLinkedCategories::class,
692  'services' => [
693  'DBLoadBalancer',
694  'LinkBatchFactory',
695  'LanguageConverterFactory',
696  ]
697  ],
698  'Mostimages' => [
699  'class' => \MostimagesPage::class,
700  'services' => [
701  'DBLoadBalancer',
702  'LanguageConverterFactory',
703  ]
704  ],
705  'Mostinterwikis' => [
706  'class' => \SpecialMostInterwikis::class,
707  'services' => [
708  'NamespaceInfo',
709  'DBLoadBalancer',
710  'LinkBatchFactory',
711  ]
712  ],
713  'Mostlinked' => [
714  'class' => \SpecialMostLinked::class,
715  'services' => [
716  'DBLoadBalancer',
717  'LinkBatchFactory',
718  ]
719  ],
720  'Mostlinkedtemplates' => [
721  'class' => \SpecialMostLinkedTemplates::class,
722  'services' => [
723  'DBLoadBalancer',
724  'LinkBatchFactory',
725  ]
726  ],
727  'Mostcategories' => [
728  'class' => \SpecialMostCategories::class,
729  'services' => [
730  'NamespaceInfo',
731  'DBLoadBalancer',
732  'LinkBatchFactory',
733  ]
734  ],
735  'Mostrevisions' => [
736  'class' => \SpecialMostRevisions::class,
737  'services' => [
738  // Same as for Fewestrevisions
739  'NamespaceInfo',
740  'DBLoadBalancer',
741  'LinkBatchFactory',
742  'LanguageConverterFactory',
743  ]
744  ],
745 
746  // Page tools
747  'ComparePages' => [
748  'class' => \SpecialComparePages::class,
749  'services' => [
750  'RevisionLookup',
751  'ContentHandlerFactory',
752  ]
753  ],
754  'Export' => [
755  'class' => \SpecialExport::class,
756  'services' => [
757  'DBLoadBalancer',
758  ]
759  ],
760  'Import' => [
761  'class' => \SpecialImport::class,
762  'services' => [
763  'PermissionManager',
764  'WikiImporterFactory',
765  ]
766  ],
767  'Undelete' => [
768  'class' => \SpecialUndelete::class,
769  'services' => [
770  'PermissionManager',
771  'RevisionStore',
772  'RevisionRenderer',
773  'ContentHandlerFactory',
774  'ChangeTagDefStore',
775  'LinkBatchFactory',
776  'RepoGroup',
777  'DBLoadBalancer',
778  'UserOptionsLookup',
779  'WikiPageFactory',
780  'SearchEngineFactory',
781  ],
782  ],
783  'Whatlinkshere' => [
784  'class' => \SpecialWhatLinksHere::class,
785  'services' => [
786  'DBLoadBalancer',
787  'LinkBatchFactory',
788  'ContentHandlerFactory',
789  'SearchEngineFactory',
790  'NamespaceInfo',
791  ]
792  ],
793  'MergeHistory' => [
794  'class' => \SpecialMergeHistory::class,
795  'services' => [
796  'MergeHistoryFactory',
797  'LinkBatchFactory',
798  'DBLoadBalancer',
799  'RevisionStore',
800  ]
801  ],
802  'ExpandTemplates' => [
803  'class' => \SpecialExpandTemplates::class,
804  'services' => [
805  'Parser',
806  'UserOptionsLookup',
807  'Tidy',
808  ],
809  ],
810  'ChangeContentModel' => [
811  'class' => \SpecialChangeContentModel::class,
812  'services' => [
813  'ContentHandlerFactory',
814  'ContentModelChangeFactory',
815  'SpamChecker',
816  'RevisionLookup',
817  'WikiPageFactory',
818  'SearchEngineFactory',
819  ],
820  ],
821 
822  // Other
823  'Booksources' => [
824  'class' => \SpecialBookSources::class,
825  'services' => [
826  'RevisionLookup',
827  ]
828  ],
829 
830  // Unlisted / redirects
831  'ApiHelp' => [
832  'class' => \SpecialApiHelp::class,
833  ],
834  'Blankpage' => [
835  'class' => \SpecialBlankpage::class,
836  ],
837  'Diff' => [
838  'class' => \SpecialDiff::class,
839  ],
840  'EditPage' => [
841  'class' => \SpecialEditPage::class,
842  ],
843  'EditTags' => [
844  'class' => \SpecialEditTags::class,
845  'services' => [
846  'PermissionManager',
847  ],
848  ],
849  'Emailuser' => [
850  'class' => \SpecialEmailUser::class,
851  'services' => [
852  'UserNameUtils',
853  'UserNamePrefixSearch',
854  'UserOptionsLookup',
855  ]
856  ],
857  'Movepage' => [
858  'class' => \MovePageForm::class,
859  'services' => [
860  'MovePageFactory',
861  'PermissionManager',
862  'UserOptionsLookup',
863  'DBLoadBalancer',
864  'ContentHandlerFactory',
865  'NamespaceInfo',
866  'LinkBatchFactory',
867  'RepoGroup',
868  'WikiPageFactory',
869  'SearchEngineFactory',
870  'WatchlistManager',
871  ]
872  ],
873  'Mycontributions' => [
874  'class' => \SpecialMycontributions::class,
875  ],
876  'MyLanguage' => [
877  'class' => \SpecialMyLanguage::class,
878  'services' => [
879  'LanguageNameUtils',
880  'WikiPageFactory',
881  ]
882  ],
883  'Mypage' => [
884  'class' => \SpecialMypage::class,
885  ],
886  'Mytalk' => [
887  'class' => \SpecialMytalk::class,
888  ],
889  'PageHistory' => [
890  'class' => \SpecialPageHistory::class,
891  ],
892  'PageInfo' => [
893  'class' => \SpecialPageInfo::class,
894  ],
895  'Purge' => [
896  'class' => \SpecialPurge::class,
897  ],
898  'Myuploads' => [
899  'class' => \SpecialMyuploads::class,
900  ],
901  'AllMyUploads' => [
902  'class' => \SpecialAllMyUploads::class,
903  ],
904  'NewSection' => [
905  'class' => \SpecialNewSection::class,
906  ],
907  'PermanentLink' => [
908  'class' => \SpecialPermanentLink::class,
909  ],
910  'Redirect' => [
911  'class' => \SpecialRedirect::class,
912  'services' => [
913  'RepoGroup',
914  'UserFactory',
915  ]
916  ],
917  'Revisiondelete' => [
918  'class' => \SpecialRevisionDelete::class,
919  'services' => [
920  'PermissionManager',
921  'RepoGroup',
922  ],
923  ],
924  'RunJobs' => [
925  'class' => \SpecialRunJobs::class,
926  'services' => [
927  'JobRunner',
928  'ReadOnlyMode',
929  ]
930  ],
931  'Specialpages' => [
932  'class' => \SpecialSpecialpages::class,
933  ],
934  'PageData' => [
935  'class' => \SpecialPageData::class,
936  ],
937  ];
938 
940  private $list;
941 
943  private $aliases;
944 
946  private $options;
947 
949  private $contLang;
950 
952  private $objectFactory;
953 
955  private $hookContainer;
956 
958  private $hookRunner;
959 
963  public const CONSTRUCTOR_OPTIONS = [
964  'DisableInternalSearch',
965  'EmailAuthentication',
966  'EnableEmail',
967  'EnableJavaScriptTest',
968  'EnableSpecialMute',
969  'PageLanguageUseDB',
970  'SpecialPages',
971  ];
972 
976  private $titleFactory;
977 
985  public function __construct(
988  ObjectFactory $objectFactory,
991  ) {
992  $options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS );
993  $this->options = $options;
994  $this->contLang = $contLang;
995  $this->objectFactory = $objectFactory;
996  $this->titleFactory = $titleFactory;
997  $this->hookContainer = $hookContainer;
998  $this->hookRunner = new HookRunner( $hookContainer );
999  }
1000 
1007  public function getNames(): array {
1008  return array_keys( $this->getPageList() );
1009  }
1010 
1016  private function getPageList(): array {
1017  if ( !is_array( $this->list ) ) {
1018  $this->list = self::CORE_LIST;
1019 
1020  if ( !$this->options->get( 'DisableInternalSearch' ) ) {
1021  $this->list['Search'] = [
1022  'class' => \SpecialSearch::class,
1023  'services' => [
1024  'SearchEngineConfig',
1025  'SearchEngineFactory',
1026  'NamespaceInfo',
1027  'ContentHandlerFactory',
1028  'InterwikiLookup',
1029  'ReadOnlyMode',
1030  'UserOptionsManager',
1031  'LanguageConverterFactory'
1032  ]
1033  ];
1034  }
1035 
1036  if ( $this->options->get( 'EmailAuthentication' ) ) {
1037  $this->list['Confirmemail'] = [
1038  'class' => \SpecialConfirmEmail::class,
1039  'services' => [
1040  'UserFactory',
1041  ]
1042  ];
1043  $this->list['Invalidateemail'] = [
1044  'class' => \SpecialEmailInvalidate::class,
1045  'services' => [
1046  'UserFactory',
1047  ]
1048  ];
1049  }
1050 
1051  if ( $this->options->get( 'EnableEmail' ) ) {
1052  $this->list['ChangeEmail'] = [
1053  'class' => \SpecialChangeEmail::class,
1054  'services' => [
1055  'AuthManager',
1056  ],
1057  ];
1058  }
1059 
1060  if ( $this->options->get( 'EnableJavaScriptTest' ) ) {
1061  $this->list['JavaScriptTest'] = [
1062  'class' => \SpecialJavaScriptTest::class
1063  ];
1064  }
1065 
1066  if ( $this->options->get( 'EnableSpecialMute' ) ) {
1067  $this->list['Mute'] = [
1068  'class' => \SpecialMute::class,
1069  'services' => [
1070  'CentralIdLookup',
1071  'UserOptionsManager',
1072  'UserIdentityLookup',
1073  ]
1074  ];
1075  }
1076 
1077  if ( $this->options->get( 'PageLanguageUseDB' ) ) {
1078  $this->list['PageLanguage'] = [
1079  'class' => \SpecialPageLanguage::class,
1080  'services' => [
1081  'ContentHandlerFactory',
1082  'LanguageNameUtils',
1083  'DBLoadBalancer',
1084  'SearchEngineFactory',
1085  ]
1086  ];
1087  }
1088 
1089  // Add extension special pages
1090  $this->list = array_merge( $this->list, $this->options->get( 'SpecialPages' ) );
1091 
1092  // This hook can be used to disable unwanted core special pages
1093  // or conditionally register special pages.
1094  $this->hookRunner->onSpecialPage_initList( $this->list );
1095  }
1096 
1097  return $this->list;
1098  }
1099 
1106  private function getAliasList(): array {
1107  if ( $this->aliases === null ) {
1108  $aliases = $this->contLang->getSpecialPageAliases();
1109  $pageList = $this->getPageList();
1110 
1111  $this->aliases = [];
1112  $keepAlias = [];
1113 
1114  // Force every canonical name to be an alias for itself.
1115  foreach ( $pageList as $name => $stuff ) {
1116  $caseFoldedAlias = $this->contLang->caseFold( $name );
1117  $this->aliases[$caseFoldedAlias] = $name;
1118  $keepAlias[$caseFoldedAlias] = 'canonical';
1119  }
1120 
1121  // Check for $aliases being an array since Language::getSpecialPageAliases can return null
1122  if ( is_array( $aliases ) ) {
1123  foreach ( $aliases as $realName => $aliasList ) {
1124  $aliasList = array_values( $aliasList );
1125  foreach ( $aliasList as $i => $alias ) {
1126  $caseFoldedAlias = $this->contLang->caseFold( $alias );
1127 
1128  if ( isset( $this->aliases[$caseFoldedAlias] ) &&
1129  $realName === $this->aliases[$caseFoldedAlias]
1130  ) {
1131  // Ignore same-realName conflicts
1132  continue;
1133  }
1134 
1135  if ( !isset( $keepAlias[$caseFoldedAlias] ) ) {
1136  $this->aliases[$caseFoldedAlias] = $realName;
1137  if ( !$i ) {
1138  $keepAlias[$caseFoldedAlias] = 'first';
1139  }
1140  } elseif ( !$i ) {
1141  wfWarn( "First alias '$alias' for $realName conflicts with " .
1142  "{$keepAlias[$caseFoldedAlias]} alias for " .
1143  $this->aliases[$caseFoldedAlias]
1144  );
1145  }
1146  }
1147  }
1148  }
1149  }
1150 
1151  return $this->aliases;
1152  }
1153 
1162  public function resolveAlias( $alias ) {
1163  $bits = explode( '/', $alias, 2 );
1164 
1165  $caseFoldedAlias = $this->contLang->caseFold( $bits[0] );
1166  $caseFoldedAlias = str_replace( ' ', '_', $caseFoldedAlias );
1167  $aliases = $this->getAliasList();
1168  if ( !isset( $aliases[$caseFoldedAlias] ) ) {
1169  return [ null, null ];
1170  }
1171  $name = $aliases[$caseFoldedAlias];
1172  $par = $bits[1] ?? null; // T4087
1173 
1174  return [ $name, $par ];
1175  }
1176 
1183  public function exists( $name ) {
1184  list( $title, /*...*/ ) = $this->resolveAlias( $name );
1185 
1186  $specialPageList = $this->getPageList();
1187  return isset( $specialPageList[$title] );
1188  }
1189 
1196  public function getPage( $name ) {
1197  list( $realName, /*...*/ ) = $this->resolveAlias( $name );
1198 
1199  $specialPageList = $this->getPageList();
1200 
1201  if ( isset( $specialPageList[$realName] ) ) {
1202  $rec = $specialPageList[$realName];
1203 
1204  if ( $rec instanceof SpecialPage ) {
1206  "A SpecialPage instance for $realName was found in " .
1207  '$wgSpecialPages or came from a SpecialPage_initList hook handler, ' .
1208  'this was deprecated in MediaWiki 1.34',
1209  '1.34'
1210  );
1211 
1212  $page = $rec; // XXX: we should deep clone here
1213  } elseif ( is_array( $rec ) || is_string( $rec ) || is_callable( $rec ) ) {
1214  $page = $this->objectFactory->createObject(
1215  $rec,
1216  [
1217  'allowClassName' => true,
1218  'allowCallable' => true
1219  ]
1220  );
1221  } else {
1222  $page = null;
1223  }
1224 
1225  if ( $page instanceof SpecialPage ) {
1226  $page->setHookContainer( $this->hookContainer );
1227  $page->setContentLanguage( $this->contLang );
1228  $page->setSpecialPageFactory( $this );
1229  return $page;
1230  }
1231 
1232  // It's not a classname, nor a callback, nor a legacy constructor array,
1233  // nor a special page object. Give up.
1234  wfLogWarning( "Cannot instantiate special page $realName: bad spec!" );
1235  }
1236 
1237  return null;
1238  }
1239 
1249  public function getUsablePages( User $user ): array {
1250  $pages = [];
1251  foreach ( $this->getPageList() as $name => $rec ) {
1252  $page = $this->getPage( $name );
1253  if ( $page ) { // not null
1254  $page->setContext( RequestContext::getMain() );
1255  if ( $page->isListed()
1256  && ( !$page->isRestricted() || $page->userCanExecute( $user ) )
1257  ) {
1258  $pages[$name] = $page;
1259  }
1260  }
1261  }
1262 
1263  return $pages;
1264  }
1265 
1271  public function getRegularPages(): array {
1272  $pages = [];
1273  foreach ( $this->getPageList() as $name => $rec ) {
1274  $page = $this->getPage( $name );
1275  if ( $page && $page->isListed() && !$page->isRestricted() ) {
1276  $pages[$name] = $page;
1277  }
1278  }
1279 
1280  return $pages;
1281  }
1282 
1298  public function executePath( $path, IContextSource $context, $including = false,
1299  LinkRenderer $linkRenderer = null
1300  ) {
1301  if ( $path instanceof PageReference ) {
1302  $path = $path->getDBkey();
1303  }
1304 
1305  $bits = explode( '/', $path, 2 );
1306  $name = $bits[0];
1307  $par = $bits[1] ?? null; // T4087
1308 
1309  $page = $this->getPage( $name );
1310  if ( !$page ) {
1311  $context->getOutput()->setArticleRelated( false );
1312  $context->getOutput()->setRobotPolicy( 'noindex,nofollow' );
1313 
1314  global $wgSend404Code;
1315  if ( $wgSend404Code ) {
1316  $context->getOutput()->setStatusCode( 404 );
1317  }
1318 
1319  $context->getOutput()->showErrorPage( 'nosuchspecialpage', 'nospecialpagetext' );
1320 
1321  return false;
1322  }
1323 
1324  if ( !$including ) {
1325  // Narrow DB query expectations for this HTTP request
1326  $trxLimits = $context->getConfig()->get( 'TrxProfilerLimits' );
1327  $trxProfiler = Profiler::instance()->getTransactionProfiler();
1328  if ( $context->getRequest()->wasPosted() && !$page->doesWrites() ) {
1329  $trxProfiler->setExpectations( $trxLimits['POST-nonwrite'], __METHOD__ );
1330  $context->getRequest()->markAsSafeRequest();
1331  }
1332  }
1333 
1334  // Page exists, set the context
1335  $page->setContext( $context );
1336 
1337  if ( !$including ) {
1338  // Redirect to canonical alias for GET commands
1339  // Not for POST, we'd lose the post data, so it's best to just distribute
1340  // the request. Such POST requests are possible for old extensions that
1341  // generate self-links without being aware that their default name has
1342  // changed.
1343  if ( $name != $page->getLocalName() && !$context->getRequest()->wasPosted() ) {
1344  $query = $context->getRequest()->getQueryValues();
1345  unset( $query['title'] );
1346  $title = $page->getPageTitle( $par );
1347  $url = $title->getFullURL( $query );
1348  $context->getOutput()->redirect( $url );
1349 
1350  return $title;
1351  }
1352 
1353  // @phan-suppress-next-line PhanUndeclaredMethod
1354  $context->setTitle( $page->getPageTitle( $par ) );
1355  } elseif ( !$page->isIncludable() ) {
1356  return false;
1357  }
1358 
1359  $page->including( $including );
1360  if ( $linkRenderer ) {
1361  $page->setLinkRenderer( $linkRenderer );
1362  }
1363 
1364  // Execute special page
1365  $page->run( $par );
1366 
1367  return true;
1368  }
1369 
1385  public function capturePath(
1386  PageReference $page, IContextSource $context, LinkRenderer $linkRenderer = null
1387  ) {
1388  global $wgTitle, $wgOut, $wgRequest, $wgUser, $wgLang;
1389  $main = RequestContext::getMain();
1390 
1391  // Save current globals and main context
1392  $glob = [
1393  'title' => $wgTitle,
1394  'output' => $wgOut,
1395  'request' => $wgRequest,
1396  'user' => $wgUser,
1397  'language' => $wgLang,
1398  ];
1399  $ctx = [
1400  'title' => $main->getTitle(),
1401  'output' => $main->getOutput(),
1402  'request' => $main->getRequest(),
1403  'user' => $main->getUser(),
1404  'language' => $main->getLanguage(),
1405  ];
1406  if ( $main->canUseWikiPage() ) {
1407  $ctx['wikipage'] = $main->getWikiPage();
1408  }
1409 
1410  // just needed for $wgTitle and RequestContext::setTitle
1411  $title = $this->titleFactory->castFromPageReference( $page );
1412 
1413  // Override
1414  $wgTitle = $title;
1415  $wgOut = $context->getOutput();
1417  $wgUser = $context->getUser();
1419  $main->setTitle( $title );
1420  $main->setOutput( $context->getOutput() );
1421  $main->setRequest( $context->getRequest() );
1422  $main->setUser( $context->getUser() );
1423  $main->setLanguage( $context->getLanguage() );
1424 
1425  // The useful part
1426  $ret = $this->executePath( $page, $context, true, $linkRenderer );
1427 
1428  // Restore old globals and context
1429  $wgTitle = $glob['title'];
1430  $wgOut = $glob['output'];
1431  $wgRequest = $glob['request'];
1432  $wgUser = $glob['user'];
1433  $wgLang = $glob['language'];
1434  $main->setTitle( $ctx['title'] );
1435  $main->setOutput( $ctx['output'] );
1436  $main->setRequest( $ctx['request'] );
1437  $main->setUser( $ctx['user'] );
1438  $main->setLanguage( $ctx['language'] );
1439  if ( isset( $ctx['wikipage'] ) ) {
1440  $main->setWikiPage( $ctx['wikipage'] );
1441  }
1442 
1443  return $ret;
1444  }
1445 
1453  public function getLocalNameFor( $name, $subpage = false ) {
1454  $aliases = $this->contLang->getSpecialPageAliases();
1455  $aliasList = $this->getAliasList();
1456 
1457  // Find the first alias that maps back to $name
1458  if ( isset( $aliases[$name] ) ) {
1459  $found = false;
1460  foreach ( $aliases[$name] as $alias ) {
1461  $caseFoldedAlias = $this->contLang->caseFold( $alias );
1462  $caseFoldedAlias = str_replace( ' ', '_', $caseFoldedAlias );
1463  if ( isset( $aliasList[$caseFoldedAlias] ) &&
1464  $aliasList[$caseFoldedAlias] === $name
1465  ) {
1466  $name = $alias;
1467  $found = true;
1468  break;
1469  }
1470  }
1471  if ( !$found ) {
1472  wfWarn( "Did not find a usable alias for special page '$name'. " .
1473  "It seems all defined aliases conflict?" );
1474  }
1475  } else {
1476  // Check if someone misspelled the correct casing
1477  if ( is_array( $aliases ) ) {
1478  foreach ( $aliases as $n => $values ) {
1479  if ( strcasecmp( $name, $n ) === 0 ) {
1480  wfWarn( "Found alias defined for $n when searching for " .
1481  "special page aliases for $name. Case mismatch?" );
1482  return $this->getLocalNameFor( $n, $subpage );
1483  }
1484  }
1485  }
1486 
1487  wfWarn( "Did not find alias for special page '$name'. " .
1488  "Perhaps no aliases are defined for it?" );
1489  }
1490 
1491  if ( $subpage !== false && $subpage !== null ) {
1492  // Make sure it's in dbkey form
1493  $subpage = str_replace( ' ', '_', $subpage );
1494  $name = "$name/$subpage";
1495  }
1496 
1497  return $this->contLang->ucfirst( $name );
1498  }
1499 
1506  public function getTitleForAlias( $alias ) {
1507  list( $name, $subpage ) = $this->resolveAlias( $alias );
1508  if ( $name != null ) {
1509  return SpecialPage::getTitleFor( $name, $subpage );
1510  }
1511 
1512  return null;
1513  }
1514 }
1515 
1517 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:1385
MediaWiki\SpecialPage\SpecialPageFactory\getUsablePages
getUsablePages(User $user)
Get listed special pages available to the current user.
Definition: SpecialPageFactory.php:1249
MediaWiki\SpecialPage\SpecialPageFactory\$options
ServiceOptions $options
Definition: SpecialPageFactory.php:946
Profiler\instance
static instance()
Singleton.
Definition: Profiler.php:69
$wgRequest
$wgRequest
Definition: Setup.php:681
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:1162
MediaWiki\SpecialPage\SpecialPageFactory\$hookRunner
HookRunner $hookRunner
Definition: SpecialPageFactory.php:958
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:1056
MediaWiki\SpecialPage\SpecialPageFactory\getLocalNameFor
getLocalNameFor( $name, $subpage=false)
Get the local name for a specified canonical name.
Definition: SpecialPageFactory.php:1453
MediaWiki\SpecialPage\SpecialPageFactory\$titleFactory
TitleFactory $titleFactory
Definition: SpecialPageFactory.php:976
MediaWiki\SpecialPage\SpecialPageFactory\$list
array $list
Special page name => class name.
Definition: SpecialPageFactory.php:940
$wgLang
$wgLang
Definition: Setup.php:807
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:985
MediaWiki\SpecialPage\SpecialPageFactory\$contLang
Language $contLang
Definition: SpecialPageFactory.php:949
wfDeprecatedMsg
wfDeprecatedMsg( $msg, $version=false, $component=false, $callerOffset=2)
Log a deprecation warning with arbitrary message text.
Definition: GlobalFunctions.php:1028
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:41
MediaWiki\SpecialPage\SpecialPageFactory\executePath
executePath( $path, IContextSource $context, $including=false, LinkRenderer $linkRenderer=null)
Execute a special page path.
Definition: SpecialPageFactory.php:1298
$wgTitle
$wgTitle
Definition: Setup.php:825
MediaWiki\SpecialPage\SpecialPageFactory\getNames
getNames()
Returns a list of canonical special page names.
Definition: SpecialPageFactory.php:1007
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:1183
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:1271
MediaWiki\SpecialPage\SpecialPageFactory\$objectFactory
ObjectFactory $objectFactory
Definition: SpecialPageFactory.php:952
IContextSource\getUser
getUser()
MediaWiki\SpecialPage\SpecialPageFactory\getPage
getPage( $name)
Find the object with a given name and return it (or NULL)
Definition: SpecialPageFactory.php:1196
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:49
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:955
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:554
MediaWiki\SpecialPage\SpecialPageFactory\getTitleForAlias
getTitleForAlias( $alias)
Get a title for a given alias.
Definition: SpecialPageFactory.php:1506
MediaWiki\SpecialPage\SpecialPageFactory\CONSTRUCTOR_OPTIONS
const CONSTRUCTOR_OPTIONS
Definition: SpecialPageFactory.php:963
MediaWiki\$context
IContextSource $context
Definition: MediaWiki.php:40
$wgOut
$wgOut
Definition: Setup.php:812
MediaWiki\SpecialPage\SpecialPageFactory\$aliases
array $aliases
Definition: SpecialPageFactory.php:943
MediaWiki\SpecialPage\SpecialPageFactory\getAliasList
getAliasList()
Initialise and return the list of special page aliases.
Definition: SpecialPageFactory.php:1106
MediaWiki\SpecialPage\SpecialPageFactory\getPageList
getPageList()
Get the special page list as an array.
Definition: SpecialPageFactory.php:1016
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:68
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