MediaWiki  REL1_22
SkinTemplate.php
Go to the documentation of this file.
00001 <?php
00030 class MediaWiki_I18N {
00031     var $_context = array();
00032 
00033     function set( $varName, $value ) {
00034         $this->_context[$varName] = $value;
00035     }
00036 
00037     function translate( $value ) {
00038         wfProfileIn( __METHOD__ );
00039 
00040         // Hack for i18n:attributes in PHPTAL 1.0.0 dev version as of 2004-10-23
00041         $value = preg_replace( '/^string:/', '', $value );
00042 
00043         $value = wfMessage( $value )->text();
00044         // interpolate variables
00045         $m = array();
00046         while ( preg_match( '/\$([0-9]*?)/sm', $value, $m ) ) {
00047             list( $src, $var ) = $m;
00048             wfSuppressWarnings();
00049             $varValue = $this->_context[$var];
00050             wfRestoreWarnings();
00051             $value = str_replace( $src, $varValue, $value );
00052         }
00053         wfProfileOut( __METHOD__ );
00054         return $value;
00055     }
00056 }
00057 
00070 class SkinTemplate extends Skin {
00079     var $skinname = 'monobook';
00080 
00085     var $stylename = 'monobook';
00086 
00091     var $template = 'QuickTemplate';
00092 
00097     var $useHeadElement = false;
00098 
00106     function setupSkinUserCss( OutputPage $out ) {
00107         $out->addModuleStyles( array( 'mediawiki.legacy.shared', 'mediawiki.legacy.commonPrint' ) );
00108     }
00109 
00121     function setupTemplate( $classname, $repository = false, $cache_dir = false ) {
00122         return new $classname();
00123     }
00124 
00131     public function getLanguages() {
00132         global $wgHideInterlanguageLinks;
00133         $out = $this->getOutput();
00134         $userLang = $this->getLanguage();
00135 
00136         # Language links
00137         $language_urls = array();
00138 
00139         if ( !$wgHideInterlanguageLinks ) {
00140             foreach ( $out->getLanguageLinks() as $languageLinkText ) {
00141                 $languageLinkParts = explode( ':', $languageLinkText, 2 );
00142                 $class = 'interwiki-' . $languageLinkParts[0];
00143                 unset( $languageLinkParts );
00144                 $languageLinkTitle = Title::newFromText( $languageLinkText );
00145                 if ( $languageLinkTitle ) {
00146                     $ilInterwikiCode = $languageLinkTitle->getInterwiki();
00147                     $ilLangName = Language::fetchLanguageName( $ilInterwikiCode );
00148 
00149                     if ( strval( $ilLangName ) === '' ) {
00150                         $ilLangName = $languageLinkText;
00151                     } else {
00152                         $ilLangName = $this->formatLanguageName( $ilLangName );
00153                     }
00154 
00155                     // CLDR extension or similar is required to localize the language name;
00156                     // otherwise we'll end up with the autonym again.
00157                     $ilLangLocalName = Language::fetchLanguageName( $ilInterwikiCode, $userLang->getCode() );
00158 
00159                     $language_urls[] = array(
00160                         'href' => $languageLinkTitle->getFullURL(),
00161                         'text' => $ilLangName,
00162                         'title' => wfMessage( 'interlanguage-link-title', $languageLinkTitle->getText(), $ilLangLocalName )->text(),
00163                         'class' => $class,
00164                         'lang' => wfBCP47( $ilInterwikiCode ),
00165                         'hreflang' => wfBCP47( $ilInterwikiCode ),
00166                     );
00167                 }
00168             }
00169         }
00170         return $language_urls;
00171     }
00172 
00173     protected function setupTemplateForOutput() {
00174         wfProfileIn( __METHOD__ );
00175 
00176         $request = $this->getRequest();
00177         $user = $this->getUser();
00178         $title = $this->getTitle();
00179 
00180         wfProfileIn( __METHOD__ . '-init' );
00181         $tpl = $this->setupTemplate( $this->template, 'skins' );
00182         wfProfileOut( __METHOD__ . '-init' );
00183 
00184         wfProfileIn( __METHOD__ . '-stuff' );
00185         $this->thispage = $title->getPrefixedDBkey();
00186         $this->titletxt = $title->getPrefixedText();
00187         $this->userpage = $user->getUserPage()->getPrefixedText();
00188         $query = array();
00189         if ( !$request->wasPosted() ) {
00190             $query = $request->getValues();
00191             unset( $query['title'] );
00192             unset( $query['returnto'] );
00193             unset( $query['returntoquery'] );
00194         }
00195         $this->thisquery = wfArrayToCgi( $query );
00196         $this->loggedin = $user->isLoggedIn();
00197         $this->username = $user->getName();
00198 
00199         if ( $this->loggedin || $this->showIPinHeader() ) {
00200             $this->userpageUrlDetails = self::makeUrlDetails( $this->userpage );
00201         } else {
00202             # This won't be used in the standard skins, but we define it to preserve the interface
00203             # To save time, we check for existence
00204             $this->userpageUrlDetails = self::makeKnownUrlDetails( $this->userpage );
00205         }
00206 
00207         wfProfileOut( __METHOD__ . '-stuff' );
00208 
00209         wfProfileOut( __METHOD__ );
00210 
00211         return $tpl;
00212     }
00213 
00219     function outputPage( OutputPage $out = null ) {
00220         global $wgContLang;
00221         global $wgScript, $wgStylePath;
00222         global $wgMimeType, $wgJsMimeType;
00223         global $wgXhtmlNamespaces, $wgHtml5Version;
00224         global $wgDisableCounters, $wgSitename, $wgLogo;
00225         global $wgMaxCredits, $wgShowCreditsIfMax;
00226         global $wgPageShowWatchingUsers;
00227         global $wgArticlePath, $wgScriptPath, $wgServer;
00228 
00229         wfProfileIn( __METHOD__ );
00230         Profiler::instance()->setTemplated( true );
00231 
00232         $oldContext = null;
00233         if ( $out !== null ) {
00234             // @todo Add wfDeprecated in 1.20
00235             $oldContext = $this->getContext();
00236             $this->setContext( $out->getContext() );
00237         }
00238 
00239         $out = $this->getOutput();
00240         $request = $this->getRequest();
00241         $user = $this->getUser();
00242         $title = $this->getTitle();
00243 
00244         wfProfileIn( __METHOD__ . '-init' );
00245         $this->initPage( $out );
00246         wfProfileOut( __METHOD__ . '-init' );
00247 
00248         $tpl = $this->setupTemplateForOutput();
00249 
00250         wfProfileIn( __METHOD__ . '-stuff-head' );
00251         if ( !$this->useHeadElement ) {
00252             $tpl->set( 'pagecss', false );
00253             $tpl->set( 'usercss', false );
00254 
00255             $tpl->set( 'userjs', false );
00256             $tpl->set( 'userjsprev', false );
00257 
00258             $tpl->set( 'jsvarurl', false );
00259 
00260             $tpl->set( 'xhtmldefaultnamespace', 'http://www.w3.org/1999/xhtml' );
00261             $tpl->set( 'xhtmlnamespaces', $wgXhtmlNamespaces );
00262             $tpl->set( 'html5version', $wgHtml5Version );
00263             $tpl->set( 'headlinks', $out->getHeadLinks() );
00264             $tpl->set( 'csslinks', $out->buildCssLinks() );
00265             $tpl->set( 'pageclass', $this->getPageClasses( $title ) );
00266             $tpl->set( 'skinnameclass', ( 'skin-' . Sanitizer::escapeClass( $this->getSkinName() ) ) );
00267         }
00268         wfProfileOut( __METHOD__ . '-stuff-head' );
00269 
00270         wfProfileIn( __METHOD__ . '-stuff2' );
00271         $tpl->set( 'title', $out->getPageTitle() );
00272         $tpl->set( 'pagetitle', $out->getHTMLTitle() );
00273         $tpl->set( 'displaytitle', $out->mPageLinkTitle );
00274 
00275         $tpl->setRef( 'thispage', $this->thispage );
00276         $tpl->setRef( 'titleprefixeddbkey', $this->thispage );
00277         $tpl->set( 'titletext', $title->getText() );
00278         $tpl->set( 'articleid', $title->getArticleID() );
00279 
00280         $tpl->set( 'isarticle', $out->isArticle() );
00281 
00282         $subpagestr = $this->subPageSubtitle();
00283         if ( $subpagestr !== '' ) {
00284             $subpagestr = '<span class="subpages">' . $subpagestr . '</span>';
00285         }
00286         $tpl->set( 'subtitle', $subpagestr . $out->getSubtitle() );
00287 
00288         $undelete = $this->getUndeleteLink();
00289         if ( $undelete === '' ) {
00290             $tpl->set( 'undelete', '' );
00291         } else {
00292             $tpl->set( 'undelete', '<span class="subpages">' . $undelete . '</span>' );
00293         }
00294 
00295         $tpl->set( 'catlinks', $this->getCategories() );
00296         if ( $out->isSyndicated() ) {
00297             $feeds = array();
00298             foreach ( $out->getSyndicationLinks() as $format => $link ) {
00299                 $feeds[$format] = array(
00300                     // Messages: feed-atom, feed-rss
00301                     'text' => $this->msg( "feed-$format" )->text(),
00302                     'href' => $link
00303                 );
00304             }
00305             $tpl->setRef( 'feeds', $feeds );
00306         } else {
00307             $tpl->set( 'feeds', false );
00308         }
00309 
00310         $tpl->setRef( 'mimetype', $wgMimeType );
00311         $tpl->setRef( 'jsmimetype', $wgJsMimeType );
00312         $tpl->set( 'charset', 'UTF-8' );
00313         $tpl->setRef( 'wgScript', $wgScript );
00314         $tpl->setRef( 'skinname', $this->skinname );
00315         $tpl->set( 'skinclass', get_class( $this ) );
00316         $tpl->setRef( 'skin', $this );
00317         $tpl->setRef( 'stylename', $this->stylename );
00318         $tpl->set( 'printable', $out->isPrintable() );
00319         $tpl->set( 'handheld', $request->getBool( 'handheld' ) );
00320         $tpl->setRef( 'loggedin', $this->loggedin );
00321         $tpl->set( 'notspecialpage', !$title->isSpecialPage() );
00322         /* XXX currently unused, might get useful later
00323         $tpl->set( 'editable', ( !$title->isSpecialPage() ) );
00324         $tpl->set( 'exists', $title->getArticleID() != 0 );
00325         $tpl->set( 'watch', $user->isWatched( $title ) ? 'unwatch' : 'watch' );
00326         $tpl->set( 'protect', count( $title->isProtected() ) ? 'unprotect' : 'protect' );
00327         $tpl->set( 'helppage', $this->msg( 'helppage' )->text() );
00328         */
00329         $tpl->set( 'searchaction', $this->escapeSearchLink() );
00330         $tpl->set( 'searchtitle', SpecialPage::getTitleFor( 'Search' )->getPrefixedDBkey() );
00331         $tpl->set( 'search', trim( $request->getVal( 'search' ) ) );
00332         $tpl->setRef( 'stylepath', $wgStylePath );
00333         $tpl->setRef( 'articlepath', $wgArticlePath );
00334         $tpl->setRef( 'scriptpath', $wgScriptPath );
00335         $tpl->setRef( 'serverurl', $wgServer );
00336         $tpl->setRef( 'logopath', $wgLogo );
00337         $tpl->setRef( 'sitename', $wgSitename );
00338 
00339         $userLang = $this->getLanguage();
00340         $userLangCode = $userLang->getHtmlCode();
00341         $userLangDir = $userLang->getDir();
00342 
00343         $tpl->set( 'lang', $userLangCode );
00344         $tpl->set( 'dir', $userLangDir );
00345         $tpl->set( 'rtl', $userLang->isRTL() );
00346 
00347         $tpl->set( 'capitalizeallnouns', $userLang->capitalizeAllNouns() ? ' capitalize-all-nouns' : '' );
00348         $tpl->set( 'showjumplinks', true ); // showjumplinks preference has been removed
00349         $tpl->set( 'username', $this->loggedin ? $this->username : null );
00350         $tpl->setRef( 'userpage', $this->userpage );
00351         $tpl->setRef( 'userpageurl', $this->userpageUrlDetails['href'] );
00352         $tpl->set( 'userlang', $userLangCode );
00353 
00354         // Users can have their language set differently than the
00355         // content of the wiki. For these users, tell the web browser
00356         // that interface elements are in a different language.
00357         $tpl->set( 'userlangattributes', '' );
00358         $tpl->set( 'specialpageattributes', '' ); # obsolete
00359 
00360         if ( $userLangCode !== $wgContLang->getHtmlCode() || $userLangDir !== $wgContLang->getDir() ) {
00361             $escUserlang = htmlspecialchars( $userLangCode );
00362             $escUserdir = htmlspecialchars( $userLangDir );
00363             // Attributes must be in double quotes because htmlspecialchars() doesn't
00364             // escape single quotes
00365             $attrs = " lang=\"$escUserlang\" dir=\"$escUserdir\"";
00366             $tpl->set( 'userlangattributes', $attrs );
00367         }
00368 
00369         wfProfileOut( __METHOD__ . '-stuff2' );
00370 
00371         wfProfileIn( __METHOD__ . '-stuff3' );
00372         $tpl->set( 'newtalk', $this->getNewtalks() );
00373         $tpl->set( 'logo', $this->logoText() );
00374 
00375         $tpl->set( 'copyright', false );
00376         $tpl->set( 'viewcount', false );
00377         $tpl->set( 'lastmod', false );
00378         $tpl->set( 'credits', false );
00379         $tpl->set( 'numberofwatchingusers', false );
00380         if ( $out->isArticle() && $title->exists() ) {
00381             if ( $this->isRevisionCurrent() ) {
00382                 if ( !$wgDisableCounters ) {
00383                     $viewcount = $this->getWikiPage()->getCount();
00384                     if ( $viewcount ) {
00385                         $tpl->set( 'viewcount', $this->msg( 'viewcount' )->numParams( $viewcount )->parse() );
00386                     }
00387                 }
00388 
00389                 if ( $wgPageShowWatchingUsers ) {
00390                     $dbr = wfGetDB( DB_SLAVE );
00391                     $num = $dbr->selectField( 'watchlist', 'COUNT(*)',
00392                         array( 'wl_title' => $title->getDBkey(), 'wl_namespace' => $title->getNamespace() ),
00393                         __METHOD__
00394                     );
00395                     if ( $num > 0 ) {
00396                         $tpl->set( 'numberofwatchingusers',
00397                             $this->msg( 'number_of_watching_users_pageview' )->numParams( $num )->parse()
00398                         );
00399                     }
00400                 }
00401 
00402                 if ( $wgMaxCredits != 0 ) {
00403                     $tpl->set( 'credits', Action::factory( 'credits', $this->getWikiPage(),
00404                         $this->getContext() )->getCredits( $wgMaxCredits, $wgShowCreditsIfMax ) );
00405                 } else {
00406                     $tpl->set( 'lastmod', $this->lastModified() );
00407                 }
00408             }
00409             $tpl->set( 'copyright', $this->getCopyright() );
00410         }
00411         wfProfileOut( __METHOD__ . '-stuff3' );
00412 
00413         wfProfileIn( __METHOD__ . '-stuff4' );
00414         $tpl->set( 'copyrightico', $this->getCopyrightIcon() );
00415         $tpl->set( 'poweredbyico', $this->getPoweredBy() );
00416         $tpl->set( 'disclaimer', $this->disclaimerLink() );
00417         $tpl->set( 'privacy', $this->privacyLink() );
00418         $tpl->set( 'about', $this->aboutLink() );
00419 
00420         $tpl->set( 'footerlinks', array(
00421             'info' => array(
00422                 'lastmod',
00423                 'viewcount',
00424                 'numberofwatchingusers',
00425                 'credits',
00426                 'copyright',
00427             ),
00428             'places' => array(
00429                 'privacy',
00430                 'about',
00431                 'disclaimer',
00432             ),
00433         ) );
00434 
00435         global $wgFooterIcons;
00436         $tpl->set( 'footericons', $wgFooterIcons );
00437         foreach ( $tpl->data['footericons'] as $footerIconsKey => &$footerIconsBlock ) {
00438             if ( count( $footerIconsBlock ) > 0 ) {
00439                 foreach ( $footerIconsBlock as &$footerIcon ) {
00440                     if ( isset( $footerIcon['src'] ) ) {
00441                         if ( !isset( $footerIcon['width'] ) ) {
00442                             $footerIcon['width'] = 88;
00443                         }
00444                         if ( !isset( $footerIcon['height'] ) ) {
00445                             $footerIcon['height'] = 31;
00446                         }
00447                     }
00448                 }
00449             } else {
00450                 unset( $tpl->data['footericons'][$footerIconsKey] );
00451             }
00452         }
00453 
00454         $tpl->set( 'sitenotice', $this->getSiteNotice() );
00455         $tpl->set( 'bottomscripts', $this->bottomScripts() );
00456         $tpl->set( 'printfooter', $this->printSource() );
00457 
00458         # An ID that includes the actual body text; without categories, contentSub, ...
00459         $realBodyAttribs = array( 'id' => 'mw-content-text' );
00460 
00461         # Add a mw-content-ltr/rtl class to be able to style based on text direction
00462         # when the content is different from the UI language, i.e.:
00463         # not for special pages or file pages AND only when viewing AND if the page exists
00464         # (or is in MW namespace, because that has default content)
00465         if ( !in_array( $title->getNamespace(), array( NS_SPECIAL, NS_FILE ) ) &&
00466             Action::getActionName( $this ) === 'view' &&
00467             ( $title->exists() || $title->getNamespace() == NS_MEDIAWIKI ) ) {
00468             $pageLang = $title->getPageViewLanguage();
00469             $realBodyAttribs['lang'] = $pageLang->getHtmlCode();
00470             $realBodyAttribs['dir'] = $pageLang->getDir();
00471             $realBodyAttribs['class'] = 'mw-content-' . $pageLang->getDir();
00472         }
00473 
00474         $out->mBodytext = Html::rawElement( 'div', $realBodyAttribs, $out->mBodytext );
00475         $tpl->setRef( 'bodytext', $out->mBodytext );
00476 
00477         $language_urls = $this->getLanguages();
00478         if ( count( $language_urls ) ) {
00479             $tpl->setRef( 'language_urls', $language_urls );
00480         } else {
00481             $tpl->set( 'language_urls', false );
00482         }
00483         wfProfileOut( __METHOD__ . '-stuff4' );
00484 
00485         wfProfileIn( __METHOD__ . '-stuff5' );
00486         # Personal toolbar
00487         $tpl->set( 'personal_urls', $this->buildPersonalUrls() );
00488         $content_navigation = $this->buildContentNavigationUrls();
00489         $content_actions = $this->buildContentActionUrls( $content_navigation );
00490         $tpl->setRef( 'content_navigation', $content_navigation );
00491         $tpl->setRef( 'content_actions', $content_actions );
00492 
00493         $tpl->set( 'sidebar', $this->buildSidebar() );
00494         $tpl->set( 'nav_urls', $this->buildNavUrls() );
00495 
00496         // Set the head scripts near the end, in case the above actions resulted in added scripts
00497         if ( $this->useHeadElement ) {
00498             $tpl->set( 'headelement', $out->headElement( $this ) );
00499         } else {
00500             $tpl->set( 'headscripts', $out->getHeadScripts() . $out->getHeadItems() );
00501         }
00502 
00503         $tpl->set( 'debug', '' );
00504         $tpl->set( 'debughtml', $this->generateDebugHTML() );
00505         $tpl->set( 'reporttime', wfReportTime() );
00506 
00507         // original version by hansm
00508         if ( !wfRunHooks( 'SkinTemplateOutputPageBeforeExec', array( &$this, &$tpl ) ) ) {
00509             wfDebug( __METHOD__ . ": Hook SkinTemplateOutputPageBeforeExec broke outputPage execution!\n" );
00510         }
00511 
00512         // Set the bodytext to another key so that skins can just output it on it's own
00513         // and output printfooter and debughtml separately
00514         $tpl->set( 'bodycontent', $tpl->data['bodytext'] );
00515 
00516         // Append printfooter and debughtml onto bodytext so that skins that were already
00517         // using bodytext before they were split out don't suddenly start not outputting information
00518         $tpl->data['bodytext'] .= Html::rawElement( 'div', array( 'class' => 'printfooter' ), "\n{$tpl->data['printfooter']}" ) . "\n";
00519         $tpl->data['bodytext'] .= $tpl->data['debughtml'];
00520 
00521         // allow extensions adding stuff after the page content.
00522         // See Skin::afterContentHook() for further documentation.
00523         $tpl->set( 'dataAfterContent', $this->afterContentHook() );
00524         wfProfileOut( __METHOD__ . '-stuff5' );
00525 
00526         // execute template
00527         wfProfileIn( __METHOD__ . '-execute' );
00528         $res = $tpl->execute();
00529         wfProfileOut( __METHOD__ . '-execute' );
00530 
00531         // result may be an error
00532         $this->printOrError( $res );
00533 
00534         if ( $oldContext ) {
00535             $this->setContext( $oldContext );
00536         }
00537         wfProfileOut( __METHOD__ );
00538     }
00539 
00544     public function getPersonalToolsList() {
00545         $tpl = $this->setupTemplateForOutput();
00546         $tpl->set( 'personal_urls', $this->buildPersonalUrls() );
00547         $html = '';
00548         foreach ( $tpl->getPersonalTools() as $key => $item ) {
00549             $html .= $tpl->makeListItem( $key, $item );
00550         }
00551         return $html;
00552     }
00553 
00562     function formatLanguageName( $name ) {
00563         return $this->getLanguage()->ucfirst( $name );
00564     }
00565 
00574     function printOrError( $str ) {
00575         echo $str;
00576     }
00577 
00587     function useCombinedLoginLink() {
00588         global $wgUseCombinedLoginLink;
00589         return $wgUseCombinedLoginLink;
00590     }
00591 
00596     protected function buildPersonalUrls() {
00597         $title = $this->getTitle();
00598         $request = $this->getRequest();
00599         $pageurl = $title->getLocalURL();
00600         wfProfileIn( __METHOD__ );
00601 
00602         /* set up the default links for the personal toolbar */
00603         $personal_urls = array();
00604 
00605         # Due to bug 32276, if a user does not have read permissions,
00606         # $this->getTitle() will just give Special:Badtitle, which is
00607         # not especially useful as a returnto parameter. Use the title
00608         # from the request instead, if there was one.
00609         if ( $this->getUser()->isAllowed( 'read' ) ) {
00610             $page = $this->getTitle();
00611         } else {
00612             $page = Title::newFromText( $request->getVal( 'title', '' ) );
00613         }
00614         $page = $request->getVal( 'returnto', $page );
00615         $a = array();
00616         if ( strval( $page ) !== '' ) {
00617             $a['returnto'] = $page;
00618             $query = $request->getVal( 'returntoquery', $this->thisquery );
00619             if ( $query != '' ) {
00620                 $a['returntoquery'] = $query;
00621             }
00622         }
00623 
00624         $returnto = wfArrayToCgi( $a );
00625         if ( $this->loggedin ) {
00626             $personal_urls['userpage'] = array(
00627                 'text' => $this->username,
00628                 'href' => &$this->userpageUrlDetails['href'],
00629                 'class' => $this->userpageUrlDetails['exists'] ? false : 'new',
00630                 'active' => ( $this->userpageUrlDetails['href'] == $pageurl ),
00631                 'dir' => 'auto'
00632             );
00633             $usertalkUrlDetails = $this->makeTalkUrlDetails( $this->userpage );
00634             $personal_urls['mytalk'] = array(
00635                 'text' => $this->msg( 'mytalk' )->text(),
00636                 'href' => &$usertalkUrlDetails['href'],
00637                 'class' => $usertalkUrlDetails['exists'] ? false : 'new',
00638                 'active' => ( $usertalkUrlDetails['href'] == $pageurl )
00639             );
00640             $href = self::makeSpecialUrl( 'Preferences' );
00641             $personal_urls['preferences'] = array(
00642                 'text' => $this->msg( 'mypreferences' )->text(),
00643                 'href' => $href,
00644                 'active' => ( $href == $pageurl )
00645             );
00646 
00647             if ( $this->getUser()->isAllowed( 'viewmywatchlist' ) ) {
00648                 $href = self::makeSpecialUrl( 'Watchlist' );
00649                 $personal_urls['watchlist'] = array(
00650                     'text' => $this->msg( 'mywatchlist' )->text(),
00651                     'href' => $href,
00652                     'active' => ( $href == $pageurl )
00653                 );
00654             }
00655 
00656             # We need to do an explicit check for Special:Contributions, as we
00657             # have to match both the title, and the target, which could come
00658             # from request values (Special:Contributions?target=Jimbo_Wales)
00659             # or be specified in "sub page" form
00660             # (Special:Contributions/Jimbo_Wales). The plot
00661             # thickens, because the Title object is altered for special pages,
00662             # so it doesn't contain the original alias-with-subpage.
00663             $origTitle = Title::newFromText( $request->getText( 'title' ) );
00664             if ( $origTitle instanceof Title && $origTitle->isSpecialPage() ) {
00665                 list( $spName, $spPar ) = SpecialPageFactory::resolveAlias( $origTitle->getText() );
00666                 $active = $spName == 'Contributions'
00667                     && ( ( $spPar && $spPar == $this->username )
00668                         || $request->getText( 'target' ) == $this->username );
00669             } else {
00670                 $active = false;
00671             }
00672 
00673             $href = self::makeSpecialUrlSubpage( 'Contributions', $this->username );
00674             $personal_urls['mycontris'] = array(
00675                 'text' => $this->msg( 'mycontris' )->text(),
00676                 'href' => $href,
00677                 'active' => $active
00678             );
00679             $personal_urls['logout'] = array(
00680                 'text' => $this->msg( 'userlogout' )->text(),
00681                 'href' => self::makeSpecialUrl( 'Userlogout',
00682                     // userlogout link must always contain an & character, otherwise we might not be able
00683                     // to detect a buggy precaching proxy (bug 17790)
00684                     $title->isSpecial( 'Preferences' ) ? 'noreturnto' : $returnto
00685                 ),
00686                 'active' => false
00687             );
00688         } else {
00689             $useCombinedLoginLink = $this->useCombinedLoginLink();
00690             $loginlink = $this->getUser()->isAllowed( 'createaccount' ) && $useCombinedLoginLink
00691                 ? 'nav-login-createaccount'
00692                 : 'login';
00693             $is_signup = $request->getText( 'type' ) == 'signup';
00694 
00695             $login_id = $this->showIPinHeader() ? 'anonlogin' : 'login';
00696             $login_url = array(
00697                 'text' => $this->msg( $loginlink )->text(),
00698                 'href' => self::makeSpecialUrl( 'Userlogin', $returnto ),
00699                 'active' => $title->isSpecial( 'Userlogin' ) && ( $loginlink == 'nav-login-createaccount' || !$is_signup ),
00700             );
00701             $createaccount_url = array(
00702                 'text' => $this->msg( 'createaccount' )->text(),
00703                 'href' => self::makeSpecialUrl( 'Userlogin', "$returnto&type=signup" ),
00704                 'active' => $title->isSpecial( 'Userlogin' ) && $is_signup,
00705             );
00706 
00707             if ( $this->showIPinHeader() ) {
00708                 $href = &$this->userpageUrlDetails['href'];
00709                 $personal_urls['anonuserpage'] = array(
00710                     'text' => $this->username,
00711                     'href' => $href,
00712                     'class' => $this->userpageUrlDetails['exists'] ? false : 'new',
00713                     'active' => ( $pageurl == $href )
00714                 );
00715                 $usertalkUrlDetails = $this->makeTalkUrlDetails( $this->userpage );
00716                 $href = &$usertalkUrlDetails['href'];
00717                 $personal_urls['anontalk'] = array(
00718                     'text' => $this->msg( 'anontalk' )->text(),
00719                     'href' => $href,
00720                     'class' => $usertalkUrlDetails['exists'] ? false : 'new',
00721                     'active' => ( $pageurl == $href )
00722                 );
00723             }
00724 
00725             if ( $this->getUser()->isAllowed( 'createaccount' ) && !$useCombinedLoginLink ) {
00726                 $personal_urls['createaccount'] = $createaccount_url;
00727             }
00728 
00729             $personal_urls[$login_id] = $login_url;
00730         }
00731 
00732         wfRunHooks( 'PersonalUrls', array( &$personal_urls, &$title ) );
00733         wfProfileOut( __METHOD__ );
00734         return $personal_urls;
00735     }
00736 
00748     function tabAction( $title, $message, $selected, $query = '', $checkEdit = false ) {
00749         $classes = array();
00750         if ( $selected ) {
00751             $classes[] = 'selected';
00752         }
00753         if ( $checkEdit && !$title->isKnown() ) {
00754             $classes[] = 'new';
00755             if ( $query !== '' ) {
00756                 $query = 'action=edit&redlink=1&' . $query;
00757             } else {
00758                 $query = 'action=edit&redlink=1';
00759             }
00760         }
00761 
00762         // wfMessageFallback will nicely accept $message as an array of fallbacks
00763         // or just a single key
00764         $msg = wfMessageFallback( $message )->setContext( $this->getContext() );
00765         if ( is_array( $message ) ) {
00766             // for hook compatibility just keep the last message name
00767             $message = end( $message );
00768         }
00769         if ( $msg->exists() ) {
00770             $text = $msg->text();
00771         } else {
00772             global $wgContLang;
00773             $text = $wgContLang->getFormattedNsText(
00774                 MWNamespace::getSubject( $title->getNamespace() ) );
00775         }
00776 
00777         $result = array();
00778         if ( !wfRunHooks( 'SkinTemplateTabAction', array( &$this,
00779                 $title, $message, $selected, $checkEdit,
00780                 &$classes, &$query, &$text, &$result ) ) ) {
00781             return $result;
00782         }
00783 
00784         return array(
00785             'class' => implode( ' ', $classes ),
00786             'text' => $text,
00787             'href' => $title->getLocalURL( $query ),
00788             'primary' => true );
00789     }
00790 
00791     function makeTalkUrlDetails( $name, $urlaction = '' ) {
00792         $title = Title::newFromText( $name );
00793         if ( !is_object( $title ) ) {
00794             throw new MWException( __METHOD__ . " given invalid pagename $name" );
00795         }
00796         $title = $title->getTalkPage();
00797         self::checkTitle( $title, $name );
00798         return array(
00799             'href' => $title->getLocalURL( $urlaction ),
00800             'exists' => $title->getArticleID() != 0,
00801         );
00802     }
00803 
00804     function makeArticleUrlDetails( $name, $urlaction = '' ) {
00805         $title = Title::newFromText( $name );
00806         $title = $title->getSubjectPage();
00807         self::checkTitle( $title, $name );
00808         return array(
00809             'href' => $title->getLocalURL( $urlaction ),
00810             'exists' => $title->getArticleID() != 0,
00811         );
00812     }
00813 
00847     protected function buildContentNavigationUrls() {
00848         global $wgDisableLangConversion;
00849 
00850         wfProfileIn( __METHOD__ );
00851 
00852         // Display tabs for the relevant title rather than always the title itself
00853         $title = $this->getRelevantTitle();
00854         $onPage = $title->equals( $this->getTitle() );
00855 
00856         $out = $this->getOutput();
00857         $request = $this->getRequest();
00858         $user = $this->getUser();
00859 
00860         $content_navigation = array(
00861             'namespaces' => array(),
00862             'views' => array(),
00863             'actions' => array(),
00864             'variants' => array()
00865         );
00866 
00867         // parameters
00868         $action = $request->getVal( 'action', 'view' );
00869 
00870         $userCanRead = $title->quickUserCan( 'read', $user );
00871 
00872         $preventActiveTabs = false;
00873         wfRunHooks( 'SkinTemplatePreventOtherActiveTabs', array( &$this, &$preventActiveTabs ) );
00874 
00875         // Checks if page is some kind of content
00876         if ( $title->canExist() ) {
00877             // Gets page objects for the related namespaces
00878             $subjectPage = $title->getSubjectPage();
00879             $talkPage = $title->getTalkPage();
00880 
00881             // Determines if this is a talk page
00882             $isTalk = $title->isTalkPage();
00883 
00884             // Generates XML IDs from namespace names
00885             $subjectId = $title->getNamespaceKey( '' );
00886 
00887             if ( $subjectId == 'main' ) {
00888                 $talkId = 'talk';
00889             } else {
00890                 $talkId = "{$subjectId}_talk";
00891             }
00892 
00893             $skname = $this->skinname;
00894 
00895             // Adds namespace links
00896             $subjectMsg = array( "nstab-$subjectId" );
00897             if ( $subjectPage->isMainPage() ) {
00898                 array_unshift( $subjectMsg, 'mainpage-nstab' );
00899             }
00900             $content_navigation['namespaces'][$subjectId] = $this->tabAction(
00901                 $subjectPage, $subjectMsg, !$isTalk && !$preventActiveTabs, '', $userCanRead
00902             );
00903             $content_navigation['namespaces'][$subjectId]['context'] = 'subject';
00904             $content_navigation['namespaces'][$talkId] = $this->tabAction(
00905                 $talkPage, array( "nstab-$talkId", 'talk' ), $isTalk && !$preventActiveTabs, '', $userCanRead
00906             );
00907             $content_navigation['namespaces'][$talkId]['context'] = 'talk';
00908 
00909             if ( $userCanRead ) {
00910                 // Adds view view link
00911                 if ( $title->exists() ) {
00912                     $content_navigation['views']['view'] = $this->tabAction(
00913                         $isTalk ? $talkPage : $subjectPage,
00914                         array( "$skname-view-view", 'view' ),
00915                         ( $onPage && ( $action == 'view' || $action == 'purge' ) ), '', true
00916                     );
00917                     // signal to hide this from simple content_actions
00918                     $content_navigation['views']['view']['redundant'] = true;
00919                 }
00920 
00921                 wfProfileIn( __METHOD__ . '-edit' );
00922 
00923                 // Checks if user can edit the current page if it exists or create it otherwise
00924                 if ( $title->quickUserCan( 'edit', $user ) && ( $title->exists() || $title->quickUserCan( 'create', $user ) ) ) {
00925                     // Builds CSS class for talk page links
00926                     $isTalkClass = $isTalk ? ' istalk' : '';
00927                     // Whether the user is editing the page
00928                     $isEditing = $onPage && ( $action == 'edit' || $action == 'submit' );
00929                     // Whether to show the "Add a new section" tab
00930                     // Checks if this is a current rev of talk page and is not forced to be hidden
00931                     $showNewSection = !$out->forceHideNewSectionLink()
00932                         && ( ( $isTalk && $this->isRevisionCurrent() ) || $out->showNewSectionLink() );
00933                     $section = $request->getVal( 'section' );
00934 
00935                     $msgKey = $title->exists() || ( $title->getNamespace() == NS_MEDIAWIKI && $title->getDefaultMessageText() !== false ) ?
00936                         'edit' : 'create';
00937                     $content_navigation['views']['edit'] = array(
00938                         'class' => ( $isEditing && ( $section !== 'new' || !$showNewSection ) ? 'selected' : '' ) . $isTalkClass,
00939                         'text' => wfMessageFallback( "$skname-view-$msgKey", $msgKey )->setContext( $this->getContext() )->text(),
00940                         'href' => $title->getLocalURL( $this->editUrlOptions() ),
00941                         'primary' => true, // don't collapse this in vector
00942                     );
00943 
00944                     // section link
00945                     if ( $showNewSection ) {
00946                         // Adds new section link
00947                         //$content_navigation['actions']['addsection']
00948                         $content_navigation['views']['addsection'] = array(
00949                             'class' => ( $isEditing && $section == 'new' ) ? 'selected' : false,
00950                             'text' => wfMessageFallback( "$skname-action-addsection", 'addsection' )->setContext( $this->getContext() )->text(),
00951                             'href' => $title->getLocalURL( 'action=edit&section=new' )
00952                         );
00953                     }
00954                 // Checks if the page has some kind of viewable content
00955                 } elseif ( $title->hasSourceText() ) {
00956                     // Adds view source view link
00957                     $content_navigation['views']['viewsource'] = array(
00958                         'class' => ( $onPage && $action == 'edit' ) ? 'selected' : false,
00959                         'text' => wfMessageFallback( "$skname-action-viewsource", 'viewsource' )->setContext( $this->getContext() )->text(),
00960                         'href' => $title->getLocalURL( $this->editUrlOptions() ),
00961                         'primary' => true, // don't collapse this in vector
00962                     );
00963                 }
00964                 wfProfileOut( __METHOD__ . '-edit' );
00965 
00966                 wfProfileIn( __METHOD__ . '-live' );
00967                 // Checks if the page exists
00968                 if ( $title->exists() ) {
00969                     // Adds history view link
00970                     $content_navigation['views']['history'] = array(
00971                         'class' => ( $onPage && $action == 'history' ) ? 'selected' : false,
00972                         'text' => wfMessageFallback( "$skname-view-history", 'history_short' )->setContext( $this->getContext() )->text(),
00973                         'href' => $title->getLocalURL( 'action=history' ),
00974                         'rel' => 'archives',
00975                     );
00976 
00977                     if ( $title->quickUserCan( 'delete', $user ) ) {
00978                         $content_navigation['actions']['delete'] = array(
00979                             'class' => ( $onPage && $action == 'delete' ) ? 'selected' : false,
00980                             'text' => wfMessageFallback( "$skname-action-delete", 'delete' )->setContext( $this->getContext() )->text(),
00981                             'href' => $title->getLocalURL( 'action=delete' )
00982                         );
00983                     }
00984 
00985                     if ( $title->quickUserCan( 'move', $user ) ) {
00986                         $moveTitle = SpecialPage::getTitleFor( 'Movepage', $title->getPrefixedDBkey() );
00987                         $content_navigation['actions']['move'] = array(
00988                             'class' => $this->getTitle()->isSpecial( 'Movepage' ) ? 'selected' : false,
00989                             'text' => wfMessageFallback( "$skname-action-move", 'move' )->setContext( $this->getContext() )->text(),
00990                             'href' => $moveTitle->getLocalURL()
00991                         );
00992                     }
00993                 } else {
00994                     // article doesn't exist or is deleted
00995                     if ( $user->isAllowed( 'deletedhistory' ) ) {
00996                         $n = $title->isDeleted();
00997                         if ( $n ) {
00998                             $undelTitle = SpecialPage::getTitleFor( 'Undelete' );
00999                             // If the user can't undelete but can view deleted history show them a "View .. deleted" tab instead
01000                             $msgKey = $user->isAllowed( 'undelete' ) ? 'undelete' : 'viewdeleted';
01001                             $content_navigation['actions']['undelete'] = array(
01002                                 'class' => $this->getTitle()->isSpecial( 'Undelete' ) ? 'selected' : false,
01003                                 'text' => wfMessageFallback( "$skname-action-$msgKey", "{$msgKey}_short" )
01004                                     ->setContext( $this->getContext() )->numParams( $n )->text(),
01005                                 'href' => $undelTitle->getLocalURL( array( 'target' => $title->getPrefixedDBkey() ) )
01006                             );
01007                         }
01008                     }
01009                 }
01010 
01011                 if ( $title->getNamespace() !== NS_MEDIAWIKI && $title->quickUserCan( 'protect', $user ) && $title->getRestrictionTypes() ) {
01012                     $mode = $title->isProtected() ? 'unprotect' : 'protect';
01013                     $content_navigation['actions'][$mode] = array(
01014                         'class' => ( $onPage && $action == $mode ) ? 'selected' : false,
01015                         'text' => wfMessageFallback( "$skname-action-$mode", $mode )->setContext( $this->getContext() )->text(),
01016                         'href' => $title->getLocalURL( "action=$mode" )
01017                     );
01018                 }
01019 
01020                 wfProfileOut( __METHOD__ . '-live' );
01021 
01022                 // Checks if the user is logged in
01023                 if ( $this->loggedin && $user->isAllowedAll( 'viewmywatchlist', 'editmywatchlist' ) ) {
01033                     $mode = $user->isWatched( $title ) ? 'unwatch' : 'watch';
01034                     $token = WatchAction::getWatchToken( $title, $user, $mode );
01035                     $content_navigation['actions'][$mode] = array(
01036                         'class' => $onPage && ( $action == 'watch' || $action == 'unwatch' ) ? 'selected' : false,
01037                         // uses 'watch' or 'unwatch' message
01038                         'text' => $this->msg( $mode )->text(),
01039                         'href' => $title->getLocalURL( array( 'action' => $mode, 'token' => $token ) )
01040                     );
01041                 }
01042             }
01043 
01044             wfRunHooks( 'SkinTemplateNavigation', array( &$this, &$content_navigation ) );
01045 
01046             if ( $userCanRead && !$wgDisableLangConversion ) {
01047                 $pageLang = $title->getPageLanguage();
01048                 // Gets list of language variants
01049                 $variants = $pageLang->getVariants();
01050                 // Checks that language conversion is enabled and variants exist
01051                 // And if it is not in the special namespace
01052                 if ( count( $variants ) > 1 ) {
01053                     // Gets preferred variant (note that user preference is
01054                     // only possible for wiki content language variant)
01055                     $preferred = $pageLang->getPreferredVariant();
01056                     if ( Action::getActionName( $this ) === 'view' ) {
01057                         $params = $request->getQueryValues();
01058                         unset( $params['title'] );
01059                     } else {
01060                         $params = array();
01061                     }
01062                     // Loops over each variant
01063                     foreach ( $variants as $code ) {
01064                         // Gets variant name from language code
01065                         $varname = $pageLang->getVariantname( $code );
01066                         // Appends variant link
01067                         $content_navigation['variants'][] = array(
01068                             'class' => ( $code == $preferred ) ? 'selected' : false,
01069                             'text' => $varname,
01070                             'href' => $title->getLocalURL( array( 'variant' => $code ) + $params ),
01071                             'lang' => wfBCP47( $code ),
01072                             'hreflang' => wfBCP47( $code ),
01073                         );
01074                     }
01075                 }
01076             }
01077         } else {
01078             // If it's not content, it's got to be a special page
01079             $content_navigation['namespaces']['special'] = array(
01080                 'class' => 'selected',
01081                 'text' => $this->msg( 'nstab-special' )->text(),
01082                 'href' => $request->getRequestURL(), // @see: bug 2457, bug 2510
01083                 'context' => 'subject'
01084             );
01085 
01086             wfRunHooks( 'SkinTemplateNavigation::SpecialPage',
01087                 array( &$this, &$content_navigation ) );
01088         }
01089 
01090         // Equiv to SkinTemplateContentActions
01091         wfRunHooks( 'SkinTemplateNavigation::Universal', array( &$this, &$content_navigation ) );
01092 
01093         // Setup xml ids and tooltip info
01094         foreach ( $content_navigation as $section => &$links ) {
01095             foreach ( $links as $key => &$link ) {
01096                 $xmlID = $key;
01097                 if ( isset( $link['context'] ) && $link['context'] == 'subject' ) {
01098                     $xmlID = 'ca-nstab-' . $xmlID;
01099                 } elseif ( isset( $link['context'] ) && $link['context'] == 'talk' ) {
01100                     $xmlID = 'ca-talk';
01101                 } elseif ( $section == 'variants' ) {
01102                     $xmlID = 'ca-varlang-' . $xmlID;
01103                 } else {
01104                     $xmlID = 'ca-' . $xmlID;
01105                 }
01106                 $link['id'] = $xmlID;
01107             }
01108         }
01109 
01110         # We don't want to give the watch tab an accesskey if the
01111         # page is being edited, because that conflicts with the
01112         # accesskey on the watch checkbox.  We also don't want to
01113         # give the edit tab an accesskey, because that's fairly
01114         # superfluous and conflicts with an accesskey (Ctrl-E) often
01115         # used for editing in Safari.
01116         if ( in_array( $action, array( 'edit', 'submit' ) ) ) {
01117             if ( isset( $content_navigation['views']['edit'] ) ) {
01118                 $content_navigation['views']['edit']['tooltiponly'] = true;
01119             }
01120             if ( isset( $content_navigation['actions']['watch'] ) ) {
01121                 $content_navigation['actions']['watch']['tooltiponly'] = true;
01122             }
01123             if ( isset( $content_navigation['actions']['unwatch'] ) ) {
01124                 $content_navigation['actions']['unwatch']['tooltiponly'] = true;
01125             }
01126         }
01127 
01128         wfProfileOut( __METHOD__ );
01129 
01130         return $content_navigation;
01131     }
01132 
01138     function buildContentActionUrls( $content_navigation ) {
01139 
01140         wfProfileIn( __METHOD__ );
01141 
01142         // content_actions has been replaced with content_navigation for backwards
01143         // compatibility and also for skins that just want simple tabs content_actions
01144         // is now built by flattening the content_navigation arrays into one
01145 
01146         $content_actions = array();
01147 
01148         foreach ( $content_navigation as $links ) {
01149 
01150             foreach ( $links as $key => $value ) {
01151 
01152                 if ( isset( $value['redundant'] ) && $value['redundant'] ) {
01153                     // Redundant tabs are dropped from content_actions
01154                     continue;
01155                 }
01156 
01157                 // content_actions used to have ids built using the "ca-$key" pattern
01158                 // so the xmlID based id is much closer to the actual $key that we want
01159                 // for that reason we'll just strip out the ca- if present and use
01160                 // the latter potion of the "id" as the $key
01161                 if ( isset( $value['id'] ) && substr( $value['id'], 0, 3 ) == 'ca-' ) {
01162                     $key = substr( $value['id'], 3 );
01163                 }
01164 
01165                 if ( isset( $content_actions[$key] ) ) {
01166                     wfDebug( __METHOD__ . ": Found a duplicate key for $key while flattening content_navigation into content_actions." );
01167                     continue;
01168                 }
01169 
01170                 $content_actions[$key] = $value;
01171 
01172             }
01173 
01174         }
01175 
01176         wfProfileOut( __METHOD__ );
01177 
01178         return $content_actions;
01179     }
01180 
01186     protected function buildNavUrls() {
01187         global $wgUploadNavigationUrl;
01188 
01189         wfProfileIn( __METHOD__ );
01190 
01191         $out = $this->getOutput();
01192         $request = $this->getRequest();
01193 
01194         $nav_urls = array();
01195         $nav_urls['mainpage'] = array( 'href' => self::makeMainPageUrl() );
01196         if ( $wgUploadNavigationUrl ) {
01197             $nav_urls['upload'] = array( 'href' => $wgUploadNavigationUrl );
01198         } elseif ( UploadBase::isEnabled() && UploadBase::isAllowed( $this->getUser() ) === true ) {
01199             $nav_urls['upload'] = array( 'href' => self::makeSpecialUrl( 'Upload' ) );
01200         } else {
01201             $nav_urls['upload'] = false;
01202         }
01203         $nav_urls['specialpages'] = array( 'href' => self::makeSpecialUrl( 'Specialpages' ) );
01204 
01205         $nav_urls['print'] = false;
01206         $nav_urls['permalink'] = false;
01207         $nav_urls['info'] = false;
01208         $nav_urls['whatlinkshere'] = false;
01209         $nav_urls['recentchangeslinked'] = false;
01210         $nav_urls['contributions'] = false;
01211         $nav_urls['log'] = false;
01212         $nav_urls['blockip'] = false;
01213         $nav_urls['emailuser'] = false;
01214         $nav_urls['userrights'] = false;
01215 
01216         // A print stylesheet is attached to all pages, but nobody ever
01217         // figures that out. :)  Add a link...
01218         if ( !$out->isPrintable() && ( $out->isArticle() || $this->getTitle()->isSpecialPage() ) ) {
01219             $nav_urls['print'] = array(
01220                 'text' => $this->msg( 'printableversion' )->text(),
01221                 'href' => $this->getTitle()->getLocalURL(
01222                     $request->appendQueryValue( 'printable', 'yes', true ) )
01223             );
01224         }
01225 
01226         if ( $out->isArticle() ) {
01227             // Also add a "permalink" while we're at it
01228             $revid = $this->getRevisionId();
01229             if ( $revid ) {
01230                 $nav_urls['permalink'] = array(
01231                     'text' => $this->msg( 'permalink' )->text(),
01232                     'href' => $this->getTitle()->getLocalURL( "oldid=$revid" )
01233                 );
01234             }
01235 
01236             // Use the copy of revision ID in case this undocumented, shady hook tries to mess with internals
01237             wfRunHooks( 'SkinTemplateBuildNavUrlsNav_urlsAfterPermalink',
01238                 array( &$this, &$nav_urls, &$revid, &$revid ) );
01239         }
01240 
01241         if ( $out->isArticleRelated() ) {
01242             $nav_urls['whatlinkshere'] = array(
01243                 'href' => SpecialPage::getTitleFor( 'Whatlinkshere', $this->thispage )->getLocalURL()
01244             );
01245 
01246             $nav_urls['info'] = array(
01247                 'text' => $this->msg( 'pageinfo-toolboxlink' )->text(),
01248                 'href' => $this->getTitle()->getLocalURL( "action=info" )
01249             );
01250 
01251             if ( $this->getTitle()->getArticleID() ) {
01252                 $nav_urls['recentchangeslinked'] = array(
01253                     'href' => SpecialPage::getTitleFor( 'Recentchangeslinked', $this->thispage )->getLocalURL()
01254                 );
01255             }
01256         }
01257 
01258         $user = $this->getRelevantUser();
01259         if ( $user ) {
01260             $rootUser = $user->getName();
01261 
01262             $nav_urls['contributions'] = array(
01263                 'text' => $this->msg( 'contributions', $rootUser )->text(),
01264                 'href' => self::makeSpecialUrlSubpage( 'Contributions', $rootUser )
01265             );
01266 
01267             $nav_urls['log'] = array(
01268                 'href' => self::makeSpecialUrlSubpage( 'Log', $rootUser )
01269             );
01270 
01271             if ( $this->getUser()->isAllowed( 'block' ) ) {
01272                 $nav_urls['blockip'] = array(
01273                     'href' => self::makeSpecialUrlSubpage( 'Block', $rootUser )
01274                 );
01275             }
01276 
01277             if ( $this->showEmailUser( $user ) ) {
01278                 $nav_urls['emailuser'] = array(
01279                     'href' => self::makeSpecialUrlSubpage( 'Emailuser', $rootUser )
01280                 );
01281             }
01282 
01283             if ( !$user->isAnon() ) {
01284                 $sur = new UserrightsPage;
01285                 $sur->setContext( $this->getContext() );
01286                 if ( $sur->userCanExecute( $this->getUser() ) ) {
01287                     $nav_urls['userrights'] = array(
01288                         'href' => self::makeSpecialUrlSubpage( 'Userrights', $rootUser )
01289                     );
01290                 }
01291             }
01292         }
01293 
01294         wfProfileOut( __METHOD__ );
01295         return $nav_urls;
01296     }
01297 
01303     function getNameSpaceKey() {
01304         return $this->getTitle()->getNamespaceKey();
01305     }
01306 }
01307 
01313 abstract class QuickTemplate {
01317     function __construct() {
01318         $this->data = array();
01319         $this->translator = new MediaWiki_I18N();
01320     }
01321 
01327     public function set( $name, $value ) {
01328         $this->data[$name] = $value;
01329     }
01330 
01338     public function get( $name, $default = null ) {
01339         if ( isset( $this->data[$name] ) ) {
01340             return $this->data[$name];
01341         } else {
01342             return $default;
01343         }
01344     }
01345 
01350     public function setRef( $name, &$value ) {
01351         $this->data[$name] =& $value;
01352     }
01353 
01357     public function setTranslator( &$t ) {
01358         $this->translator = &$t;
01359     }
01360 
01365     abstract public function execute();
01366 
01370     function text( $str ) {
01371         echo htmlspecialchars( $this->data[$str] );
01372     }
01373 
01378     function jstext( $str ) {
01379         wfDeprecated( __METHOD__, '1.21' );
01380         echo Xml::escapeJsString( $this->data[$str] );
01381     }
01382 
01386     function html( $str ) {
01387         echo $this->data[$str];
01388     }
01389 
01393     function msg( $str ) {
01394         echo htmlspecialchars( $this->translator->translate( $str ) );
01395     }
01396 
01400     function msgHtml( $str ) {
01401         echo $this->translator->translate( $str );
01402     }
01403 
01408     function msgWiki( $str ) {
01409         global $wgOut;
01410 
01411         $text = $this->translator->translate( $str );
01412         echo $wgOut->parse( $text );
01413     }
01414 
01419     function haveData( $str ) {
01420         return isset( $this->data[$str] );
01421     }
01422 
01428     function haveMsg( $str ) {
01429         $msg = $this->translator->translate( $str );
01430         return ( $msg != '-' ) && ( $msg != '' ); # ????
01431     }
01432 
01438     public function getSkin() {
01439         return $this->data['skin'];
01440     }
01441 }
01442 
01448 abstract class BaseTemplate extends QuickTemplate {
01449 
01456     public function getMsg( $name ) {
01457         return $this->getSkin()->msg( $name );
01458     }
01459 
01460     function msg( $str ) {
01461         echo $this->getMsg( $str )->escaped();
01462     }
01463 
01464     function msgHtml( $str ) {
01465         echo $this->getMsg( $str )->text();
01466     }
01467 
01468     function msgWiki( $str ) {
01469         echo $this->getMsg( $str )->parseAsBlock();
01470     }
01471 
01479     function getToolbox() {
01480         wfProfileIn( __METHOD__ );
01481 
01482         $toolbox = array();
01483         if ( isset( $this->data['nav_urls']['whatlinkshere'] ) && $this->data['nav_urls']['whatlinkshere'] ) {
01484             $toolbox['whatlinkshere'] = $this->data['nav_urls']['whatlinkshere'];
01485             $toolbox['whatlinkshere']['id'] = 't-whatlinkshere';
01486         }
01487         if ( isset( $this->data['nav_urls']['recentchangeslinked'] ) && $this->data['nav_urls']['recentchangeslinked'] ) {
01488             $toolbox['recentchangeslinked'] = $this->data['nav_urls']['recentchangeslinked'];
01489             $toolbox['recentchangeslinked']['msg'] = 'recentchangeslinked-toolbox';
01490             $toolbox['recentchangeslinked']['id'] = 't-recentchangeslinked';
01491         }
01492         if ( isset( $this->data['feeds'] ) && $this->data['feeds'] ) {
01493             $toolbox['feeds']['id'] = 'feedlinks';
01494             $toolbox['feeds']['links'] = array();
01495             foreach ( $this->data['feeds'] as $key => $feed ) {
01496                 $toolbox['feeds']['links'][$key] = $feed;
01497                 $toolbox['feeds']['links'][$key]['id'] = "feed-$key";
01498                 $toolbox['feeds']['links'][$key]['rel'] = 'alternate';
01499                 $toolbox['feeds']['links'][$key]['type'] = "application/{$key}+xml";
01500                 $toolbox['feeds']['links'][$key]['class'] = 'feedlink';
01501             }
01502         }
01503         foreach ( array( 'contributions', 'log', 'blockip', 'emailuser', 'userrights', 'upload', 'specialpages' ) as $special ) {
01504             if ( isset( $this->data['nav_urls'][$special] ) && $this->data['nav_urls'][$special] ) {
01505                 $toolbox[$special] = $this->data['nav_urls'][$special];
01506                 $toolbox[$special]['id'] = "t-$special";
01507             }
01508         }
01509         if ( isset( $this->data['nav_urls']['print'] ) && $this->data['nav_urls']['print'] ) {
01510             $toolbox['print'] = $this->data['nav_urls']['print'];
01511             $toolbox['print']['id'] = 't-print';
01512             $toolbox['print']['rel'] = 'alternate';
01513             $toolbox['print']['msg'] = 'printableversion';
01514         }
01515         if ( isset( $this->data['nav_urls']['permalink'] ) && $this->data['nav_urls']['permalink'] ) {
01516             $toolbox['permalink'] = $this->data['nav_urls']['permalink'];
01517             if ( $toolbox['permalink']['href'] === '' ) {
01518                 unset( $toolbox['permalink']['href'] );
01519                 $toolbox['ispermalink']['tooltiponly'] = true;
01520                 $toolbox['ispermalink']['id'] = 't-ispermalink';
01521                 $toolbox['ispermalink']['msg'] = 'permalink';
01522             } else {
01523                 $toolbox['permalink']['id'] = 't-permalink';
01524             }
01525         }
01526         if ( isset( $this->data['nav_urls']['info'] ) && $this->data['nav_urls']['info'] ) {
01527             $toolbox['info'] = $this->data['nav_urls']['info'];
01528             $toolbox['info']['id'] = 't-info';
01529         }
01530 
01531         wfRunHooks( 'BaseTemplateToolbox', array( &$this, &$toolbox ) );
01532         wfProfileOut( __METHOD__ );
01533         return $toolbox;
01534     }
01535 
01546     function getPersonalTools() {
01547         $personal_tools = array();
01548         foreach ( $this->get( 'personal_urls' ) as $key => $plink ) {
01549             # The class on a personal_urls item is meant to go on the <a> instead
01550             # of the <li> so we have to use a single item "links" array instead
01551             # of using most of the personal_url's keys directly.
01552             $ptool = array(
01553                 'links' => array(
01554                     array( 'single-id' => "pt-$key" ),
01555                 ),
01556                 'id' => "pt-$key",
01557             );
01558             if ( isset( $plink['active'] ) ) {
01559                 $ptool['active'] = $plink['active'];
01560             }
01561             foreach ( array( 'href', 'class', 'text' ) as $k ) {
01562                 if ( isset( $plink[$k] ) ) {
01563                     $ptool['links'][0][$k] = $plink[$k];
01564                 }
01565             }
01566             $personal_tools[$key] = $ptool;
01567         }
01568         return $personal_tools;
01569     }
01570 
01571     function getSidebar( $options = array() ) {
01572         // Force the rendering of the following portals
01573         $sidebar = $this->data['sidebar'];
01574         if ( !isset( $sidebar['SEARCH'] ) ) {
01575             $sidebar['SEARCH'] = true;
01576         }
01577         if ( !isset( $sidebar['TOOLBOX'] ) ) {
01578             $sidebar['TOOLBOX'] = true;
01579         }
01580         if ( !isset( $sidebar['LANGUAGES'] ) ) {
01581             $sidebar['LANGUAGES'] = true;
01582         }
01583 
01584         if ( !isset( $options['search'] ) || $options['search'] !== true ) {
01585             unset( $sidebar['SEARCH'] );
01586         }
01587         if ( isset( $options['toolbox'] ) && $options['toolbox'] === false ) {
01588             unset( $sidebar['TOOLBOX'] );
01589         }
01590         if ( isset( $options['languages'] ) && $options['languages'] === false ) {
01591             unset( $sidebar['LANGUAGES'] );
01592         }
01593 
01594         $boxes = array();
01595         foreach ( $sidebar as $boxName => $content ) {
01596             if ( $content === false ) {
01597                 continue;
01598             }
01599             switch ( $boxName ) {
01600             case 'SEARCH':
01601                 // Search is a special case, skins should custom implement this
01602                 $boxes[$boxName] = array(
01603                     'id' => 'p-search',
01604                     'header' => $this->getMsg( 'search' )->text(),
01605                     'generated' => false,
01606                     'content' => true,
01607                 );
01608                 break;
01609             case 'TOOLBOX':
01610                 $msgObj = $this->getMsg( 'toolbox' );
01611                 $boxes[$boxName] = array(
01612                     'id' => 'p-tb',
01613                     'header' => $msgObj->exists() ? $msgObj->text() : 'toolbox',
01614                     'generated' => false,
01615                     'content' => $this->getToolbox(),
01616                 );
01617                 break;
01618             case 'LANGUAGES':
01619                 if ( $this->data['language_urls'] ) {
01620                     $msgObj = $this->getMsg( 'otherlanguages' );
01621                     $boxes[$boxName] = array(
01622                         'id' => 'p-lang',
01623                         'header' => $msgObj->exists() ? $msgObj->text() : 'otherlanguages',
01624                         'generated' => false,
01625                         'content' => $this->data['language_urls'],
01626                     );
01627                 }
01628                 break;
01629             default:
01630                 $msgObj = $this->getMsg( $boxName );
01631                 $boxes[$boxName] = array(
01632                     'id' => "p-$boxName",
01633                     'header' => $msgObj->exists() ? $msgObj->text() : $boxName,
01634                     'generated' => true,
01635                     'content' => $content,
01636                 );
01637                 break;
01638             }
01639         }
01640 
01641         // HACK: Compatibility with extensions still using SkinTemplateToolboxEnd
01642         $hookContents = null;
01643         if ( isset( $boxes['TOOLBOX'] ) ) {
01644             ob_start();
01645             // We pass an extra 'true' at the end so extensions using BaseTemplateToolbox
01646             // can abort and avoid outputting double toolbox links
01647             wfRunHooks( 'SkinTemplateToolboxEnd', array( &$this, true ) );
01648             $hookContents = ob_get_contents();
01649             ob_end_clean();
01650             if ( !trim( $hookContents ) ) {
01651                 $hookContents = null;
01652             }
01653         }
01654         // END hack
01655 
01656         if ( isset( $options['htmlOnly'] ) && $options['htmlOnly'] === true ) {
01657             foreach ( $boxes as $boxName => $box ) {
01658                 if ( is_array( $box['content'] ) ) {
01659                     $content = '<ul>';
01660                     foreach ( $box['content'] as $key => $val ) {
01661                         $content .= "\n " . $this->makeListItem( $key, $val );
01662                     }
01663                     // HACK, shove the toolbox end onto the toolbox if we're rendering itself
01664                     if ( $hookContents ) {
01665                         $content .= "\n $hookContents";
01666                     }
01667                     // END hack
01668                     $content .= "\n</ul>\n";
01669                     $boxes[$boxName]['content'] = $content;
01670                 }
01671             }
01672         } else {
01673             if ( $hookContents ) {
01674                 $boxes['TOOLBOXEND'] = array(
01675                     'id' => 'p-toolboxend',
01676                     'header' => $boxes['TOOLBOX']['header'],
01677                     'generated' => false,
01678                     'content' => "<ul>{$hookContents}</ul>",
01679                 );
01680                 // HACK: Make sure that TOOLBOXEND is sorted next to TOOLBOX
01681                 $boxes2 = array();
01682                 foreach ( $boxes as $key => $box ) {
01683                     if ( $key === 'TOOLBOXEND' ) {
01684                         continue;
01685                     }
01686                     $boxes2[$key] = $box;
01687                     if ( $key === 'TOOLBOX' ) {
01688                         $boxes2['TOOLBOXEND'] = $boxes['TOOLBOXEND'];
01689                     }
01690                 }
01691                 $boxes = $boxes2;
01692                 // END hack
01693             }
01694         }
01695 
01696         return $boxes;
01697     }
01698 
01742     function makeLink( $key, $item, $options = array() ) {
01743         if ( isset( $item['text'] ) ) {
01744             $text = $item['text'];
01745         } else {
01746             $text = $this->translator->translate( isset( $item['msg'] ) ? $item['msg'] : $key );
01747         }
01748 
01749         $html = htmlspecialchars( $text );
01750 
01751         if ( isset( $options['text-wrapper'] ) ) {
01752             $wrapper = $options['text-wrapper'];
01753             if ( isset( $wrapper['tag'] ) ) {
01754                 $wrapper = array( $wrapper );
01755             }
01756             while ( count( $wrapper ) > 0 ) {
01757                 $element = array_pop( $wrapper );
01758                 $html = Html::rawElement( $element['tag'], isset( $element['attributes'] ) ? $element['attributes'] : null, $html );
01759             }
01760         }
01761 
01762         if ( isset( $item['href'] ) || isset( $options['link-fallback'] ) ) {
01763             $attrs = $item;
01764             foreach ( array( 'single-id', 'text', 'msg', 'tooltiponly', 'context', 'primary' ) as $k ) {
01765                 unset( $attrs[$k] );
01766             }
01767 
01768             if ( isset( $item['id'] ) && !isset( $item['single-id'] ) ) {
01769                 $item['single-id'] = $item['id'];
01770             }
01771             if ( isset( $item['single-id'] ) ) {
01772                 if ( isset( $item['tooltiponly'] ) && $item['tooltiponly'] ) {
01773                     $title = Linker::titleAttrib( $item['single-id'] );
01774                     if ( $title !== false ) {
01775                         $attrs['title'] = $title;
01776                     }
01777                 } else {
01778                     $tip = Linker::tooltipAndAccesskeyAttribs( $item['single-id'] );
01779                     if ( isset( $tip['title'] ) && $tip['title'] !== false ) {
01780                         $attrs['title'] = $tip['title'];
01781                     }
01782                     if ( isset( $tip['accesskey'] ) && $tip['accesskey'] !== false ) {
01783                         $attrs['accesskey'] = $tip['accesskey'];
01784                     }
01785                 }
01786             }
01787             if ( isset( $options['link-class'] ) ) {
01788                 if ( isset( $attrs['class'] ) ) {
01789                     $attrs['class'] .= " {$options['link-class']}";
01790                 } else {
01791                     $attrs['class'] = $options['link-class'];
01792                 }
01793             }
01794             $html = Html::rawElement( isset( $attrs['href'] ) ? 'a' : $options['link-fallback'], $attrs, $html );
01795         }
01796 
01797         return $html;
01798     }
01799 
01827     function makeListItem( $key, $item, $options = array() ) {
01828         if ( isset( $item['links'] ) ) {
01829             $links = array();
01830             foreach ( $item['links'] as $linkKey => $link ) {
01831                 $links[] = $this->makeLink( $linkKey, $link, $options );
01832             }
01833             $html = implode( ' ', $links );
01834         } else {
01835             $link = $item;
01836             // These keys are used by makeListItem and shouldn't be passed on to the link
01837             foreach ( array( 'id', 'class', 'active', 'tag' ) as $k ) {
01838                 unset( $link[$k] );
01839             }
01840             if ( isset( $item['id'] ) && !isset( $item['single-id'] ) ) {
01841                 // The id goes on the <li> not on the <a> for single links
01842                 // but makeSidebarLink still needs to know what id to use when
01843                 // generating tooltips and accesskeys.
01844                 $link['single-id'] = $item['id'];
01845             }
01846             $html = $this->makeLink( $key, $link, $options );
01847         }
01848 
01849         $attrs = array();
01850         foreach ( array( 'id', 'class' ) as $attr ) {
01851             if ( isset( $item[$attr] ) ) {
01852                 $attrs[$attr] = $item[$attr];
01853             }
01854         }
01855         if ( isset( $item['active'] ) && $item['active'] ) {
01856             if ( !isset( $attrs['class'] ) ) {
01857                 $attrs['class'] = '';
01858             }
01859             $attrs['class'] .= ' active';
01860             $attrs['class'] = trim( $attrs['class'] );
01861         }
01862         return Html::rawElement( isset( $options['tag'] ) ? $options['tag'] : 'li', $attrs, $html );
01863     }
01864 
01865     function makeSearchInput( $attrs = array() ) {
01866         $realAttrs = array(
01867             'type' => 'search',
01868             'name' => 'search',
01869             'placeholder' => wfMessage( 'searchsuggest-search' )->text(),
01870             'value' => $this->get( 'search', '' ),
01871         );
01872         $realAttrs = array_merge( $realAttrs, Linker::tooltipAndAccesskeyAttribs( 'search' ), $attrs );
01873         return Html::element( 'input', $realAttrs );
01874     }
01875 
01876     function makeSearchButton( $mode, $attrs = array() ) {
01877         switch ( $mode ) {
01878             case 'go':
01879             case 'fulltext':
01880                 $realAttrs = array(
01881                     'type' => 'submit',
01882                     'name' => $mode,
01883                     'value' => $this->translator->translate(
01884                         $mode == 'go' ? 'searcharticle' : 'searchbutton' ),
01885                 );
01886                 $realAttrs = array_merge(
01887                     $realAttrs,
01888                     Linker::tooltipAndAccesskeyAttribs( "search-$mode" ),
01889                     $attrs
01890                 );
01891                 return Html::element( 'input', $realAttrs );
01892             case 'image':
01893                 $buttonAttrs = array(
01894                     'type' => 'submit',
01895                     'name' => 'button',
01896                 );
01897                 $buttonAttrs = array_merge(
01898                     $buttonAttrs,
01899                     Linker::tooltipAndAccesskeyAttribs( 'search-fulltext' ),
01900                     $attrs
01901                 );
01902                 unset( $buttonAttrs['src'] );
01903                 unset( $buttonAttrs['alt'] );
01904                 unset( $buttonAttrs['width'] );
01905                 unset( $buttonAttrs['height'] );
01906                 $imgAttrs = array(
01907                     'src' => $attrs['src'],
01908                     'alt' => isset( $attrs['alt'] )
01909                         ? $attrs['alt']
01910                         : $this->translator->translate( 'searchbutton' ),
01911                     'width' => isset( $attrs['width'] ) ? $attrs['width'] : null,
01912                     'height' => isset( $attrs['height'] ) ? $attrs['height'] : null,
01913                 );
01914                 return Html::rawElement( 'button', $buttonAttrs, Html::element( 'img', $imgAttrs ) );
01915             default:
01916                 throw new MWException( 'Unknown mode passed to BaseTemplate::makeSearchButton' );
01917         }
01918     }
01919 
01928     function getFooterLinks( $option = null ) {
01929         $footerlinks = $this->get( 'footerlinks' );
01930 
01931         // Reduce footer links down to only those which are being used
01932         $validFooterLinks = array();
01933         foreach ( $footerlinks as $category => $links ) {
01934             $validFooterLinks[$category] = array();
01935             foreach ( $links as $link ) {
01936                 if ( isset( $this->data[$link] ) && $this->data[$link] ) {
01937                     $validFooterLinks[$category][] = $link;
01938                 }
01939             }
01940             if ( count( $validFooterLinks[$category] ) <= 0 ) {
01941                 unset( $validFooterLinks[$category] );
01942             }
01943         }
01944 
01945         if ( $option == 'flat' ) {
01946             // fold footerlinks into a single array using a bit of trickery
01947             $validFooterLinks = call_user_func_array(
01948                 'array_merge',
01949                 array_values( $validFooterLinks )
01950             );
01951         }
01952 
01953         return $validFooterLinks;
01954     }
01955 
01967     function getFooterIcons( $option = null ) {
01968         // Generate additional footer icons
01969         $footericons = $this->get( 'footericons' );
01970 
01971         if ( $option == 'icononly' ) {
01972             // Unset any icons which don't have an image
01973             foreach ( $footericons as &$footerIconsBlock ) {
01974                 foreach ( $footerIconsBlock as $footerIconKey => $footerIcon ) {
01975                     if ( !is_string( $footerIcon ) && !isset( $footerIcon['src'] ) ) {
01976                         unset( $footerIconsBlock[$footerIconKey] );
01977                     }
01978                 }
01979             }
01980             // Redo removal of any empty blocks
01981             foreach ( $footericons as $footerIconsKey => &$footerIconsBlock ) {
01982                 if ( count( $footerIconsBlock ) <= 0 ) {
01983                     unset( $footericons[$footerIconsKey] );
01984                 }
01985             }
01986         } elseif ( $option == 'nocopyright' ) {
01987             unset( $footericons['copyright']['copyright'] );
01988             if ( count( $footericons['copyright'] ) <= 0 ) {
01989                 unset( $footericons['copyright'] );
01990             }
01991         }
01992 
01993         return $footericons;
01994     }
01995 
02001     function printTrail() { ?>
02002 <?php echo MWDebug::getDebugHTML( $this->getSkin()->getContext() ); ?>
02003 <?php $this->html( 'bottomscripts' ); /* JS call to runBodyOnloadHook */ ?>
02004 <?php $this->html( 'reporttime' ) ?>
02005 <?php
02006     }
02007 
02008 }