MediaWiki  REL1_20
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 
00130         function outputPage( OutputPage $out=null ) {
00131                 global $wgContLang;
00132                 global $wgScript, $wgStylePath;
00133                 global $wgMimeType, $wgJsMimeType;
00134                 global $wgXhtmlDefaultNamespace, $wgXhtmlNamespaces, $wgHtml5Version;
00135                 global $wgDisableCounters, $wgSitename, $wgLogo, $wgHideInterlanguageLinks;
00136                 global $wgMaxCredits, $wgShowCreditsIfMax;
00137                 global $wgPageShowWatchingUsers;
00138                 global $wgArticlePath, $wgScriptPath, $wgServer;
00139 
00140                 wfProfileIn( __METHOD__ );
00141                 Profiler::instance()->setTemplated( true );
00142 
00143                 $oldContext = null;
00144                 if ( $out !== null ) {
00145                         // @todo Add wfDeprecated in 1.20
00146                         $oldContext = $this->getContext();
00147                         $this->setContext( $out->getContext() );
00148                 }
00149 
00150                 $out = $this->getOutput();
00151                 $request = $this->getRequest();
00152                 $user = $this->getUser();
00153                 $title = $this->getTitle();
00154 
00155                 wfProfileIn( __METHOD__ . '-init' );
00156                 $this->initPage( $out );
00157 
00158                 $tpl = $this->setupTemplate( $this->template, 'skins' );
00159                 wfProfileOut( __METHOD__ . '-init' );
00160 
00161                 wfProfileIn( __METHOD__ . '-stuff' );
00162                 $this->thispage = $title->getPrefixedDBkey();
00163                 $this->titletxt = $title->getPrefixedText();
00164                 $this->userpage = $user->getUserPage()->getPrefixedText();
00165                 $query = array();
00166                 if ( !$request->wasPosted() ) {
00167                         $query = $request->getValues();
00168                         unset( $query['title'] );
00169                         unset( $query['returnto'] );
00170                         unset( $query['returntoquery'] );
00171                 }
00172                 $this->thisquery = wfArrayToCGI( $query );
00173                 $this->loggedin = $user->isLoggedIn();
00174                 $this->username = $user->getName();
00175 
00176                 if ( $this->loggedin || $this->showIPinHeader() ) {
00177                         $this->userpageUrlDetails = self::makeUrlDetails( $this->userpage );
00178                 } else {
00179                         # This won't be used in the standard skins, but we define it to preserve the interface
00180                         # To save time, we check for existence
00181                         $this->userpageUrlDetails = self::makeKnownUrlDetails( $this->userpage );
00182                 }
00183 
00184                 wfProfileOut( __METHOD__ . '-stuff' );
00185 
00186                 wfProfileIn( __METHOD__ . '-stuff-head' );
00187                 if ( !$this->useHeadElement ) {
00188                         $tpl->set( 'pagecss', false );
00189                         $tpl->set( 'usercss', false );
00190 
00191                         $tpl->set( 'userjs', false );
00192                         $tpl->set( 'userjsprev', false );
00193 
00194                         $tpl->set( 'jsvarurl', false );
00195 
00196                         $tpl->setRef( 'xhtmldefaultnamespace', $wgXhtmlDefaultNamespace );
00197                         $tpl->set( 'xhtmlnamespaces', $wgXhtmlNamespaces );
00198                         $tpl->set( 'html5version', $wgHtml5Version );
00199                         $tpl->set( 'headlinks', $out->getHeadLinks() );
00200                         $tpl->set( 'csslinks', $out->buildCssLinks() );
00201                         $tpl->set( 'pageclass', $this->getPageClasses( $title ) );
00202                         $tpl->set( 'skinnameclass', ( 'skin-' . Sanitizer::escapeClass( $this->getSkinName() ) ) );
00203                 }
00204                 wfProfileOut( __METHOD__ . '-stuff-head' );
00205 
00206                 wfProfileIn( __METHOD__ . '-stuff2' );
00207                 $tpl->set( 'title', $out->getPageTitle() );
00208                 $tpl->set( 'pagetitle', $out->getHTMLTitle() );
00209                 $tpl->set( 'displaytitle', $out->mPageLinkTitle );
00210 
00211                 $tpl->setRef( 'thispage', $this->thispage );
00212                 $tpl->setRef( 'titleprefixeddbkey', $this->thispage );
00213                 $tpl->set( 'titletext', $title->getText() );
00214                 $tpl->set( 'articleid', $title->getArticleID() );
00215 
00216                 $tpl->set( 'isarticle', $out->isArticle() );
00217 
00218                 $subpagestr = $this->subPageSubtitle();
00219                 if ( $subpagestr !== '' ) {
00220                         $subpagestr = '<span class="subpages">' . $subpagestr . '</span>';
00221                 }
00222                 $tpl->set( 'subtitle',  $subpagestr . $out->getSubtitle() );
00223 
00224                 $undelete = $this->getUndeleteLink();
00225                 if ( $undelete === '' ) {
00226                         $tpl->set( 'undelete', '' );
00227                 } else {
00228                         $tpl->set( 'undelete', '<span class="subpages">' . $undelete . '</span>' );
00229                 }
00230 
00231                 $tpl->set( 'catlinks', $this->getCategories() );
00232                 if( $out->isSyndicated() ) {
00233                         $feeds = array();
00234                         foreach( $out->getSyndicationLinks() as $format => $link ) {
00235                                 $feeds[$format] = array(
00236                                         'text' => $this->msg( "feed-$format" )->text(),
00237                                         'href' => $link
00238                                 );
00239                         }
00240                         $tpl->setRef( 'feeds', $feeds );
00241                 } else {
00242                         $tpl->set( 'feeds', false );
00243                 }
00244 
00245                 $tpl->setRef( 'mimetype', $wgMimeType );
00246                 $tpl->setRef( 'jsmimetype', $wgJsMimeType );
00247                 $tpl->set( 'charset', 'UTF-8' );
00248                 $tpl->setRef( 'wgScript', $wgScript );
00249                 $tpl->setRef( 'skinname', $this->skinname );
00250                 $tpl->set( 'skinclass', get_class( $this ) );
00251                 $tpl->setRef( 'skin', $this );
00252                 $tpl->setRef( 'stylename', $this->stylename );
00253                 $tpl->set( 'printable', $out->isPrintable() );
00254                 $tpl->set( 'handheld', $request->getBool( 'handheld' ) );
00255                 $tpl->setRef( 'loggedin', $this->loggedin );
00256                 $tpl->set( 'notspecialpage', !$title->isSpecialPage() );
00257                 /* XXX currently unused, might get useful later
00258                 $tpl->set( 'editable', ( !$title->isSpecialPage() ) );
00259                 $tpl->set( 'exists', $title->getArticleID() != 0 );
00260                 $tpl->set( 'watch', $user->isWatched( $title ) ? 'unwatch' : 'watch' );
00261                 $tpl->set( 'protect', count( $title->isProtected() ) ? 'unprotect' : 'protect' );
00262                 $tpl->set( 'helppage', $this->msg( 'helppage' )->text() );
00263                 */
00264                 $tpl->set( 'searchaction', $this->escapeSearchLink() );
00265                 $tpl->set( 'searchtitle', SpecialPage::getTitleFor( 'Search' )->getPrefixedDBKey() );
00266                 $tpl->set( 'search', trim( $request->getVal( 'search' ) ) );
00267                 $tpl->setRef( 'stylepath', $wgStylePath );
00268                 $tpl->setRef( 'articlepath', $wgArticlePath );
00269                 $tpl->setRef( 'scriptpath', $wgScriptPath );
00270                 $tpl->setRef( 'serverurl', $wgServer );
00271                 $tpl->setRef( 'logopath', $wgLogo );
00272                 $tpl->setRef( 'sitename', $wgSitename );
00273 
00274                 $userLang = $this->getLanguage();
00275                 $userLangCode = $userLang->getHtmlCode();
00276                 $userLangDir  = $userLang->getDir();
00277 
00278                 $tpl->set( 'lang', $userLangCode );
00279                 $tpl->set( 'dir', $userLangDir );
00280                 $tpl->set( 'rtl', $userLang->isRTL() );
00281 
00282                 $tpl->set( 'capitalizeallnouns', $userLang->capitalizeAllNouns() ? ' capitalize-all-nouns' : '' );
00283                 $tpl->set( 'showjumplinks', $user->getOption( 'showjumplinks' ) );
00284                 $tpl->set( 'username', $this->loggedin ? $this->username : null );
00285                 $tpl->setRef( 'userpage', $this->userpage );
00286                 $tpl->setRef( 'userpageurl', $this->userpageUrlDetails['href'] );
00287                 $tpl->set( 'userlang', $userLangCode );
00288 
00289                 // Users can have their language set differently than the
00290                 // content of the wiki. For these users, tell the web browser
00291                 // that interface elements are in a different language.
00292                 $tpl->set( 'userlangattributes', '' );
00293                 $tpl->set( 'specialpageattributes', '' ); # obsolete
00294 
00295                 if ( $userLangCode !== $wgContLang->getHtmlCode() || $userLangDir !== $wgContLang->getDir() ) {
00296                         $escUserlang = htmlspecialchars( $userLangCode );
00297                         $escUserdir = htmlspecialchars( $userLangDir );
00298                         // Attributes must be in double quotes because htmlspecialchars() doesn't
00299                         // escape single quotes
00300                         $attrs = " lang=\"$escUserlang\" dir=\"$escUserdir\"";
00301                         $tpl->set( 'userlangattributes', $attrs );
00302                 }
00303 
00304                 wfProfileOut( __METHOD__ . '-stuff2' );
00305 
00306                 wfProfileIn( __METHOD__ . '-stuff3' );
00307                 $tpl->set( 'newtalk', $this->getNewtalks() );
00308                 $tpl->set( 'logo', $this->logoText() );
00309 
00310                 $tpl->set( 'copyright', false );
00311                 $tpl->set( 'viewcount', false );
00312                 $tpl->set( 'lastmod', false );
00313                 $tpl->set( 'credits', false );
00314                 $tpl->set( 'numberofwatchingusers', false );
00315                 if ( $out->isArticle() && $title->exists() ) {
00316                         if ( $this->isRevisionCurrent() ) {
00317                                 if ( !$wgDisableCounters ) {
00318                                         $viewcount = $this->getWikiPage()->getCount();
00319                                         if ( $viewcount ) {
00320                                                 $tpl->set( 'viewcount', $this->msg( 'viewcount' )->numParams( $viewcount )->parse() );
00321                                         }
00322                                 }
00323 
00324                                 if ( $wgPageShowWatchingUsers ) {
00325                                         $dbr = wfGetDB( DB_SLAVE );
00326                                         $num = $dbr->selectField( 'watchlist', 'COUNT(*)',
00327                                                 array( 'wl_title' => $title->getDBkey(), 'wl_namespace' => $title->getNamespace() ),
00328                                                 __METHOD__
00329                                         );
00330                                         if ( $num > 0 ) {
00331                                                 $tpl->set( 'numberofwatchingusers',
00332                                                         $this->msg( 'number_of_watching_users_pageview' )->numParams( $num )->parse()
00333                                                 );
00334                                         }
00335                                 }
00336 
00337                                 if ( $wgMaxCredits != 0 ) {
00338                                         $tpl->set( 'credits', Action::factory( 'credits', $this->getWikiPage(),
00339                                                 $this->getContext() )->getCredits( $wgMaxCredits, $wgShowCreditsIfMax ) );
00340                                 } else {
00341                                         $tpl->set( 'lastmod', $this->lastModified() );
00342                                 }
00343                         }
00344                         $tpl->set( 'copyright', $this->getCopyright() );
00345                 }
00346                 wfProfileOut( __METHOD__ . '-stuff3' );
00347 
00348                 wfProfileIn( __METHOD__ . '-stuff4' );
00349                 $tpl->set( 'copyrightico', $this->getCopyrightIcon() );
00350                 $tpl->set( 'poweredbyico', $this->getPoweredBy() );
00351                 $tpl->set( 'disclaimer', $this->disclaimerLink() );
00352                 $tpl->set( 'privacy', $this->privacyLink() );
00353                 $tpl->set( 'about', $this->aboutLink() );
00354 
00355                 $tpl->set( 'footerlinks', array(
00356                         'info' => array(
00357                                 'lastmod',
00358                                 'viewcount',
00359                                 'numberofwatchingusers',
00360                                 'credits',
00361                                 'copyright',
00362                         ),
00363                         'places' => array(
00364                                 'privacy',
00365                                 'about',
00366                                 'disclaimer',
00367                         ),
00368                 ) );
00369 
00370                 global $wgFooterIcons;
00371                 $tpl->set( 'footericons', $wgFooterIcons );
00372                 foreach ( $tpl->data['footericons'] as $footerIconsKey => &$footerIconsBlock ) {
00373                         if ( count( $footerIconsBlock ) > 0 ) {
00374                                 foreach ( $footerIconsBlock as &$footerIcon ) {
00375                                         if ( isset( $footerIcon['src'] ) ) {
00376                                                 if ( !isset( $footerIcon['width'] ) ) {
00377                                                         $footerIcon['width'] = 88;
00378                                                 }
00379                                                 if ( !isset( $footerIcon['height'] ) ) {
00380                                                         $footerIcon['height'] = 31;
00381                                                 }
00382                                         }
00383                                 }
00384                         } else {
00385                                 unset( $tpl->data['footericons'][$footerIconsKey] );
00386                         }
00387                 }
00388 
00389                 $tpl->set( 'sitenotice', $this->getSiteNotice() );
00390                 $tpl->set( 'bottomscripts', $this->bottomScripts() );
00391                 $tpl->set( 'printfooter', $this->printSource() );
00392 
00393                 # An ID that includes the actual body text; without categories, contentSub, ...
00394                 $realBodyAttribs = array( 'id' => 'mw-content-text' );
00395 
00396                 # Add a mw-content-ltr/rtl class to be able to style based on text direction
00397                 # when the content is different from the UI language, i.e.:
00398                 # not for special pages or file pages AND only when viewing AND if the page exists
00399                 # (or is in MW namespace, because that has default content)
00400                 if ( !in_array( $title->getNamespace(), array( NS_SPECIAL, NS_FILE ) ) &&
00401                         in_array( $request->getVal( 'action', 'view' ), array( 'view', 'historysubmit' ) ) &&
00402                         ( $title->exists() || $title->getNamespace() == NS_MEDIAWIKI ) ) {
00403                         $pageLang = $title->getPageViewLanguage();
00404                         $realBodyAttribs['lang'] = $pageLang->getHtmlCode();
00405                         $realBodyAttribs['dir'] = $pageLang->getDir();
00406                         $realBodyAttribs['class'] = 'mw-content-'.$pageLang->getDir();
00407                 }
00408 
00409                 $out->mBodytext = Html::rawElement( 'div', $realBodyAttribs, $out->mBodytext );
00410                 $tpl->setRef( 'bodytext', $out->mBodytext );
00411 
00412                 # Language links
00413                 $language_urls = array();
00414 
00415                 if ( !$wgHideInterlanguageLinks ) {
00416                         foreach( $out->getLanguageLinks() as $l ) {
00417                                 $tmp = explode( ':', $l, 2 );
00418                                 $class = 'interwiki-' . $tmp[0];
00419                                 unset( $tmp );
00420                                 $nt = Title::newFromText( $l );
00421                                 if ( $nt ) {
00422                                         $ilLangName = Language::fetchLanguageName( $nt->getInterwiki() );
00423                                         if ( strval( $ilLangName ) === '' ) {
00424                                                 $ilLangName = $l;
00425                                         } else {
00426                                                 $ilLangName = $this->getLanguage()->ucfirst( $ilLangName );
00427                                         }
00428                                         $language_urls[] = array(
00429                                                 'href' => $nt->getFullURL(),
00430                                                 'text' => $ilLangName,
00431                                                 'title' => $nt->getText(),
00432                                                 'class' => $class,
00433                                                 'lang' => $nt->getInterwiki(),
00434                                                 'hreflang' => $nt->getInterwiki(),
00435                                         );
00436                                 }
00437                         }
00438                 }
00439                 if ( count( $language_urls ) ) {
00440                         $tpl->setRef( 'language_urls', $language_urls );
00441                 } else {
00442                         $tpl->set( 'language_urls', false );
00443                 }
00444                 wfProfileOut( __METHOD__ . '-stuff4' );
00445 
00446                 wfProfileIn( __METHOD__ . '-stuff5' );
00447                 # Personal toolbar
00448                 $tpl->set( 'personal_urls', $this->buildPersonalUrls() );
00449                 $content_navigation = $this->buildContentNavigationUrls();
00450                 $content_actions = $this->buildContentActionUrls( $content_navigation );
00451                 $tpl->setRef( 'content_navigation', $content_navigation );
00452                 $tpl->setRef( 'content_actions', $content_actions );
00453 
00454                 $tpl->set( 'sidebar', $this->buildSidebar() );
00455                 $tpl->set( 'nav_urls', $this->buildNavUrls() );
00456 
00457                 // Set the head scripts near the end, in case the above actions resulted in added scripts
00458                 if ( $this->useHeadElement ) {
00459                         $tpl->set( 'headelement', $out->headElement( $this ) );
00460                 } else {
00461                         $tpl->set( 'headscripts', $out->getHeadScripts() . $out->getHeadItems() );
00462                 }
00463 
00464                 $tpl->set( 'debug', '' );
00465                 $tpl->set( 'debughtml', $this->generateDebugHTML() );
00466                 $tpl->set( 'reporttime', wfReportTime() );
00467 
00468                 // original version by hansm
00469                 if( !wfRunHooks( 'SkinTemplateOutputPageBeforeExec', array( &$this, &$tpl ) ) ) {
00470                         wfDebug( __METHOD__ . ": Hook SkinTemplateOutputPageBeforeExec broke outputPage execution!\n" );
00471                 }
00472 
00473                 // Set the bodytext to another key so that skins can just output it on it's own
00474                 // and output printfooter and debughtml separately
00475                 $tpl->set( 'bodycontent', $tpl->data['bodytext'] );
00476 
00477                 // Append printfooter and debughtml onto bodytext so that skins that were already
00478                 // using bodytext before they were split out don't suddenly start not outputting information
00479                 $tpl->data['bodytext'] .= Html::rawElement( 'div', array( 'class' => 'printfooter' ), "\n{$tpl->data['printfooter']}" ) . "\n";
00480                 $tpl->data['bodytext'] .= $tpl->data['debughtml'];
00481 
00482                 // allow extensions adding stuff after the page content.
00483                 // See Skin::afterContentHook() for further documentation.
00484                 $tpl->set( 'dataAfterContent', $this->afterContentHook() );
00485                 wfProfileOut( __METHOD__ . '-stuff5' );
00486 
00487                 // execute template
00488                 wfProfileIn( __METHOD__ . '-execute' );
00489                 $res = $tpl->execute();
00490                 wfProfileOut( __METHOD__ . '-execute' );
00491 
00492                 // result may be an error
00493                 $this->printOrError( $res );
00494 
00495                 if ( $oldContext ) {
00496                         $this->setContext( $oldContext );
00497                 }
00498                 wfProfileOut( __METHOD__ );
00499         }
00500 
00509         function printOrError( $str ) {
00510                 echo $str;
00511         }
00512 
00522         function useCombinedLoginLink() {
00523                 global $wgUseCombinedLoginLink;
00524                 return $wgUseCombinedLoginLink;
00525         }
00526 
00531         protected function buildPersonalUrls() {
00532                 $title = $this->getTitle();
00533                 $request = $this->getRequest();
00534                 $pageurl = $title->getLocalURL();
00535                 wfProfileIn( __METHOD__ );
00536 
00537                 /* set up the default links for the personal toolbar */
00538                 $personal_urls = array();
00539 
00540                 # Due to bug 32276, if a user does not have read permissions,
00541                 # $this->getTitle() will just give Special:Badtitle, which is
00542                 # not especially useful as a returnto parameter. Use the title
00543                 # from the request instead, if there was one.
00544                 $page = Title::newFromURL( $request->getVal( 'title', '' ) );
00545                 $page = $request->getVal( 'returnto', $page );
00546                 $a = array();
00547                 if ( strval( $page ) !== '' ) {
00548                         $a['returnto'] = $page;
00549                         $query = $request->getVal( 'returntoquery', $this->thisquery );
00550                         if( $query != '' ) {
00551                                 $a['returntoquery'] = $query;
00552                         }
00553                 }
00554                 $returnto = wfArrayToCGI( $a );
00555                 if( $this->loggedin ) {
00556                         $personal_urls['userpage'] = array(
00557                                 'text' => $this->username,
00558                                 'href' => &$this->userpageUrlDetails['href'],
00559                                 'class' => $this->userpageUrlDetails['exists'] ? false : 'new',
00560                                 'active' => ( $this->userpageUrlDetails['href'] == $pageurl ),
00561                                 'dir' => 'auto'
00562                         );
00563                         $usertalkUrlDetails = $this->makeTalkUrlDetails( $this->userpage );
00564                         $personal_urls['mytalk'] = array(
00565                                 'text' => $this->msg( 'mytalk' )->text(),
00566                                 'href' => &$usertalkUrlDetails['href'],
00567                                 'class' => $usertalkUrlDetails['exists'] ? false : 'new',
00568                                 'active' => ( $usertalkUrlDetails['href'] == $pageurl )
00569                         );
00570                         $href = self::makeSpecialUrl( 'Preferences' );
00571                         $personal_urls['preferences'] = array(
00572                                 'text' => $this->msg( 'mypreferences' )->text(),
00573                                 'href' => $href,
00574                                 'active' => ( $href == $pageurl )
00575                         );
00576                         $href = self::makeSpecialUrl( 'Watchlist' );
00577                         $personal_urls['watchlist'] = array(
00578                                 'text' => $this->msg( 'mywatchlist' )->text(),
00579                                 'href' => $href,
00580                                 'active' => ( $href == $pageurl )
00581                         );
00582 
00583                         # We need to do an explicit check for Special:Contributions, as we
00584                         # have to match both the title, and the target, which could come
00585                         # from request values (Special:Contributions?target=Jimbo_Wales)
00586                         # or be specified in "sub page" form
00587                         # (Special:Contributions/Jimbo_Wales). The plot
00588                         # thickens, because the Title object is altered for special pages,
00589                         # so it doesn't contain the original alias-with-subpage.
00590                         $origTitle = Title::newFromText( $request->getText( 'title' ) );
00591                         if( $origTitle instanceof Title && $origTitle->isSpecialPage() ) {
00592                                 list( $spName, $spPar ) = SpecialPageFactory::resolveAlias( $origTitle->getText() );
00593                                 $active = $spName == 'Contributions'
00594                                         && ( ( $spPar && $spPar == $this->username )
00595                                                 || $request->getText( 'target' ) == $this->username );
00596                         } else {
00597                                 $active = false;
00598                         }
00599 
00600                         $href = self::makeSpecialUrlSubpage( 'Contributions', $this->username );
00601                         $personal_urls['mycontris'] = array(
00602                                 'text' => $this->msg( 'mycontris' )->text(),
00603                                 'href' => $href,
00604                                 'active' => $active
00605                         );
00606                         $personal_urls['logout'] = array(
00607                                 'text' => $this->msg( 'userlogout' )->text(),
00608                                 'href' => self::makeSpecialUrl( 'Userlogout',
00609                                         // userlogout link must always contain an & character, otherwise we might not be able
00610                                         // to detect a buggy precaching proxy (bug 17790)
00611                                         $title->isSpecial( 'Preferences' ) ? 'noreturnto' : $returnto
00612                                 ),
00613                                 'active' => false
00614                         );
00615                 } else {
00616                         $useCombinedLoginLink = $this->useCombinedLoginLink();
00617                         $loginlink = $this->getUser()->isAllowed( 'createaccount' ) && $useCombinedLoginLink
00618                                 ? 'nav-login-createaccount'
00619                                 : 'login';
00620                         $is_signup = $request->getText( 'type' ) == 'signup';
00621 
00622                         # anonlogin & login are the same
00623                         global $wgSecureLogin;
00624                         $proto = $wgSecureLogin ? PROTO_HTTPS : null;
00625 
00626                         $login_id = $this->showIPinHeader() ? 'anonlogin' : 'login';
00627                         $login_url = array(
00628                                 'text' => $this->msg( $loginlink )->text(),
00629                                 'href' => self::makeSpecialUrl( 'Userlogin', $returnto, $proto ),
00630                                 'active' => $title->isSpecial( 'Userlogin' ) && ( $loginlink == 'nav-login-createaccount' || !$is_signup ),
00631                                 'class' => $wgSecureLogin ? 'link-https' : ''
00632                         );
00633                         $createaccount_url = array(
00634                                 'text' => $this->msg( 'createaccount' )->text(),
00635                                 'href' => self::makeSpecialUrl( 'Userlogin', "$returnto&type=signup", $proto ),
00636                                 'active' => $title->isSpecial( 'Userlogin' ) && $is_signup,
00637                                 'class' => $wgSecureLogin ? 'link-https' : ''
00638                         );
00639 
00640                         if( $this->showIPinHeader() ) {
00641                                 $href = &$this->userpageUrlDetails['href'];
00642                                 $personal_urls['anonuserpage'] = array(
00643                                         'text' => $this->username,
00644                                         'href' => $href,
00645                                         'class' => $this->userpageUrlDetails['exists'] ? false : 'new',
00646                                         'active' => ( $pageurl == $href )
00647                                 );
00648                                 $usertalkUrlDetails = $this->makeTalkUrlDetails( $this->userpage );
00649                                 $href = &$usertalkUrlDetails['href'];
00650                                 $personal_urls['anontalk'] = array(
00651                                         'text' => $this->msg( 'anontalk' )->text(),
00652                                         'href' => $href,
00653                                         'class' => $usertalkUrlDetails['exists'] ? false : 'new',
00654                                         'active' => ( $pageurl == $href )
00655                                 );
00656                         }
00657 
00658                         if ( $this->getUser()->isAllowed( 'createaccount' ) && !$useCombinedLoginLink ) {
00659                                 $personal_urls['createaccount'] = $createaccount_url;
00660                         }
00661 
00662                         $personal_urls[$login_id] = $login_url;
00663                 }
00664 
00665                 wfRunHooks( 'PersonalUrls', array( &$personal_urls, &$title ) );
00666                 wfProfileOut( __METHOD__ );
00667                 return $personal_urls;
00668         }
00669 
00679         function tabAction( $title, $message, $selected, $query = '', $checkEdit = false ) {
00680                 $classes = array();
00681                 if( $selected ) {
00682                         $classes[] = 'selected';
00683                 }
00684                 if( $checkEdit && !$title->isKnown() ) {
00685                         $classes[] = 'new';
00686                         $query = 'action=edit&redlink=1';
00687                 }
00688 
00689                 // wfMessageFallback will nicely accept $message as an array of fallbacks
00690                 // or just a single key
00691                 $msg = wfMessageFallback( $message )->setContext( $this->getContext() );
00692                 if ( is_array( $message ) ) {
00693                         // for hook compatibility just keep the last message name
00694                         $message = end( $message );
00695                 }
00696                 if ( $msg->exists() ) {
00697                         $text = $msg->text();
00698                 } else {
00699                         global $wgContLang;
00700                         $text = $wgContLang->getFormattedNsText(
00701                                 MWNamespace::getSubject( $title->getNamespace() ) );
00702                 }
00703 
00704                 $result = array();
00705                 if( !wfRunHooks( 'SkinTemplateTabAction', array( &$this,
00706                                 $title, $message, $selected, $checkEdit,
00707                                 &$classes, &$query, &$text, &$result ) ) ) {
00708                         return $result;
00709                 }
00710 
00711                 return array(
00712                         'class' => implode( ' ', $classes ),
00713                         'text' => $text,
00714                         'href' => $title->getLocalUrl( $query ),
00715                         'primary' => true );
00716         }
00717 
00718         function makeTalkUrlDetails( $name, $urlaction = '' ) {
00719                 $title = Title::newFromText( $name );
00720                 if( !is_object( $title ) ) {
00721                         throw new MWException( __METHOD__ . " given invalid pagename $name" );
00722                 }
00723                 $title = $title->getTalkPage();
00724                 self::checkTitle( $title, $name );
00725                 return array(
00726                         'href' => $title->getLocalURL( $urlaction ),
00727                         'exists' => $title->getArticleID() != 0,
00728                 );
00729         }
00730 
00731         function makeArticleUrlDetails( $name, $urlaction = '' ) {
00732                 $title = Title::newFromText( $name );
00733                 $title= $title->getSubjectPage();
00734                 self::checkTitle( $title, $name );
00735                 return array(
00736                         'href' => $title->getLocalURL( $urlaction ),
00737                         'exists' => $title->getArticleID() != 0,
00738                 );
00739         }
00740 
00774         protected function buildContentNavigationUrls() {
00775                 global $wgDisableLangConversion;
00776 
00777                 wfProfileIn( __METHOD__ );
00778 
00779                 // Display tabs for the relevant title rather than always the title itself
00780                 $title = $this->getRelevantTitle();
00781                 $onPage = $title->equals( $this->getTitle() );
00782 
00783                 $out = $this->getOutput();
00784                 $request = $this->getRequest();
00785                 $user = $this->getUser();
00786 
00787                 $content_navigation = array(
00788                         'namespaces' => array(),
00789                         'views' => array(),
00790                         'actions' => array(),
00791                         'variants' => array()
00792                 );
00793 
00794                 // parameters
00795                 $action = $request->getVal( 'action', 'view' );
00796 
00797                 $userCanRead = $title->quickUserCan( 'read', $user );
00798 
00799                 $preventActiveTabs = false;
00800                 wfRunHooks( 'SkinTemplatePreventOtherActiveTabs', array( &$this, &$preventActiveTabs ) );
00801 
00802                 // Checks if page is some kind of content
00803                 if( $title->canExist() ) {
00804                         // Gets page objects for the related namespaces
00805                         $subjectPage = $title->getSubjectPage();
00806                         $talkPage = $title->getTalkPage();
00807 
00808                         // Determines if this is a talk page
00809                         $isTalk = $title->isTalkPage();
00810 
00811                         // Generates XML IDs from namespace names
00812                         $subjectId = $title->getNamespaceKey( '' );
00813 
00814                         if ( $subjectId == 'main' ) {
00815                                 $talkId = 'talk';
00816                         } else {
00817                                 $talkId = "{$subjectId}_talk";
00818                         }
00819 
00820                         $skname = $this->skinname;
00821 
00822                         // Adds namespace links
00823                         $subjectMsg = array( "nstab-$subjectId" );
00824                         if ( $subjectPage->isMainPage() ) {
00825                                 array_unshift( $subjectMsg, 'mainpage-nstab' );
00826                         }
00827                         $content_navigation['namespaces'][$subjectId] = $this->tabAction(
00828                                 $subjectPage, $subjectMsg, !$isTalk && !$preventActiveTabs, '', $userCanRead
00829                         );
00830                         $content_navigation['namespaces'][$subjectId]['context'] = 'subject';
00831                         $content_navigation['namespaces'][$talkId] = $this->tabAction(
00832                                 $talkPage, array( "nstab-$talkId", 'talk' ), $isTalk && !$preventActiveTabs, '', $userCanRead
00833                         );
00834                         $content_navigation['namespaces'][$talkId]['context'] = 'talk';
00835 
00836                         if ( $userCanRead ) {
00837                                 // Adds view view link
00838                                 if ( $title->exists() ) {
00839                                         $content_navigation['views']['view'] = $this->tabAction(
00840                                                 $isTalk ? $talkPage : $subjectPage,
00841                                                 array( "$skname-view-view", 'view' ),
00842                                                 ( $onPage && ( $action == 'view' || $action == 'purge' ) ), '', true
00843                                         );
00844                                         // signal to hide this from simple content_actions
00845                                         $content_navigation['views']['view']['redundant'] = true;
00846                                 }
00847 
00848                                 wfProfileIn( __METHOD__ . '-edit' );
00849 
00850                                 // Checks if user can edit the current page if it exists or create it otherwise
00851                                 if ( $title->quickUserCan( 'edit', $user ) && ( $title->exists() || $title->quickUserCan( 'create', $user ) ) ) {
00852                                         // Builds CSS class for talk page links
00853                                         $isTalkClass = $isTalk ? ' istalk' : '';
00854                                         // Whether the user is editing the page
00855                                         $isEditing = $onPage && ( $action == 'edit' || $action == 'submit' );
00856                                         // Whether to show the "Add a new section" tab
00857                                         // Checks if this is a current rev of talk page and is not forced to be hidden
00858                                         $showNewSection = !$out->forceHideNewSectionLink()
00859                                                 && ( ( $isTalk && $this->isRevisionCurrent() ) || $out->showNewSectionLink() );
00860                                         $section = $request->getVal( 'section' );
00861 
00862                                         $msgKey = $title->exists() || ( $title->getNamespace() == NS_MEDIAWIKI && $title->getDefaultMessageText() !== false ) ?
00863                                                 'edit' : 'create';
00864                                         $content_navigation['views']['edit'] = array(
00865                                                 'class' => ( $isEditing && ( $section !== 'new' || !$showNewSection ) ? 'selected' : '' ) . $isTalkClass,
00866                                                 'text' => wfMessageFallback( "$skname-view-$msgKey", $msgKey )->setContext( $this->getContext() )->text(),
00867                                                 'href' => $title->getLocalURL( $this->editUrlOptions() ),
00868                                                 'primary' => true, // don't collapse this in vector
00869                                         );
00870 
00871                                         // section link
00872                                         if ( $showNewSection ) {
00873                                                 // Adds new section link
00874                                                 //$content_navigation['actions']['addsection']
00875                                                 $content_navigation['views']['addsection'] = array(
00876                                                         'class' => ( $isEditing && $section == 'new' ) ? 'selected' : false,
00877                                                         'text' => wfMessageFallback( "$skname-action-addsection", 'addsection' )->setContext( $this->getContext() )->text(),
00878                                                         'href' => $title->getLocalURL( 'action=edit&section=new' )
00879                                                 );
00880                                         }
00881                                 // Checks if the page has some kind of viewable content
00882                                 } elseif ( $title->hasSourceText() ) {
00883                                         // Adds view source view link
00884                                         $content_navigation['views']['viewsource'] = array(
00885                                                 'class' => ( $onPage && $action == 'edit' ) ? 'selected' : false,
00886                                                 'text' => wfMessageFallback( "$skname-action-viewsource", 'viewsource' )->setContext( $this->getContext() )->text(),
00887                                                 'href' => $title->getLocalURL( $this->editUrlOptions() ),
00888                                                 'primary' => true, // don't collapse this in vector
00889                                         );
00890                                 }
00891                                 wfProfileOut( __METHOD__ . '-edit' );
00892 
00893                                 wfProfileIn( __METHOD__ . '-live' );
00894                                 // Checks if the page exists
00895                                 if ( $title->exists() ) {
00896                                         // Adds history view link
00897                                         $content_navigation['views']['history'] = array(
00898                                                 'class' => ( $onPage && $action == 'history' ) ? 'selected' : false,
00899                                                 'text' => wfMessageFallback( "$skname-view-history", 'history_short' )->setContext( $this->getContext() )->text(),
00900                                                 'href' => $title->getLocalURL( 'action=history' ),
00901                                                 'rel' => 'archives',
00902                                         );
00903 
00904                                         if ( $title->quickUserCan( 'delete', $user ) ) {
00905                                                 $content_navigation['actions']['delete'] = array(
00906                                                         'class' => ( $onPage && $action == 'delete' ) ? 'selected' : false,
00907                                                         'text' => wfMessageFallback( "$skname-action-delete", 'delete' )->setContext( $this->getContext() )->text(),
00908                                                         'href' => $title->getLocalURL( 'action=delete' )
00909                                                 );
00910                                         }
00911 
00912                                         if ( $title->quickUserCan( 'move', $user ) ) {
00913                                                 $moveTitle = SpecialPage::getTitleFor( 'Movepage', $title->getPrefixedDBkey() );
00914                                                 $content_navigation['actions']['move'] = array(
00915                                                         'class' => $this->getTitle()->isSpecial( 'Movepage' ) ? 'selected' : false,
00916                                                         'text' => wfMessageFallback( "$skname-action-move", 'move' )->setContext( $this->getContext() )->text(),
00917                                                         'href' => $moveTitle->getLocalURL()
00918                                                 );
00919                                         }
00920                                 } else {
00921                                         // article doesn't exist or is deleted
00922                                         if ( $user->isAllowed( 'deletedhistory' ) ) {
00923                                                 $n = $title->isDeleted();
00924                                                 if ( $n ) {
00925                                                         $undelTitle = SpecialPage::getTitleFor( 'Undelete' );
00926                                                         // If the user can't undelete but can view deleted history show them a "View .. deleted" tab instead
00927                                                         $msgKey = $user->isAllowed( 'undelete' ) ? 'undelete' : 'viewdeleted';
00928                                                         $content_navigation['actions']['undelete'] = array(
00929                                                                 'class' => $this->getTitle()->isSpecial( 'Undelete' ) ? 'selected' : false,
00930                                                                 'text' => wfMessageFallback( "$skname-action-$msgKey", "{$msgKey}_short" )
00931                                                                         ->setContext( $this->getContext() )->numParams( $n )->text(),
00932                                                                 'href' => $undelTitle->getLocalURL( array( 'target' => $title->getPrefixedDBkey() ) )
00933                                                         );
00934                                                 }
00935                                         }
00936                                 }
00937 
00938                                 if ( $title->getNamespace() !== NS_MEDIAWIKI && $title->quickUserCan( 'protect', $user ) ) {
00939                                         $mode = $title->isProtected() ? 'unprotect' : 'protect';
00940                                         $content_navigation['actions'][$mode] = array(
00941                                                 'class' => ( $onPage && $action == $mode ) ? 'selected' : false,
00942                                                 'text' => wfMessageFallback( "$skname-action-$mode", $mode )->setContext( $this->getContext() )->text(),
00943                                                 'href' => $title->getLocalURL( "action=$mode" )
00944                                         );
00945                                 }
00946 
00947                                 wfProfileOut( __METHOD__ . '-live' );
00948 
00949                                 // Checks if the user is logged in
00950                                 if ( $this->loggedin ) {
00960                                         $mode = $user->isWatched( $title ) ? 'unwatch' : 'watch';
00961                                         $token = WatchAction::getWatchToken( $title, $user, $mode );
00962                                         $content_navigation['actions'][$mode] = array(
00963                                                 'class' => $onPage && ( $action == 'watch' || $action == 'unwatch' ) ? 'selected' : false,
00964                                                 // uses 'watch' or 'unwatch' message
00965                                                 'text' => $this->msg( $mode )->text(),
00966                                                 'href' => $title->getLocalURL( array( 'action' => $mode, 'token' => $token ) )
00967                                         );
00968                                 }
00969                         }
00970 
00971                         wfRunHooks( 'SkinTemplateNavigation', array( &$this, &$content_navigation ) );
00972 
00973                         if ( $userCanRead && !$wgDisableLangConversion ) {
00974                                 $pageLang = $title->getPageLanguage();
00975                                 // Gets list of language variants
00976                                 $variants = $pageLang->getVariants();
00977                                 // Checks that language conversion is enabled and variants exist
00978                                 // And if it is not in the special namespace
00979                                 if ( count( $variants ) > 1 ) {
00980                                         // Gets preferred variant (note that user preference is
00981                                         // only possible for wiki content language variant)
00982                                         $preferred = $pageLang->getPreferredVariant();
00983                                         // Loops over each variant
00984                                         foreach( $variants as $code ) {
00985                                                 // Gets variant name from language code
00986                                                 $varname = $pageLang->getVariantname( $code );
00987                                                 // Checks if the variant is marked as disabled
00988                                                 if( $varname == 'disable' ) {
00989                                                         // Skips this variant
00990                                                         continue;
00991                                                 }
00992                                                 // Appends variant link
00993                                                 $content_navigation['variants'][] = array(
00994                                                         'class' => ( $code == $preferred ) ? 'selected' : false,
00995                                                         'text' => $varname,
00996                                                         'href' => $title->getLocalURL( array( 'variant' => $code ) ),
00997                                                         'lang' => $code,
00998                                                         'hreflang' => $code
00999                                                 );
01000                                         }
01001                                 }
01002                         }
01003                 } else {
01004                         // If it's not content, it's got to be a special page
01005                         $content_navigation['namespaces']['special'] = array(
01006                                 'class' => 'selected',
01007                                 'text' => $this->msg( 'nstab-special' )->text(),
01008                                 'href' => $request->getRequestURL(), // @see: bug 2457, bug 2510
01009                                 'context' => 'subject'
01010                         );
01011 
01012                         wfRunHooks( 'SkinTemplateNavigation::SpecialPage',
01013                                 array( &$this, &$content_navigation ) );
01014                 }
01015 
01016                 // Equiv to SkinTemplateContentActions
01017                 wfRunHooks( 'SkinTemplateNavigation::Universal', array( &$this,  &$content_navigation ) );
01018 
01019                 // Setup xml ids and tooltip info
01020                 foreach ( $content_navigation as $section => &$links ) {
01021                         foreach ( $links as $key => &$link ) {
01022                                 $xmlID = $key;
01023                                 if ( isset( $link['context'] ) && $link['context'] == 'subject' ) {
01024                                         $xmlID = 'ca-nstab-' . $xmlID;
01025                                 } elseif ( isset( $link['context'] ) && $link['context'] == 'talk' ) {
01026                                         $xmlID = 'ca-talk';
01027                                 } elseif ( $section == 'variants' ) {
01028                                         $xmlID = 'ca-varlang-' . $xmlID;
01029                                 } else {
01030                                         $xmlID = 'ca-' . $xmlID;
01031                                 }
01032                                 $link['id'] = $xmlID;
01033                         }
01034                 }
01035 
01036                 # We don't want to give the watch tab an accesskey if the
01037                 # page is being edited, because that conflicts with the
01038                 # accesskey on the watch checkbox.  We also don't want to
01039                 # give the edit tab an accesskey, because that's fairly su-
01040                 # perfluous and conflicts with an accesskey (Ctrl-E) often
01041                 # used for editing in Safari.
01042                 if ( in_array( $action, array( 'edit', 'submit' ) ) ) {
01043                         if ( isset( $content_navigation['views']['edit'] ) ) {
01044                                 $content_navigation['views']['edit']['tooltiponly'] = true;
01045                         }
01046                         if ( isset( $content_navigation['actions']['watch'] ) ) {
01047                                 $content_navigation['actions']['watch']['tooltiponly'] = true;
01048                         }
01049                         if ( isset( $content_navigation['actions']['unwatch'] ) ) {
01050                                 $content_navigation['actions']['unwatch']['tooltiponly'] = true;
01051                         }
01052                 }
01053 
01054                 wfProfileOut( __METHOD__ );
01055 
01056                 return $content_navigation;
01057         }
01058 
01064         function buildContentActionUrls( $content_navigation ) {
01065 
01066                 wfProfileIn( __METHOD__ );
01067 
01068                 // content_actions has been replaced with content_navigation for backwards
01069                 // compatibility and also for skins that just want simple tabs content_actions
01070                 // is now built by flattening the content_navigation arrays into one
01071 
01072                 $content_actions = array();
01073 
01074                 foreach ( $content_navigation as $links ) {
01075 
01076                         foreach ( $links as $key => $value ) {
01077 
01078                                 if ( isset( $value['redundant'] ) && $value['redundant'] ) {
01079                                         // Redundant tabs are dropped from content_actions
01080                                         continue;
01081                                 }
01082 
01083                                 // content_actions used to have ids built using the "ca-$key" pattern
01084                                 // so the xmlID based id is much closer to the actual $key that we want
01085                                 // for that reason we'll just strip out the ca- if present and use
01086                                 // the latter potion of the "id" as the $key
01087                                 if ( isset( $value['id'] ) && substr( $value['id'], 0, 3 ) == 'ca-' ) {
01088                                         $key = substr( $value['id'], 3 );
01089                                 }
01090 
01091                                 if ( isset( $content_actions[$key] ) ) {
01092                                         wfDebug( __METHOD__ . ": Found a duplicate key for $key while flattening content_navigation into content_actions." );
01093                                         continue;
01094                                 }
01095 
01096                                 $content_actions[$key] = $value;
01097 
01098                         }
01099 
01100                 }
01101 
01102                 wfProfileOut( __METHOD__ );
01103 
01104                 return $content_actions;
01105         }
01106 
01112         protected function buildNavUrls() {
01113                 global $wgUploadNavigationUrl;
01114 
01115                 wfProfileIn( __METHOD__ );
01116 
01117                 $out = $this->getOutput();
01118                 $request = $this->getRequest();
01119 
01120                 $nav_urls = array();
01121                 $nav_urls['mainpage'] = array( 'href' => self::makeMainPageUrl() );
01122                 if( $wgUploadNavigationUrl ) {
01123                         $nav_urls['upload'] = array( 'href' => $wgUploadNavigationUrl );
01124                 } elseif( UploadBase::isEnabled() && UploadBase::isAllowed( $this->getUser() ) === true ) {
01125                         $nav_urls['upload'] = array( 'href' => self::makeSpecialUrl( 'Upload' ) );
01126                 } else {
01127                         $nav_urls['upload'] = false;
01128                 }
01129                 $nav_urls['specialpages'] = array( 'href' => self::makeSpecialUrl( 'Specialpages' ) );
01130 
01131                 $nav_urls['print'] = false;
01132                 $nav_urls['permalink'] = false;
01133                 $nav_urls['whatlinkshere'] = false;
01134                 $nav_urls['recentchangeslinked'] = false;
01135                 $nav_urls['contributions'] = false;
01136                 $nav_urls['log'] = false;
01137                 $nav_urls['blockip'] = false;
01138                 $nav_urls['emailuser'] = false;
01139 
01140                 // A print stylesheet is attached to all pages, but nobody ever
01141                 // figures that out. :)  Add a link...
01142                 if ( $out->isArticle() ) {
01143                         if ( !$out->isPrintable() ) {
01144                                 $nav_urls['print'] = array(
01145                                         'text' => $this->msg( 'printableversion' )->text(),
01146                                         'href' => $this->getTitle()->getLocalURL(
01147                                                 $request->appendQueryValue( 'printable', 'yes', true ) )
01148                                 );
01149                         }
01150 
01151                         // Also add a "permalink" while we're at it
01152                         $revid = $this->getRevisionId();
01153                         if ( $revid ) {
01154                                 $nav_urls['permalink'] = array(
01155                                         'text' => $this->msg( 'permalink' )->text(),
01156                                         'href' => $this->getTitle()->getLocalURL( "oldid=$revid" )
01157                                 );
01158                         }
01159 
01160                         // Use the copy of revision ID in case this undocumented, shady hook tries to mess with internals
01161                         wfRunHooks( 'SkinTemplateBuildNavUrlsNav_urlsAfterPermalink',
01162                                 array( &$this, &$nav_urls, &$revid, &$revid ) );
01163                 }
01164 
01165                 if ( $out->isArticleRelated() ) {
01166                         $nav_urls['whatlinkshere'] = array(
01167                                 'href' => SpecialPage::getTitleFor( 'Whatlinkshere', $this->thispage )->getLocalUrl()
01168                         );
01169                         if ( $this->getTitle()->getArticleID() ) {
01170                                 $nav_urls['recentchangeslinked'] = array(
01171                                         'href' => SpecialPage::getTitleFor( 'Recentchangeslinked', $this->thispage )->getLocalUrl()
01172                                 );
01173                         }
01174                 }
01175 
01176                 $user = $this->getRelevantUser();
01177                 if ( $user ) {
01178                         $rootUser = $user->getName();
01179 
01180                         $nav_urls['contributions'] = array(
01181                                 'href' => self::makeSpecialUrlSubpage( 'Contributions', $rootUser )
01182                         );
01183 
01184                         $nav_urls['log'] = array(
01185                                 'href' => self::makeSpecialUrlSubpage( 'Log', $rootUser )
01186                         );
01187 
01188                         if ( $this->getUser()->isAllowed( 'block' ) ) {
01189                                 $nav_urls['blockip'] = array(
01190                                         'href' => self::makeSpecialUrlSubpage( 'Block', $rootUser )
01191                                 );
01192                         }
01193 
01194                         if ( $this->showEmailUser( $user ) ) {
01195                                 $nav_urls['emailuser'] = array(
01196                                         'href' => self::makeSpecialUrlSubpage( 'Emailuser', $rootUser )
01197                                 );
01198                         }
01199                 }
01200 
01201                 wfProfileOut( __METHOD__ );
01202                 return $nav_urls;
01203         }
01204 
01210         function getNameSpaceKey() {
01211                 return $this->getTitle()->getNamespaceKey();
01212         }
01213 
01214         public function commonPrintStylesheet() {
01215                 return false;
01216         }
01217 }
01218 
01224 abstract class QuickTemplate {
01228         public function QuickTemplate() {
01229                 $this->data = array();
01230                 $this->translator = new MediaWiki_I18N();
01231         }
01232 
01238         public function set( $name, $value ) {
01239                 $this->data[$name] = $value;
01240         }
01241 
01246         public function setRef( $name, &$value ) {
01247                 $this->data[$name] =& $value;
01248         }
01249 
01253         public function setTranslator( &$t ) {
01254                 $this->translator = &$t;
01255         }
01256 
01261         abstract public function execute();
01262 
01266         function text( $str ) {
01267                 echo htmlspecialchars( $this->data[$str] );
01268         }
01269 
01273         function jstext( $str ) {
01274                 echo Xml::escapeJsString( $this->data[$str] );
01275         }
01276 
01280         function html( $str ) {
01281                 echo $this->data[$str];
01282         }
01283 
01287         function msg( $str ) {
01288                 echo htmlspecialchars( $this->translator->translate( $str ) );
01289         }
01290 
01294         function msgHtml( $str ) {
01295                 echo $this->translator->translate( $str );
01296         }
01297 
01302         function msgWiki( $str ) {
01303                 global $wgOut;
01304 
01305                 $text = $this->translator->translate( $str );
01306                 echo $wgOut->parse( $text );
01307         }
01308 
01313         function haveData( $str ) {
01314                 return isset( $this->data[$str] );
01315         }
01316 
01322         function haveMsg( $str ) {
01323                 $msg = $this->translator->translate( $str );
01324                 return ( $msg != '-' ) && ( $msg != '' ); # ????
01325         }
01326 
01332         public function getSkin() {
01333                 return $this->data['skin'];
01334         }
01335 }
01336 
01342 abstract class BaseTemplate extends QuickTemplate {
01343 
01350         public function getMsg( $name ) {
01351                 return $this->getSkin()->msg( $name );
01352         }
01353 
01354         function msg( $str ) {
01355                 echo $this->getMsg( $str )->escaped();
01356         }
01357 
01358         function msgHtml( $str ) {
01359                 echo $this->getMsg( $str )->text();
01360         }
01361 
01362         function msgWiki( $str ) {
01363                 echo $this->getMsg( $str )->parseAsBlock();
01364         }
01365 
01373         function getToolbox() {
01374                 wfProfileIn( __METHOD__ );
01375 
01376                 $toolbox = array();
01377                 if ( isset( $this->data['nav_urls']['whatlinkshere'] ) && $this->data['nav_urls']['whatlinkshere'] ) {
01378                         $toolbox['whatlinkshere'] = $this->data['nav_urls']['whatlinkshere'];
01379                         $toolbox['whatlinkshere']['id'] = 't-whatlinkshere';
01380                 }
01381                 if ( isset( $this->data['nav_urls']['recentchangeslinked'] ) && $this->data['nav_urls']['recentchangeslinked'] ) {
01382                         $toolbox['recentchangeslinked'] = $this->data['nav_urls']['recentchangeslinked'];
01383                         $toolbox['recentchangeslinked']['msg'] = 'recentchangeslinked-toolbox';
01384                         $toolbox['recentchangeslinked']['id'] = 't-recentchangeslinked';
01385                 }
01386                 if ( isset( $this->data['feeds'] ) && $this->data['feeds'] ) {
01387                         $toolbox['feeds']['id'] = 'feedlinks';
01388                         $toolbox['feeds']['links'] = array();
01389                         foreach ( $this->data['feeds'] as $key => $feed ) {
01390                                 $toolbox['feeds']['links'][$key] = $feed;
01391                                 $toolbox['feeds']['links'][$key]['id'] = "feed-$key";
01392                                 $toolbox['feeds']['links'][$key]['rel'] = 'alternate';
01393                                 $toolbox['feeds']['links'][$key]['type'] = "application/{$key}+xml";
01394                                 $toolbox['feeds']['links'][$key]['class'] = 'feedlink';
01395                         }
01396                 }
01397                 foreach ( array( 'contributions', 'log', 'blockip', 'emailuser', 'upload', 'specialpages' ) as $special ) {
01398                         if ( isset( $this->data['nav_urls'][$special] ) && $this->data['nav_urls'][$special] ) {
01399                                 $toolbox[$special] = $this->data['nav_urls'][$special];
01400                                 $toolbox[$special]['id'] = "t-$special";
01401                         }
01402                 }
01403                 if ( isset( $this->data['nav_urls']['print'] ) && $this->data['nav_urls']['print'] ) {
01404                         $toolbox['print'] = $this->data['nav_urls']['print'];
01405                         $toolbox['print']['id'] = 't-print';
01406                         $toolbox['print']['rel'] = 'alternate';
01407                         $toolbox['print']['msg'] = 'printableversion';
01408                 }
01409                 if ( isset( $this->data['nav_urls']['permalink'] ) && $this->data['nav_urls']['permalink'] ) {
01410                         $toolbox['permalink'] = $this->data['nav_urls']['permalink'];
01411                         if ( $toolbox['permalink']['href'] === '' ) {
01412                                 unset( $toolbox['permalink']['href'] );
01413                                 $toolbox['ispermalink']['tooltiponly'] = true;
01414                                 $toolbox['ispermalink']['id'] = 't-ispermalink';
01415                                 $toolbox['ispermalink']['msg'] = 'permalink';
01416                         } else {
01417                                 $toolbox['permalink']['id'] = 't-permalink';
01418                         }
01419                 }
01420                 wfRunHooks( 'BaseTemplateToolbox', array( &$this, &$toolbox ) );
01421                 wfProfileOut( __METHOD__ );
01422                 return $toolbox;
01423         }
01424 
01435         function getPersonalTools() {
01436                 $personal_tools = array();
01437                 foreach ( $this->data['personal_urls'] as $key => $plink ) {
01438                         # The class on a personal_urls item is meant to go on the <a> instead
01439                         # of the <li> so we have to use a single item "links" array instead
01440                         # of using most of the personal_url's keys directly.
01441                         $ptool = array(
01442                                 'links' => array(
01443                                         array( 'single-id' => "pt-$key" ),
01444                                 ),
01445                                 'id' => "pt-$key",
01446                         );
01447                         if ( isset( $plink['active'] ) ) {
01448                                 $ptool['active'] = $plink['active'];
01449                         }
01450                         foreach ( array( 'href', 'class', 'text' ) as $k ) {
01451                                 if ( isset( $plink[$k] ) )
01452                                         $ptool['links'][0][$k] = $plink[$k];
01453                         }
01454                         $personal_tools[$key] = $ptool;
01455                 }
01456                 return $personal_tools;
01457         }
01458 
01459         function getSidebar( $options = array() ) {
01460                 // Force the rendering of the following portals
01461                 $sidebar = $this->data['sidebar'];
01462                 if ( !isset( $sidebar['SEARCH'] ) ) {
01463                         $sidebar['SEARCH'] = true;
01464                 }
01465                 if ( !isset( $sidebar['TOOLBOX'] ) ) {
01466                         $sidebar['TOOLBOX'] = true;
01467                 }
01468                 if ( !isset( $sidebar['LANGUAGES'] ) ) {
01469                         $sidebar['LANGUAGES'] = true;
01470                 }
01471 
01472                 if ( !isset( $options['search'] ) || $options['search'] !== true ) {
01473                         unset( $sidebar['SEARCH'] );
01474                 }
01475                 if ( isset( $options['toolbox'] ) && $options['toolbox'] === false ) {
01476                         unset( $sidebar['TOOLBOX'] );
01477                 }
01478                 if ( isset( $options['languages'] ) && $options['languages'] === false ) {
01479                         unset( $sidebar['LANGUAGES'] );
01480                 }
01481 
01482                 $boxes = array();
01483                 foreach ( $sidebar as $boxName => $content ) {
01484                         if ( $content === false ) {
01485                                 continue;
01486                         }
01487                         switch ( $boxName ) {
01488                         case 'SEARCH':
01489                                 // Search is a special case, skins should custom implement this
01490                                 $boxes[$boxName] = array(
01491                                         'id'        => 'p-search',
01492                                         'header'    => $this->getMsg( 'search' )->text(),
01493                                         'generated' => false,
01494                                         'content'   => true,
01495                                 );
01496                                 break;
01497                         case 'TOOLBOX':
01498                                 $msgObj = $this->getMsg( 'toolbox' );
01499                                 $boxes[$boxName] = array(
01500                                         'id'        => 'p-tb',
01501                                         'header'    => $msgObj->exists() ? $msgObj->text() : 'toolbox',
01502                                         'generated' => false,
01503                                         'content'   => $this->getToolbox(),
01504                                 );
01505                                 break;
01506                         case 'LANGUAGES':
01507                                 if ( $this->data['language_urls'] ) {
01508                                         $msgObj = $this->getMsg( 'otherlanguages' );
01509                                         $boxes[$boxName] = array(
01510                                                 'id'        => 'p-lang',
01511                                                 'header'    => $msgObj->exists() ? $msgObj->text() : 'otherlanguages',
01512                                                 'generated' => false,
01513                                                 'content'   => $this->data['language_urls'],
01514                                         );
01515                                 }
01516                                 break;
01517                         default:
01518                                 $msgObj = $this->getMsg( $boxName );
01519                                 $boxes[$boxName] = array(
01520                                         'id'        => "p-$boxName",
01521                                         'header'    => $msgObj->exists() ? $msgObj->text() : $boxName,
01522                                         'generated' => true,
01523                                         'content'   => $content,
01524                                 );
01525                                 break;
01526                         }
01527                 }
01528 
01529                 // HACK: Compatibility with extensions still using SkinTemplateToolboxEnd
01530                 $hookContents = null;
01531                 if ( isset( $boxes['TOOLBOX'] ) ) {
01532                         ob_start();
01533                         // We pass an extra 'true' at the end so extensions using BaseTemplateToolbox
01534                         // can abort and avoid outputting double toolbox links
01535                         wfRunHooks( 'SkinTemplateToolboxEnd', array( &$this, true ) );
01536                         $hookContents = ob_get_contents();
01537                         ob_end_clean();
01538                         if ( !trim( $hookContents ) ) {
01539                                 $hookContents = null;
01540                         }
01541                 }
01542                 // END hack
01543 
01544                 if ( isset( $options['htmlOnly'] ) && $options['htmlOnly'] === true ) {
01545                         foreach ( $boxes as $boxName => $box ) {
01546                                 if ( is_array( $box['content'] ) ) {
01547                                         $content = '<ul>';
01548                                         foreach ( $box['content'] as $key => $val ) {
01549                                                 $content .= "\n " . $this->makeListItem( $key, $val );
01550                                         }
01551                                         // HACK, shove the toolbox end onto the toolbox if we're rendering itself
01552                                         if ( $hookContents ) {
01553                                                 $content .= "\n $hookContents";
01554                                         }
01555                                         // END hack
01556                                         $content .= "\n</ul>\n";
01557                                         $boxes[$boxName]['content'] = $content;
01558                                 }
01559                         }
01560                 } else {
01561                         if ( $hookContents ) {
01562                                 $boxes['TOOLBOXEND'] = array(
01563                                         'id'        => 'p-toolboxend',
01564                                         'header'    => $boxes['TOOLBOX']['header'],
01565                                         'generated' => false,
01566                                         'content'   => "<ul>{$hookContents}</ul>",
01567                                 );
01568                                 // HACK: Make sure that TOOLBOXEND is sorted next to TOOLBOX
01569                                 $boxes2 = array();
01570                                 foreach ( $boxes as $key => $box ) {
01571                                         if ( $key === 'TOOLBOXEND' ) {
01572                                                 continue;
01573                                         }
01574                                         $boxes2[$key] = $box;
01575                                         if ( $key === 'TOOLBOX' ) {
01576                                                 $boxes2['TOOLBOXEND'] = $boxes['TOOLBOXEND'];
01577                                         }
01578                                 }
01579                                 $boxes = $boxes2;
01580                                 // END hack
01581                         }
01582                 }
01583 
01584                 return $boxes;
01585         }
01586 
01626         function makeLink( $key, $item, $options = array() ) {
01627                 if ( isset( $item['text'] ) ) {
01628                         $text = $item['text'];
01629                 } else {
01630                         $text = $this->translator->translate( isset( $item['msg'] ) ? $item['msg'] : $key );
01631                 }
01632 
01633                 $html = htmlspecialchars( $text );
01634 
01635                 if ( isset( $options['text-wrapper'] ) ) {
01636                         $wrapper = $options['text-wrapper'];
01637                         if ( isset( $wrapper['tag'] ) ) {
01638                                 $wrapper = array( $wrapper );
01639                         }
01640                         while ( count( $wrapper ) > 0 ) {
01641                                 $element = array_pop( $wrapper );
01642                                 $html = Html::rawElement( $element['tag'], isset( $element['attributes'] ) ? $element['attributes'] : null, $html );
01643                         }
01644                 }
01645 
01646                 if ( isset( $item['href'] ) || isset( $options['link-fallback'] ) ) {
01647                         $attrs = $item;
01648                         foreach ( array( 'single-id', 'text', 'msg', 'tooltiponly' ) as $k ) {
01649                                 unset( $attrs[$k] );
01650                         }
01651 
01652                         if ( isset( $item['id'] ) && !isset( $item['single-id'] ) ) {
01653                                 $item['single-id'] = $item['id'];
01654                         }
01655                         if ( isset( $item['single-id'] ) ) {
01656                                 if ( isset( $item['tooltiponly'] ) && $item['tooltiponly'] ) {
01657                                         $title = Linker::titleAttrib( $item['single-id'] );
01658                                         if ( $title !== false ) {
01659                                                 $attrs['title'] = $title;
01660                                         }
01661                                 } else {
01662                                         $tip = Linker::tooltipAndAccesskeyAttribs( $item['single-id'] );
01663                                         if ( isset( $tip['title'] ) && $tip['title'] !== false ) {
01664                                                 $attrs['title'] = $tip['title'];
01665                                         }
01666                                         if ( isset( $tip['accesskey'] ) && $tip['accesskey'] !== false ) {
01667                                                 $attrs['accesskey'] = $tip['accesskey'];
01668                                         }
01669                                 }
01670                         }
01671                         if ( isset( $options['link-class'] ) ) {
01672                                 if ( isset( $attrs['class'] ) ) {
01673                                         $attrs['class'] .= " {$options['link-class']}";
01674                                 } else {
01675                                         $attrs['class'] = $options['link-class'];
01676                                 }
01677                         }
01678                         $html = Html::rawElement( isset( $attrs['href'] ) ? 'a' : $options['link-fallback'], $attrs, $html );
01679                 }
01680 
01681                 return $html;
01682         }
01683 
01711         function makeListItem( $key, $item, $options = array() ) {
01712                 if ( isset( $item['links'] ) ) {
01713                         $html = '';
01714                         foreach ( $item['links'] as $linkKey => $link ) {
01715                                 $html .= $this->makeLink( $linkKey, $link, $options );
01716                         }
01717                 } else {
01718                         $link = $item;
01719                         // These keys are used by makeListItem and shouldn't be passed on to the link
01720                         foreach ( array( 'id', 'class', 'active', 'tag' ) as $k ) {
01721                                 unset( $link[$k] );
01722                         }
01723                         if ( isset( $item['id'] ) ) {
01724                                 // The id goes on the <li> not on the <a> for single links
01725                                 // but makeSidebarLink still needs to know what id to use when
01726                                 // generating tooltips and accesskeys.
01727                                 $link['single-id'] = $item['id'];
01728                         }
01729                         $html = $this->makeLink( $key, $link, $options );
01730                 }
01731 
01732                 $attrs = array();
01733                 foreach ( array( 'id', 'class' ) as $attr ) {
01734                         if ( isset( $item[$attr] ) ) {
01735                                 $attrs[$attr] = $item[$attr];
01736                         }
01737                 }
01738                 if ( isset( $item['active'] ) && $item['active'] ) {
01739                         if ( !isset( $attrs['class'] ) ) {
01740                                 $attrs['class'] = '';
01741                         }
01742                         $attrs['class'] .= ' active';
01743                         $attrs['class'] = trim( $attrs['class'] );
01744                 }
01745                 return Html::rawElement( isset( $options['tag'] ) ? $options['tag'] : 'li', $attrs, $html );
01746         }
01747 
01748         function makeSearchInput( $attrs = array() ) {
01749                 $realAttrs = array(
01750                         'type' => 'search',
01751                         'name' => 'search',
01752                         'value' => isset( $this->data['search'] ) ? $this->data['search'] : '',
01753                 );
01754                 $realAttrs = array_merge( $realAttrs, Linker::tooltipAndAccesskeyAttribs( 'search' ), $attrs );
01755                 return Html::element( 'input', $realAttrs );
01756         }
01757 
01758         function makeSearchButton( $mode, $attrs = array() ) {
01759                 switch( $mode ) {
01760                         case 'go':
01761                         case 'fulltext':
01762                                 $realAttrs = array(
01763                                         'type' => 'submit',
01764                                         'name' => $mode,
01765                                         'value' => $this->translator->translate(
01766                                                 $mode == 'go' ? 'searcharticle' : 'searchbutton' ),
01767                                 );
01768                                 $realAttrs = array_merge(
01769                                         $realAttrs,
01770                                         Linker::tooltipAndAccesskeyAttribs( "search-$mode" ),
01771                                         $attrs
01772                                 );
01773                                 return Html::element( 'input', $realAttrs );
01774                         case 'image':
01775                                 $buttonAttrs = array(
01776                                         'type' => 'submit',
01777                                         'name' => 'button',
01778                                 );
01779                                 $buttonAttrs = array_merge(
01780                                         $buttonAttrs,
01781                                         Linker::tooltipAndAccesskeyAttribs( 'search-fulltext' ),
01782                                         $attrs
01783                                 );
01784                                 unset( $buttonAttrs['src'] );
01785                                 unset( $buttonAttrs['alt'] );
01786                                 $imgAttrs = array(
01787                                         'src' => $attrs['src'],
01788                                         'alt' => isset( $attrs['alt'] )
01789                                                 ? $attrs['alt']
01790                                                 : $this->translator->translate( 'searchbutton' ),
01791                                 );
01792                                 return Html::rawElement( 'button', $buttonAttrs, Html::element( 'img', $imgAttrs ) );
01793                         default:
01794                                 throw new MWException( 'Unknown mode passed to BaseTemplate::makeSearchButton' );
01795                 }
01796         }
01797 
01806         function getFooterLinks( $option = null ) {
01807                 $footerlinks = $this->data['footerlinks'];
01808 
01809                 // Reduce footer links down to only those which are being used
01810                 $validFooterLinks = array();
01811                 foreach( $footerlinks as $category => $links ) {
01812                         $validFooterLinks[$category] = array();
01813                         foreach( $links as $link ) {
01814                                 if( isset( $this->data[$link] ) && $this->data[$link] ) {
01815                                         $validFooterLinks[$category][] = $link;
01816                                 }
01817                         }
01818                         if ( count( $validFooterLinks[$category] ) <= 0 ) {
01819                                 unset( $validFooterLinks[$category] );
01820                         }
01821                 }
01822 
01823                 if ( $option == 'flat' ) {
01824                         // fold footerlinks into a single array using a bit of trickery
01825                         $validFooterLinks = call_user_func_array(
01826                                 'array_merge',
01827                                 array_values( $validFooterLinks )
01828                         );
01829                 }
01830 
01831                 return $validFooterLinks;
01832         }
01833 
01845         function getFooterIcons( $option = null ) {
01846                 // Generate additional footer icons
01847                 $footericons = $this->data['footericons'];
01848 
01849                 if ( $option == 'icononly' ) {
01850                         // Unset any icons which don't have an image
01851                         foreach ( $footericons as &$footerIconsBlock ) {
01852                                 foreach ( $footerIconsBlock as $footerIconKey => $footerIcon ) {
01853                                         if ( !is_string( $footerIcon ) && !isset( $footerIcon['src'] ) ) {
01854                                                 unset( $footerIconsBlock[$footerIconKey] );
01855                                         }
01856                                 }
01857                         }
01858                         // Redo removal of any empty blocks
01859                         foreach ( $footericons as $footerIconsKey => &$footerIconsBlock ) {
01860                                 if ( count( $footerIconsBlock ) <= 0 ) {
01861                                         unset( $footericons[$footerIconsKey] );
01862                                 }
01863                         }
01864                 } elseif ( $option == 'nocopyright' ) {
01865                         unset( $footericons['copyright']['copyright'] );
01866                         if ( count( $footericons['copyright'] ) <= 0 ) {
01867                                 unset( $footericons['copyright'] );
01868                         }
01869                 }
01870 
01871                 return $footericons;
01872         }
01873 
01879         function printTrail() { ?>
01880 <?php $this->html( 'bottomscripts' ); /* JS call to runBodyOnloadHook */ ?>
01881 <?php $this->html( 'reporttime' ) ?>
01882 <?php echo MWDebug::getDebugHTML( $this->getSkin()->getContext() );
01883         }
01884 
01885 }