MediaWiki  REL1_24
SpecialPageFactory.php
Go to the documentation of this file.
00001 <?php
00046 class SpecialPageFactory {
00050     private static $list = array(
00051         // Maintenance Reports
00052         'BrokenRedirects' => 'BrokenRedirectsPage',
00053         'Deadendpages' => 'DeadendPagesPage',
00054         'DoubleRedirects' => 'DoubleRedirectsPage',
00055         'Longpages' => 'LongPagesPage',
00056         'Ancientpages' => 'AncientPagesPage',
00057         'Lonelypages' => 'LonelyPagesPage',
00058         'Fewestrevisions' => 'FewestrevisionsPage',
00059         'Withoutinterwiki' => 'WithoutInterwikiPage',
00060         'Protectedpages' => 'SpecialProtectedpages',
00061         'Protectedtitles' => 'SpecialProtectedtitles',
00062         'Shortpages' => 'ShortpagesPage',
00063         'Uncategorizedcategories' => 'UncategorizedCategoriesPage',
00064         'Uncategorizedimages' => 'UncategorizedImagesPage',
00065         'Uncategorizedpages' => 'UncategorizedPagesPage',
00066         'Uncategorizedtemplates' => 'UncategorizedTemplatesPage',
00067         'Unusedcategories' => 'UnusedCategoriesPage',
00068         'Unusedimages' => 'UnusedimagesPage',
00069         'Unusedtemplates' => 'UnusedtemplatesPage',
00070         'Unwatchedpages' => 'UnwatchedpagesPage',
00071         'Wantedcategories' => 'WantedCategoriesPage',
00072         'Wantedfiles' => 'WantedFilesPage',
00073         'Wantedpages' => 'WantedPagesPage',
00074         'Wantedtemplates' => 'WantedTemplatesPage',
00075 
00076         // List of pages
00077         'Allpages' => 'SpecialAllpages',
00078         'Prefixindex' => 'SpecialPrefixindex',
00079         'Categories' => 'SpecialCategories',
00080         'Listredirects' => 'ListredirectsPage',
00081         'PagesWithProp' => 'SpecialPagesWithProp',
00082         'TrackingCategories' => 'SpecialTrackingCategories',
00083 
00084         // Login/create account
00085         'Userlogin' => 'LoginForm',
00086         'CreateAccount' => 'SpecialCreateAccount',
00087 
00088         // Users and rights
00089         'Block' => 'SpecialBlock',
00090         'Unblock' => 'SpecialUnblock',
00091         'BlockList' => 'SpecialBlockList',
00092         'ChangePassword' => 'SpecialChangePassword',
00093         'PasswordReset' => 'SpecialPasswordReset',
00094         'DeletedContributions' => 'DeletedContributionsPage',
00095         'Preferences' => 'SpecialPreferences',
00096         'ResetTokens' => 'SpecialResetTokens',
00097         'Contributions' => 'SpecialContributions',
00098         'Listgrouprights' => 'SpecialListGroupRights',
00099         'Listusers' => 'SpecialListUsers',
00100         'Listadmins' => 'SpecialListAdmins',
00101         'Listbots' => 'SpecialListBots',
00102         'Userrights' => 'UserrightsPage',
00103         'EditWatchlist' => 'SpecialEditWatchlist',
00104 
00105         // Recent changes and logs
00106         'Newimages' => 'SpecialNewFiles',
00107         'Log' => 'SpecialLog',
00108         'Watchlist' => 'SpecialWatchlist',
00109         'Newpages' => 'SpecialNewpages',
00110         'Recentchanges' => 'SpecialRecentChanges',
00111         'Recentchangeslinked' => 'SpecialRecentChangesLinked',
00112         'Tags' => 'SpecialTags',
00113 
00114         // Media reports and uploads
00115         'Listfiles' => 'SpecialListFiles',
00116         'Filepath' => 'SpecialFilepath',
00117         'MediaStatistics' => 'MediaStatisticsPage',
00118         'MIMEsearch' => 'MIMEsearchPage',
00119         'FileDuplicateSearch' => 'FileDuplicateSearchPage',
00120         'Upload' => 'SpecialUpload',
00121         'UploadStash' => 'SpecialUploadStash',
00122         'ListDuplicatedFiles' => 'ListDuplicatedFilesPage',
00123 
00124         // Data and tools
00125         'Statistics' => 'SpecialStatistics',
00126         'Allmessages' => 'SpecialAllmessages',
00127         'Version' => 'SpecialVersion',
00128         'Lockdb' => 'SpecialLockdb',
00129         'Unlockdb' => 'SpecialUnlockdb',
00130 
00131         // Redirecting special pages
00132         'LinkSearch' => 'LinkSearchPage',
00133         'Randompage' => 'RandomPage',
00134         'RandomInCategory' => 'SpecialRandomInCategory',
00135         'Randomredirect' => 'SpecialRandomredirect',
00136 
00137         // High use pages
00138         'Mostlinkedcategories' => 'MostlinkedCategoriesPage',
00139         'Mostimages' => 'MostimagesPage',
00140         'Mostinterwikis' => 'MostinterwikisPage',
00141         'Mostlinked' => 'MostlinkedPage',
00142         'Mostlinkedtemplates' => 'MostlinkedTemplatesPage',
00143         'Mostcategories' => 'MostcategoriesPage',
00144         'Mostrevisions' => 'MostrevisionsPage',
00145 
00146         // Page tools
00147         'ComparePages' => 'SpecialComparePages',
00148         'Export' => 'SpecialExport',
00149         'Import' => 'SpecialImport',
00150         'Undelete' => 'SpecialUndelete',
00151         'Whatlinkshere' => 'SpecialWhatLinksHere',
00152         'MergeHistory' => 'SpecialMergeHistory',
00153         'ExpandTemplates' => 'SpecialExpandTemplates',
00154 
00155         // Other
00156         'Booksources' => 'SpecialBookSources',
00157 
00158         // Unlisted / redirects
00159         'Blankpage' => 'SpecialBlankpage',
00160         'Diff' => 'SpecialDiff',
00161         'Emailuser' => 'SpecialEmailUser',
00162         'Movepage' => 'MovePageForm',
00163         'Mycontributions' => 'SpecialMycontributions',
00164         'MyLanguage' => 'SpecialMyLanguage',
00165         'Mypage' => 'SpecialMypage',
00166         'Mytalk' => 'SpecialMytalk',
00167         'Myuploads' => 'SpecialMyuploads',
00168         'AllMyUploads' => 'SpecialAllMyUploads',
00169         'PermanentLink' => 'SpecialPermanentLink',
00170         'Redirect' => 'SpecialRedirect',
00171         'Revisiondelete' => 'SpecialRevisionDelete',
00172         'RunJobs' => 'SpecialRunJobs',
00173         'Specialpages' => 'SpecialSpecialpages',
00174         'Userlogout' => 'SpecialUserlogout',
00175     );
00176 
00177     private static $aliases;
00178 
00183     public static function resetList() {
00184         self::$list = null;
00185         self::$aliases = null;
00186     }
00187 
00194     public static function getNames() {
00195         return array_keys( self::getPageList() );
00196     }
00197 
00204     public static function getList() {
00205         wfDeprecated( __FUNCTION__, '1.24' );
00206         return self::getPageList();
00207     }
00208 
00214     private static function getPageList() {
00215         global $wgSpecialPages;
00216         global $wgDisableCounters, $wgDisableInternalSearch, $wgEmailAuthentication;
00217         global $wgEnableEmail, $wgEnableJavaScriptTest;
00218         global $wgPageLanguageUseDB;
00219 
00220         if ( !is_object( self::$list ) ) {
00221             wfProfileIn( __METHOD__ );
00222 
00223             if ( !$wgDisableCounters ) {
00224                 self::$list['Popularpages'] = 'PopularPagesPage';
00225             }
00226 
00227             if ( !$wgDisableInternalSearch ) {
00228                 self::$list['Search'] = 'SpecialSearch';
00229             }
00230 
00231             if ( $wgEmailAuthentication ) {
00232                 self::$list['Confirmemail'] = 'EmailConfirmation';
00233                 self::$list['Invalidateemail'] = 'EmailInvalidation';
00234             }
00235 
00236             if ( $wgEnableEmail ) {
00237                 self::$list['ChangeEmail'] = 'SpecialChangeEmail';
00238             }
00239 
00240             if ( $wgEnableJavaScriptTest ) {
00241                 self::$list['JavaScriptTest'] = 'SpecialJavaScriptTest';
00242             }
00243 
00244             if ( $wgPageLanguageUseDB ) {
00245                 self::$list['PageLanguage'] = 'SpecialPageLanguage';
00246             }
00247 
00248             self::$list['Activeusers'] = 'SpecialActiveUsers';
00249 
00250             // Add extension special pages
00251             self::$list = array_merge( self::$list, $wgSpecialPages );
00252 
00253             // Run hooks
00254             // This hook can be used to remove undesired built-in special pages
00255             wfRunHooks( 'SpecialPage_initList', array( &self::$list ) );
00256 
00257             wfProfileOut( __METHOD__ );
00258         }
00259 
00260         return self::$list;
00261     }
00262 
00271     private static function getAliasListObject() {
00272         if ( !is_object( self::$aliases ) ) {
00273             global $wgContLang;
00274             $aliases = $wgContLang->getSpecialPageAliases();
00275 
00276             $missingPages = self::getPageList();
00277 
00278             self::$aliases = array();
00279             // Check for $aliases being an array since Language::getSpecialPageAliases can return null
00280             if ( is_array( $aliases ) ) {
00281                 foreach ( $aliases as $realName => $aliasList ) {
00282                     foreach ( $aliasList as $alias ) {
00283                         self::$aliases[$wgContLang->caseFold( $alias )] = $realName;
00284                     }
00285                     unset( $missingPages->$realName );
00286                 }
00287             }
00288             foreach ( $missingPages as $name => $stuff ) {
00289                 self::$aliases[$wgContLang->caseFold( $name )] = $name;
00290             }
00291 
00292             // Cast to object: func()[$key] doesn't work, but func()->$key does
00293             self::$aliases = (object)self::$aliases;
00294         }
00295 
00296         return self::$aliases;
00297     }
00298 
00307     public static function resolveAlias( $alias ) {
00308         global $wgContLang;
00309         $bits = explode( '/', $alias, 2 );
00310 
00311         $caseFoldedAlias = $wgContLang->caseFold( $bits[0] );
00312         $caseFoldedAlias = str_replace( ' ', '_', $caseFoldedAlias );
00313         if ( isset( self::getAliasListObject()->$caseFoldedAlias ) ) {
00314             $name = self::getAliasListObject()->$caseFoldedAlias;
00315         } else {
00316             return array( null, null );
00317         }
00318 
00319         if ( !isset( $bits[1] ) ) { // bug 2087
00320             $par = null;
00321         } else {
00322             $par = $bits[1];
00323         }
00324 
00325         return array( $name, $par );
00326     }
00327 
00335     public static function setGroup( $page, $group ) {
00336         wfDeprecated( __METHOD__, '1.21' );
00337 
00338         global $wgSpecialPageGroups;
00339         $name = is_object( $page ) ? $page->getName() : $page;
00340         $wgSpecialPageGroups[$name] = $group;
00341     }
00342 
00350     public static function getGroup( &$page ) {
00351         wfDeprecated( __METHOD__, '1.21' );
00352 
00353         return $page->getFinalGroupName();
00354     }
00355 
00362     public static function exists( $name ) {
00363         list( $title, /*...*/ ) = self::resolveAlias( $name );
00364 
00365         $specialPageList = self::getPageList();
00366         return isset( $specialPageList[$title] );
00367     }
00368 
00375     public static function getPage( $name ) {
00376         list( $realName, /*...*/ ) = self::resolveAlias( $name );
00377 
00378         $specialPageList = self::getPageList();
00379 
00380         if ( isset( $specialPageList[$realName] ) ) {
00381             $rec = $specialPageList[$realName];
00382 
00383             if ( is_string( $rec ) ) {
00384                 $className = $rec;
00385                 $page = new $className;
00386             } elseif ( is_callable( $rec ) ) {
00387                 // Use callback to instantiate the special page
00388                 $page = call_user_func( $rec );
00389             } elseif ( is_array( $rec ) ) {
00390                 $className = array_shift( $rec );
00391                 // @deprecated, officially since 1.18, unofficially since forever
00392                 wfDeprecated( "Array syntax for \$wgSpecialPages is deprecated ($className), " .
00393                     "define a subclass of SpecialPage instead.", '1.18' );
00394                 $page = MWFunction::newObj( $className, $rec );
00395             } elseif ( $rec instanceof SpecialPage ) {
00396                 $page = $rec; //XXX: we should deep clone here
00397             } else {
00398                 $page = null;
00399             }
00400 
00401             if ( $page instanceof SpecialPage ) {
00402                 return $page;
00403             } else {
00404                 // It's not a classname, nor a callback, nor a legacy constructor array,
00405                 // nor a special page object. Give up.
00406                 wfLogWarning( "Cannot instantiate special page $realName: bad spec!" );
00407                 return null;
00408             }
00409 
00410         } else {
00411             return null;
00412         }
00413     }
00414 
00423     public static function getUsablePages( User $user = null ) {
00424         $pages = array();
00425         if ( $user === null ) {
00426             global $wgUser;
00427             $user = $wgUser;
00428         }
00429         foreach ( self::getPageList() as $name => $rec ) {
00430             $page = self::getPage( $name );
00431             if ( $page ) { // not null
00432                 $page->setContext( RequestContext::getMain() );
00433                 if ( $page->isListed()
00434                     && ( !$page->isRestricted() || $page->userCanExecute( $user ) )
00435                 ) {
00436                     $pages[$name] = $page;
00437                 }
00438             }
00439         }
00440 
00441         return $pages;
00442     }
00443 
00449     public static function getRegularPages() {
00450         $pages = array();
00451         foreach ( self::getPageList() as $name => $rec ) {
00452             $page = self::getPage( $name );
00453             if ( $page->isListed() && !$page->isRestricted() ) {
00454                 $pages[$name] = $page;
00455             }
00456         }
00457 
00458         return $pages;
00459     }
00460 
00468     public static function getRestrictedPages( User $user = null ) {
00469         $pages = array();
00470         if ( $user === null ) {
00471             global $wgUser;
00472             $user = $wgUser;
00473         }
00474         foreach ( self::getPageList() as $name => $rec ) {
00475             $page = self::getPage( $name );
00476             if (
00477                 $page->isListed()
00478                 && $page->isRestricted()
00479                 && $page->userCanExecute( $user )
00480             ) {
00481                 $pages[$name] = $page;
00482             }
00483         }
00484 
00485         return $pages;
00486     }
00487 
00502     public static function executePath( Title &$title, IContextSource &$context, $including = false ) {
00503         wfProfileIn( __METHOD__ );
00504 
00505         // @todo FIXME: Redirects broken due to this call
00506         $bits = explode( '/', $title->getDBkey(), 2 );
00507         $name = $bits[0];
00508         if ( !isset( $bits[1] ) ) { // bug 2087
00509             $par = null;
00510         } else {
00511             $par = $bits[1];
00512         }
00513         $page = self::getPage( $name );
00514         // Nonexistent?
00515         if ( !$page ) {
00516             $context->getOutput()->setArticleRelated( false );
00517             $context->getOutput()->setRobotPolicy( 'noindex,nofollow' );
00518 
00519             global $wgSend404Code;
00520             if ( $wgSend404Code ) {
00521                 $context->getOutput()->setStatusCode( 404 );
00522             }
00523 
00524             $context->getOutput()->showErrorPage( 'nosuchspecialpage', 'nospecialpagetext' );
00525             wfProfileOut( __METHOD__ );
00526 
00527             return false;
00528         }
00529 
00530         // Page exists, set the context
00531         $page->setContext( $context );
00532 
00533         if ( !$including ) {
00534             // Redirect to canonical alias for GET commands
00535             // Not for POST, we'd lose the post data, so it's best to just distribute
00536             // the request. Such POST requests are possible for old extensions that
00537             // generate self-links without being aware that their default name has
00538             // changed.
00539             if ( $name != $page->getLocalName() && !$context->getRequest()->wasPosted() ) {
00540                 $query = $context->getRequest()->getQueryValues();
00541                 unset( $query['title'] );
00542                 $title = $page->getPageTitle( $par );
00543                 $url = $title->getFullURL( $query );
00544                 $context->getOutput()->redirect( $url );
00545                 wfProfileOut( __METHOD__ );
00546 
00547                 return $title;
00548             } else {
00549                 $context->setTitle( $page->getPageTitle( $par ) );
00550             }
00551         } elseif ( !$page->isIncludable() ) {
00552             wfProfileOut( __METHOD__ );
00553 
00554             return false;
00555         }
00556 
00557         $page->including( $including );
00558 
00559         // Execute special page
00560         $profName = 'Special:' . $page->getName();
00561         wfProfileIn( $profName );
00562         $page->run( $par );
00563         wfProfileOut( $profName );
00564         wfProfileOut( __METHOD__ );
00565 
00566         return true;
00567     }
00568 
00583     public static function capturePath( Title $title, IContextSource $context ) {
00584         global $wgOut, $wgTitle, $wgRequest, $wgUser, $wgLang;
00585 
00586         // Save current globals
00587         $oldTitle = $wgTitle;
00588         $oldOut = $wgOut;
00589         $oldRequest = $wgRequest;
00590         $oldUser = $wgUser;
00591         $oldLang = $wgLang;
00592 
00593         // Set the globals to the current context
00594         $wgTitle = $title;
00595         $wgOut = $context->getOutput();
00596         $wgRequest = $context->getRequest();
00597         $wgUser = $context->getUser();
00598         $wgLang = $context->getLanguage();
00599 
00600         // The useful part
00601         $ret = self::executePath( $title, $context, true );
00602 
00603         // And restore the old globals
00604         $wgTitle = $oldTitle;
00605         $wgOut = $oldOut;
00606         $wgRequest = $oldRequest;
00607         $wgUser = $oldUser;
00608         $wgLang = $oldLang;
00609 
00610         return $ret;
00611     }
00612 
00620     public static function getLocalNameFor( $name, $subpage = false ) {
00621         global $wgContLang;
00622         $aliases = $wgContLang->getSpecialPageAliases();
00623 
00624         if ( isset( $aliases[$name][0] ) ) {
00625             $name = $aliases[$name][0];
00626         } else {
00627             // Try harder in case someone misspelled the correct casing
00628             $found = false;
00629             // Check for $aliases being an array since Language::getSpecialPageAliases can return null
00630             if ( is_array( $aliases ) ) {
00631                 foreach ( $aliases as $n => $values ) {
00632                     if ( strcasecmp( $name, $n ) === 0 ) {
00633                         wfWarn( "Found alias defined for $n when searching for " .
00634                             "special page aliases for $name. Case mismatch?" );
00635                         $name = $values[0];
00636                         $found = true;
00637                         break;
00638                     }
00639                 }
00640             }
00641             if ( !$found ) {
00642                 wfWarn( "Did not find alias for special page '$name'. " .
00643                     "Perhaps no aliases are defined for it?" );
00644             }
00645         }
00646         if ( $subpage !== false && !is_null( $subpage ) ) {
00647             $name = "$name/$subpage";
00648         }
00649 
00650         return $wgContLang->ucfirst( $name );
00651     }
00652 
00659     public static function getTitleForAlias( $alias ) {
00660         list( $name, $subpage ) = self::resolveAlias( $alias );
00661         if ( $name != null ) {
00662             return SpecialPage::getTitleFor( $name, $subpage );
00663         } else {
00664             return null;
00665         }
00666     }
00667 }