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