namespace MediaWiki\Special;
-use Config;
use Hooks;
use IContextSource;
use Language;
+use MediaWiki\Config\ServiceOptions;
use MediaWiki\Linker\LinkRenderer;
use Profiler;
use RequestContext;
use SpecialPage;
use Title;
use User;
-use Wikimedia\Assert\Assert;
+use Wikimedia\ObjectFactory;
/**
* Factory for handling the special page list and generating SpecialPage objects.
*/
private static $coreList = [
// Maintenance Reports
- 'BrokenRedirects' => \BrokenRedirectsPage::class,
- 'Deadendpages' => \DeadendPagesPage::class,
- 'DoubleRedirects' => \DoubleRedirectsPage::class,
- 'Longpages' => \LongPagesPage::class,
- 'Ancientpages' => \AncientPagesPage::class,
- 'Lonelypages' => \LonelyPagesPage::class,
- 'Fewestrevisions' => \FewestrevisionsPage::class,
- 'Withoutinterwiki' => \WithoutInterwikiPage::class,
+ 'BrokenRedirects' => \SpecialBrokenRedirects::class,
+ 'Deadendpages' => \SpecialDeadendPages::class,
+ 'DoubleRedirects' => \SpecialDoubleRedirects::class,
+ 'Longpages' => \SpecialLongPages::class,
+ 'Ancientpages' => \SpecialAncientPages::class,
+ 'Lonelypages' => \SpecialLonelyPages::class,
+ 'Fewestrevisions' => \SpecialFewestRevisions::class,
+ 'Withoutinterwiki' => \SpecialWithoutInterwiki::class,
'Protectedpages' => \SpecialProtectedpages::class,
'Protectedtitles' => \SpecialProtectedtitles::class,
- 'Shortpages' => \ShortPagesPage::class,
- 'Uncategorizedcategories' => \UncategorizedCategoriesPage::class,
- 'Uncategorizedimages' => \UncategorizedImagesPage::class,
- 'Uncategorizedpages' => \UncategorizedPagesPage::class,
- 'Uncategorizedtemplates' => \UncategorizedTemplatesPage::class,
- 'Unusedcategories' => \UnusedCategoriesPage::class,
- 'Unusedimages' => \UnusedimagesPage::class,
- 'Unusedtemplates' => \UnusedtemplatesPage::class,
- 'Unwatchedpages' => \UnwatchedpagesPage::class,
- 'Wantedcategories' => \WantedCategoriesPage::class,
+ 'Shortpages' => \SpecialShortPages::class,
+ 'Uncategorizedcategories' => \SpecialUncategorizedCategories::class,
+ 'Uncategorizedimages' => \SpecialUncategorizedImages::class,
+ 'Uncategorizedpages' => \SpecialUncategorizedPages::class,
+ 'Uncategorizedtemplates' => \SpecialUncategorizedTemplates::class,
+ 'Unusedcategories' => \SpecialUnusedCategories::class,
+ 'Unusedimages' => \SpecialUnusedImages::class,
+ 'Unusedtemplates' => \SpecialUnusedTemplates::class,
+ 'Unwatchedpages' => \SpecialUnwatchedPages::class,
+ 'Wantedcategories' => \SpecialWantedCategories::class,
'Wantedfiles' => \WantedFilesPage::class,
'Wantedpages' => \WantedPagesPage::class,
- 'Wantedtemplates' => \WantedTemplatesPage::class,
+ 'Wantedtemplates' => \SpecialWantedTemplates::class,
// List of pages
'Allpages' => \SpecialAllPages::class,
'Prefixindex' => \SpecialPrefixindex::class,
'Categories' => \SpecialCategories::class,
- 'Listredirects' => \ListredirectsPage::class,
+ 'Listredirects' => \SpecialListRedirects::class,
'PagesWithProp' => \SpecialPagesWithProp::class,
'TrackingCategories' => \SpecialTrackingCategories::class,
'ChangePassword' => \SpecialChangePassword::class,
'BotPasswords' => \SpecialBotPasswords::class,
'PasswordReset' => \SpecialPasswordReset::class,
- 'DeletedContributions' => \DeletedContributionsPage::class,
+ 'DeletedContributions' => \SpecialDeletedContributions::class,
'Preferences' => \SpecialPreferences::class,
'ResetTokens' => \SpecialResetTokens::class,
'Contributions' => \SpecialContributions::class,
// Media reports and uploads
'Listfiles' => \SpecialListFiles::class,
'Filepath' => \SpecialFilepath::class,
- 'MediaStatistics' => \MediaStatisticsPage::class,
- 'MIMEsearch' => \MIMEsearchPage::class,
- 'FileDuplicateSearch' => \FileDuplicateSearchPage::class,
+ 'MediaStatistics' => \SpecialMediaStatistics::class,
+ 'MIMEsearch' => \SpecialMIMESearch::class,
+ 'FileDuplicateSearch' => \SpecialFileDuplicateSearch::class,
'Upload' => \SpecialUpload::class,
'UploadStash' => \SpecialUploadStash::class,
- 'ListDuplicatedFiles' => \ListDuplicatedFilesPage::class,
+ 'ListDuplicatedFiles' => \SpecialListDuplicatedFiles::class,
// Data and tools
'ApiSandbox' => \SpecialApiSandbox::class,
'Unlockdb' => \SpecialUnlockdb::class,
// Redirecting special pages
- 'LinkSearch' => \LinkSearchPage::class,
+ 'LinkSearch' => \SpecialLinkSearch::class,
'Randompage' => \RandomPage::class,
'RandomInCategory' => \SpecialRandomInCategory::class,
'Randomredirect' => \SpecialRandomredirect::class,
'GoToInterwiki' => \SpecialGoToInterwiki::class,
// High use pages
- 'Mostlinkedcategories' => \MostlinkedCategoriesPage::class,
+ 'Mostlinkedcategories' => \SpecialMostLinkedCategories::class,
'Mostimages' => \MostimagesPage::class,
- 'Mostinterwikis' => \MostinterwikisPage::class,
- 'Mostlinked' => \MostlinkedPage::class,
- 'Mostlinkedtemplates' => \MostlinkedTemplatesPage::class,
- 'Mostcategories' => \MostcategoriesPage::class,
- 'Mostrevisions' => \MostrevisionsPage::class,
+ 'Mostinterwikis' => \SpecialMostInterwikis::class,
+ 'Mostlinked' => \SpecialMostLinked::class,
+ 'Mostlinkedtemplates' => \SpecialMostLinkedTemplates::class,
+ 'Mostcategories' => \SpecialMostCategories::class,
+ 'Mostrevisions' => \SpecialMostRevisions::class,
// Page tools
'ComparePages' => \SpecialComparePages::class,
'ApiHelp' => \SpecialApiHelp::class,
'Blankpage' => \SpecialBlankpage::class,
'Diff' => \SpecialDiff::class,
- 'EditTags' => \SpecialEditTags::class,
+ 'EditTags' => [
+ 'class' => \SpecialEditTags::class,
+ 'services' => [
+ 'PermissionManager',
+ ],
+ ],
'Emailuser' => \SpecialEmailUser::class,
'Movepage' => \MovePageForm::class,
'Mycontributions' => \SpecialMycontributions::class,
'Mytalk' => \SpecialMytalk::class,
'Myuploads' => \SpecialMyuploads::class,
'AllMyUploads' => \SpecialAllMyUploads::class,
+ 'NewSection' => \SpecialNewSection::class,
'PermanentLink' => \SpecialPermanentLink::class,
'Redirect' => \SpecialRedirect::class,
'Revisiondelete' => \SpecialRevisionDelete::class,
/** @var array */
private $aliases;
- /** @var Config */
+ /** @var ServiceOptions */
private $options;
/** @var Language */
private $contLang;
+ /** @var ObjectFactory */
+ private $objectFactory;
+
/**
* TODO Make this a const when HHVM support is dropped (T192166)
*
* @var array
* @since 1.33
- * */
+ */
public static $constructorOptions = [
'ContentHandlerUseDB',
'DisableInternalSearch',
'EmailAuthentication',
'EnableEmail',
'EnableJavaScriptTest',
+ 'EnableSpecialMute',
'PageLanguageUseDB',
'SpecialPages',
];
/**
- * @param array $options
+ * @param ServiceOptions $options
* @param Language $contLang
+ * @param ObjectFactory $objectFactory
*/
- public function __construct( array $options, Language $contLang ) {
- Assert::parameter( count( $options ) === count( self::$constructorOptions ) &&
- !array_diff( self::$constructorOptions, array_keys( $options ) ),
- '$options', 'Wrong set of options present' );
+ public function __construct(
+ ServiceOptions $options,
+ Language $contLang,
+ ObjectFactory $objectFactory
+ ) {
+ $options->assertRequiredOptions( self::$constructorOptions );
$this->options = $options;
$this->contLang = $contLang;
+ $this->objectFactory = $objectFactory;
}
/**
if ( !is_array( $this->list ) ) {
$this->list = self::$coreList;
- if ( !$this->options['DisableInternalSearch'] ) {
+ if ( !$this->options->get( 'DisableInternalSearch' ) ) {
$this->list['Search'] = \SpecialSearch::class;
}
- if ( $this->options['EmailAuthentication'] ) {
- $this->list['Confirmemail'] = \EmailConfirmation::class;
- $this->list['Invalidateemail'] = \EmailInvalidation::class;
+ if ( $this->options->get( 'EmailAuthentication' ) ) {
+ $this->list['Confirmemail'] = \SpecialConfirmEmail::class;
+ $this->list['Invalidateemail'] = \SpecialEmailInvalidate::class;
}
- if ( $this->options['EnableEmail'] ) {
+ if ( $this->options->get( 'EnableEmail' ) ) {
$this->list['ChangeEmail'] = \SpecialChangeEmail::class;
}
- if ( $this->options['EnableJavaScriptTest'] ) {
+ if ( $this->options->get( 'EnableJavaScriptTest' ) ) {
$this->list['JavaScriptTest'] = \SpecialJavaScriptTest::class;
}
- if ( $this->options['PageLanguageUseDB'] ) {
+ if ( $this->options->get( 'EnableSpecialMute' ) ) {
+ $this->list['Mute'] = \SpecialMute::class;
+ }
+
+ if ( $this->options->get( 'PageLanguageUseDB' ) ) {
$this->list['PageLanguage'] = \SpecialPageLanguage::class;
}
- if ( $this->options['ContentHandlerUseDB'] ) {
+
+ if ( $this->options->get( 'ContentHandlerUseDB' ) ) {
$this->list['ChangeContentModel'] = \SpecialChangeContentModel::class;
}
// Add extension special pages
- $this->list = array_merge( $this->list, $this->options['SpecialPages'] );
+ $this->list = array_merge( $this->list, $this->options->get( 'SpecialPages' ) );
// This hook can be used to disable unwanted core special pages
// or conditionally register special pages.
* subpage.
*
* @param string $alias
- * @return array Array( String, String|null ), or array( null, null ) if the page is invalid
+ * @return array [ String, String|null ], or [ null, null ] if the page is invalid
*/
public function resolveAlias( $alias ) {
$bits = explode( '/', $alias, 2 );
if ( isset( $specialPageList[$realName] ) ) {
$rec = $specialPageList[$realName];
- if ( is_callable( $rec ) ) {
- // Use callback to instantiate the special page
- $page = $rec();
- } elseif ( is_string( $rec ) ) {
- $className = $rec;
- $page = new $className;
- } elseif ( $rec instanceof SpecialPage ) {
+ if ( $rec instanceof SpecialPage ) {
+ wfDeprecated(
+ "a SpecialPage instance (for $realName) in " .
+ '$wgSpecialPages or from the SpecialPage_initList hook',
+ '1.34'
+ );
+
$page = $rec; // XXX: we should deep clone here
+ } elseif ( is_array( $rec ) || is_string( $rec ) || is_callable( $rec ) ) {
+ $page = $this->objectFactory->createObject(
+ $rec,
+ [
+ 'allowClassName' => true,
+ 'allowCallable' => true
+ ]
+ );
} else {
$page = null;
}
return $title;
}
+ // @phan-suppress-next-line PhanUndeclaredMethod
$context->setTitle( $page->getPageTitle( $par ) );
} elseif ( !$page->isIncludable() ) {
return false;