MediaWiki  REL1_23
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         'MIMEsearch' => 'MIMEsearchPage',
00118         'FileDuplicateSearch' => 'FileDuplicateSearchPage',
00119         'Upload' => 'SpecialUpload',
00120         'UploadStash' => 'SpecialUploadStash',
00121         'ListDuplicatedFiles' => 'ListDuplicatedFilesPage',
00122 
00123         // Data and tools
00124         'Statistics' => 'SpecialStatistics',
00125         'Allmessages' => 'SpecialAllmessages',
00126         'Version' => 'SpecialVersion',
00127         'Lockdb' => 'SpecialLockdb',
00128         'Unlockdb' => 'SpecialUnlockdb',
00129 
00130         // Redirecting special pages
00131         'LinkSearch' => 'LinkSearchPage',
00132         'Randompage' => 'RandomPage',
00133         'RandomInCategory' => 'SpecialRandomInCategory',
00134         'Randomredirect' => 'SpecialRandomredirect',
00135 
00136         // High use pages
00137         'Mostlinkedcategories' => 'MostlinkedCategoriesPage',
00138         'Mostimages' => 'MostimagesPage',
00139         'Mostinterwikis' => 'MostinterwikisPage',
00140         'Mostlinked' => 'MostlinkedPage',
00141         'Mostlinkedtemplates' => 'MostlinkedTemplatesPage',
00142         'Mostcategories' => 'MostcategoriesPage',
00143         'Mostrevisions' => 'MostrevisionsPage',
00144 
00145         // Page tools
00146         'ComparePages' => 'SpecialComparePages',
00147         'Export' => 'SpecialExport',
00148         'Import' => 'SpecialImport',
00149         'Undelete' => 'SpecialUndelete',
00150         'Whatlinkshere' => 'SpecialWhatLinksHere',
00151         'MergeHistory' => 'SpecialMergeHistory',
00152         'ExpandTemplates' => 'SpecialExpandTemplates',
00153 
00154         // Other
00155         'Booksources' => 'SpecialBookSources',
00156 
00157         // Unlisted / redirects
00158         'Blankpage' => 'SpecialBlankpage',
00159         'Diff' => 'SpecialDiff',
00160         'Emailuser' => 'SpecialEmailUser',
00161         'Movepage' => 'MovePageForm',
00162         'Mycontributions' => 'SpecialMycontributions',
00163         'Mypage' => 'SpecialMypage',
00164         'Mytalk' => 'SpecialMytalk',
00165         'Myuploads' => 'SpecialMyuploads',
00166         'AllMyUploads' => 'SpecialAllMyUploads',
00167         'PermanentLink' => 'SpecialPermanentLink',
00168         'Redirect' => 'SpecialRedirect',
00169         'Revisiondelete' => 'SpecialRevisionDelete',
00170         'RunJobs' => 'SpecialRunJobs',
00171         'Specialpages' => 'SpecialSpecialpages',
00172         'Userlogout' => 'SpecialUserlogout',
00173     );
00174 
00175     private static $aliases;
00176 
00182     static function getList() {
00183         global $wgSpecialPages;
00184         global $wgDisableCounters, $wgDisableInternalSearch, $wgEmailAuthentication;
00185         global $wgEnableEmail, $wgEnableJavaScriptTest;
00186 
00187         if ( !is_object( self::$list ) ) {
00188             wfProfileIn( __METHOD__ );
00189 
00190             if ( !$wgDisableCounters ) {
00191                 self::$list['Popularpages'] = 'PopularPagesPage';
00192             }
00193 
00194             if ( !$wgDisableInternalSearch ) {
00195                 self::$list['Search'] = 'SpecialSearch';
00196             }
00197 
00198             if ( $wgEmailAuthentication ) {
00199                 self::$list['Confirmemail'] = 'EmailConfirmation';
00200                 self::$list['Invalidateemail'] = 'EmailInvalidation';
00201             }
00202 
00203             if ( $wgEnableEmail ) {
00204                 self::$list['ChangeEmail'] = 'SpecialChangeEmail';
00205             }
00206 
00207             if ( $wgEnableJavaScriptTest ) {
00208                 self::$list['JavaScriptTest'] = 'SpecialJavaScriptTest';
00209             }
00210 
00211             self::$list['Activeusers'] = 'SpecialActiveUsers';
00212 
00213             // Add extension special pages
00214             self::$list = array_merge( self::$list, $wgSpecialPages );
00215 
00216             // Run hooks
00217             // This hook can be used to remove undesired built-in special pages
00218             wfRunHooks( 'SpecialPage_initList', array( &self::$list ) );
00219 
00220             // Cast to object: func()[$key] doesn't work, but func()->$key does
00221             settype( self::$list, 'object' );
00222 
00223             wfProfileOut( __METHOD__ );
00224         }
00225 
00226         return self::$list;
00227     }
00228 
00237     static function getAliasList() {
00238         if ( !is_object( self::$aliases ) ) {
00239             global $wgContLang;
00240             $aliases = $wgContLang->getSpecialPageAliases();
00241 
00242             // Objects are passed by reference by default, need to create a copy
00243             $missingPages = clone self::getList();
00244 
00245             self::$aliases = array();
00246             foreach ( $aliases as $realName => $aliasList ) {
00247                 foreach ( $aliasList as $alias ) {
00248                     self::$aliases[$wgContLang->caseFold( $alias )] = $realName;
00249                 }
00250                 unset( $missingPages->$realName );
00251             }
00252             foreach ( $missingPages as $name => $stuff ) {
00253                 self::$aliases[$wgContLang->caseFold( $name )] = $name;
00254             }
00255 
00256             // Cast to object: func()[$key] doesn't work, but func()->$key does
00257             self::$aliases = (object)self::$aliases;
00258         }
00259 
00260         return self::$aliases;
00261     }
00262 
00271     public static function resolveAlias( $alias ) {
00272         global $wgContLang;
00273         $bits = explode( '/', $alias, 2 );
00274 
00275         $caseFoldedAlias = $wgContLang->caseFold( $bits[0] );
00276         $caseFoldedAlias = str_replace( ' ', '_', $caseFoldedAlias );
00277         if ( isset( self::getAliasList()->$caseFoldedAlias ) ) {
00278             $name = self::getAliasList()->$caseFoldedAlias;
00279         } else {
00280             return array( null, null );
00281         }
00282 
00283         if ( !isset( $bits[1] ) ) { // bug 2087
00284             $par = null;
00285         } else {
00286             $par = $bits[1];
00287         }
00288 
00289         return array( $name, $par );
00290     }
00291 
00299     public static function setGroup( $page, $group ) {
00300         wfDeprecated( __METHOD__, '1.21' );
00301 
00302         global $wgSpecialPageGroups;
00303         $name = is_object( $page ) ? $page->getName() : $page;
00304         $wgSpecialPageGroups[$name] = $group;
00305     }
00306 
00314     public static function getGroup( &$page ) {
00315         wfDeprecated( __METHOD__, '1.21' );
00316 
00317         return $page->getFinalGroupName();
00318     }
00319 
00326     public static function exists( $name ) {
00327         list( $title, /*...*/ ) = self::resolveAlias( $name );
00328 
00329         return property_exists( self::getList(), $title );
00330     }
00331 
00338     public static function getPage( $name ) {
00339         list( $realName, /*...*/ ) = self::resolveAlias( $name );
00340         if ( property_exists( self::getList(), $realName ) ) {
00341             $rec = self::getList()->$realName;
00342             if ( is_string( $rec ) ) {
00343                 $className = $rec;
00344 
00345                 return new $className;
00346             } elseif ( is_array( $rec ) ) {
00347                 // @deprecated, officially since 1.18, unofficially since forever
00348                 wfDebug( "Array syntax for \$wgSpecialPages is deprecated, " .
00349                     "define a subclass of SpecialPage instead." );
00350                 $className = array_shift( $rec );
00351                 self::getList()->$realName = MWFunction::newObj( $className, $rec );
00352             }
00353 
00354             return self::getList()->$realName;
00355         } else {
00356             return null;
00357         }
00358     }
00359 
00368     public static function getUsablePages( User $user = null ) {
00369         $pages = array();
00370         if ( $user === null ) {
00371             global $wgUser;
00372             $user = $wgUser;
00373         }
00374         foreach ( self::getList() as $name => $rec ) {
00375             $page = self::getPage( $name );
00376             if ( $page ) { // not null
00377                 $page->setContext( RequestContext::getMain() );
00378                 if ( $page->isListed()
00379                     && ( !$page->isRestricted() || $page->userCanExecute( $user ) )
00380                 ) {
00381                     $pages[$name] = $page;
00382                 }
00383             }
00384         }
00385 
00386         return $pages;
00387     }
00388 
00394     public static function getRegularPages() {
00395         $pages = array();
00396         foreach ( self::getList() as $name => $rec ) {
00397             $page = self::getPage( $name );
00398             if ( $page->isListed() && !$page->isRestricted() ) {
00399                 $pages[$name] = $page;
00400             }
00401         }
00402 
00403         return $pages;
00404     }
00405 
00412     public static function getRestrictedPages() {
00413         global $wgUser;
00414         $pages = array();
00415         foreach ( self::getList() as $name => $rec ) {
00416             $page = self::getPage( $name );
00417             if (
00418                 $page->isListed()
00419                 && $page->isRestricted()
00420                 && $page->userCanExecute( $wgUser )
00421             ) {
00422                 $pages[$name] = $page;
00423             }
00424         }
00425 
00426         return $pages;
00427     }
00428 
00443     public static function executePath( Title &$title, IContextSource &$context, $including = false ) {
00444         wfProfileIn( __METHOD__ );
00445 
00446         // @todo FIXME: Redirects broken due to this call
00447         $bits = explode( '/', $title->getDBkey(), 2 );
00448         $name = $bits[0];
00449         if ( !isset( $bits[1] ) ) { // bug 2087
00450             $par = null;
00451         } else {
00452             $par = $bits[1];
00453         }
00454         $page = self::getPage( $name );
00455         // Nonexistent?
00456         if ( !$page ) {
00457             $context->getOutput()->setArticleRelated( false );
00458             $context->getOutput()->setRobotPolicy( 'noindex,nofollow' );
00459 
00460             global $wgSend404Code;
00461             if ( $wgSend404Code ) {
00462                 $context->getOutput()->setStatusCode( 404 );
00463             }
00464 
00465             $context->getOutput()->showErrorPage( 'nosuchspecialpage', 'nospecialpagetext' );
00466             wfProfileOut( __METHOD__ );
00467 
00468             return false;
00469         }
00470 
00471         // Page exists, set the context
00472         $page->setContext( $context );
00473 
00474         if ( !$including ) {
00475             // Redirect to canonical alias for GET commands
00476             // Not for POST, we'd lose the post data, so it's best to just distribute
00477             // the request. Such POST requests are possible for old extensions that
00478             // generate self-links without being aware that their default name has
00479             // changed.
00480             if ( $name != $page->getLocalName() && !$context->getRequest()->wasPosted() ) {
00481                 $query = $context->getRequest()->getQueryValues();
00482                 unset( $query['title'] );
00483                 $title = $page->getPageTitle( $par );
00484                 $url = $title->getFullURL( $query );
00485                 $context->getOutput()->redirect( $url );
00486                 wfProfileOut( __METHOD__ );
00487 
00488                 return $title;
00489             } else {
00490                 $context->setTitle( $page->getPageTitle( $par ) );
00491             }
00492         } elseif ( !$page->isIncludable() ) {
00493             wfProfileOut( __METHOD__ );
00494 
00495             return false;
00496         }
00497 
00498         $page->including( $including );
00499 
00500         // Execute special page
00501         $profName = 'Special:' . $page->getName();
00502         wfProfileIn( $profName );
00503         $page->run( $par );
00504         wfProfileOut( $profName );
00505         wfProfileOut( __METHOD__ );
00506 
00507         return true;
00508     }
00509 
00524     static function capturePath( Title $title, IContextSource $context ) {
00525         global $wgOut, $wgTitle, $wgRequest, $wgUser, $wgLang;
00526 
00527         // Save current globals
00528         $oldTitle = $wgTitle;
00529         $oldOut = $wgOut;
00530         $oldRequest = $wgRequest;
00531         $oldUser = $wgUser;
00532         $oldLang = $wgLang;
00533 
00534         // Set the globals to the current context
00535         $wgTitle = $title;
00536         $wgOut = $context->getOutput();
00537         $wgRequest = $context->getRequest();
00538         $wgUser = $context->getUser();
00539         $wgLang = $context->getLanguage();
00540 
00541         // The useful part
00542         $ret = self::executePath( $title, $context, true );
00543 
00544         // And restore the old globals
00545         $wgTitle = $oldTitle;
00546         $wgOut = $oldOut;
00547         $wgRequest = $oldRequest;
00548         $wgUser = $oldUser;
00549         $wgLang = $oldLang;
00550 
00551         return $ret;
00552     }
00553 
00561     static function getLocalNameFor( $name, $subpage = false ) {
00562         global $wgContLang;
00563         $aliases = $wgContLang->getSpecialPageAliases();
00564 
00565         if ( isset( $aliases[$name][0] ) ) {
00566             $name = $aliases[$name][0];
00567         } else {
00568             // Try harder in case someone misspelled the correct casing
00569             $found = false;
00570             foreach ( $aliases as $n => $values ) {
00571                 if ( strcasecmp( $name, $n ) === 0 ) {
00572                     wfWarn( "Found alias defined for $n when searching for " .
00573                         "special page aliases for $name. Case mismatch?" );
00574                     $name = $values[0];
00575                     $found = true;
00576                     break;
00577                 }
00578             }
00579             if ( !$found ) {
00580                 wfWarn( "Did not find alias for special page '$name'. " .
00581                     "Perhaps no aliases are defined for it?" );
00582             }
00583         }
00584         if ( $subpage !== false && !is_null( $subpage ) ) {
00585             $name = "$name/$subpage";
00586         }
00587 
00588         return $wgContLang->ucfirst( $name );
00589     }
00590 
00597     static function getTitleForAlias( $alias ) {
00598         $name = self::resolveAlias( $alias );
00599         if ( $name ) {
00600             return SpecialPage::getTitleFor( $name );
00601         } else {
00602             return null;
00603         }
00604     }
00605 }