MediaWiki  REL1_21
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                         Action::getActionName( $this ) === 'view' &&
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 $languageLinkText ) {
00417                                 $languageLinkParts = explode( ':', $languageLinkText, 2 );
00418                                 $class = 'interwiki-' . $languageLinkParts[0];
00419                                 unset( $languageLinkParts );
00420                                 $languageLinkTitle = Title::newFromText( $languageLinkText );
00421                                 if ( $languageLinkTitle ) {
00422                                         $ilInterwikiCode = $languageLinkTitle->getInterwiki();
00423                                         $ilLangName = Language::fetchLanguageName( $ilInterwikiCode );
00424 
00425                                         if ( strval( $ilLangName ) === '' ) {
00426                                                 $ilLangName = $languageLinkText;
00427                                         } else {
00428                                                 $ilLangName = $this->formatLanguageName( $ilLangName );
00429                                         }
00430 
00431                                         $language_urls[] = array(
00432                                                 'href' => $languageLinkTitle->getFullURL(),
00433                                                 'text' => $ilLangName,
00434                                                 'title' => $languageLinkTitle->getText(),
00435                                                 'class' => $class,
00436                                                 'lang' => $ilInterwikiCode,
00437                                                 'hreflang' => $ilInterwikiCode
00438                                         );
00439                                 }
00440                         }
00441                 }
00442                 if ( count( $language_urls ) ) {
00443                         $tpl->setRef( 'language_urls', $language_urls );
00444                 } else {
00445                         $tpl->set( 'language_urls', false );
00446                 }
00447                 wfProfileOut( __METHOD__ . '-stuff4' );
00448 
00449                 wfProfileIn( __METHOD__ . '-stuff5' );
00450                 # Personal toolbar
00451                 $tpl->set( 'personal_urls', $this->buildPersonalUrls() );
00452                 $content_navigation = $this->buildContentNavigationUrls();
00453                 $content_actions = $this->buildContentActionUrls( $content_navigation );
00454                 $tpl->setRef( 'content_navigation', $content_navigation );
00455                 $tpl->setRef( 'content_actions', $content_actions );
00456 
00457                 $tpl->set( 'sidebar', $this->buildSidebar() );
00458                 $tpl->set( 'nav_urls', $this->buildNavUrls() );
00459 
00460                 // Set the head scripts near the end, in case the above actions resulted in added scripts
00461                 if ( $this->useHeadElement ) {
00462                         $tpl->set( 'headelement', $out->headElement( $this ) );
00463                 } else {
00464                         $tpl->set( 'headscripts', $out->getHeadScripts() . $out->getHeadItems() );
00465                 }
00466 
00467                 $tpl->set( 'debug', '' );
00468                 $tpl->set( 'debughtml', $this->generateDebugHTML() );
00469                 $tpl->set( 'reporttime', wfReportTime() );
00470 
00471                 // original version by hansm
00472                 if( !wfRunHooks( 'SkinTemplateOutputPageBeforeExec', array( &$this, &$tpl ) ) ) {
00473                         wfDebug( __METHOD__ . ": Hook SkinTemplateOutputPageBeforeExec broke outputPage execution!\n" );
00474                 }
00475 
00476                 // Set the bodytext to another key so that skins can just output it on it's own
00477                 // and output printfooter and debughtml separately
00478                 $tpl->set( 'bodycontent', $tpl->data['bodytext'] );
00479 
00480                 // Append printfooter and debughtml onto bodytext so that skins that were already
00481                 // using bodytext before they were split out don't suddenly start not outputting information
00482                 $tpl->data['bodytext'] .= Html::rawElement( 'div', array( 'class' => 'printfooter' ), "\n{$tpl->data['printfooter']}" ) . "\n";
00483                 $tpl->data['bodytext'] .= $tpl->data['debughtml'];
00484 
00485                 // allow extensions adding stuff after the page content.
00486                 // See Skin::afterContentHook() for further documentation.
00487                 $tpl->set( 'dataAfterContent', $this->afterContentHook() );
00488                 wfProfileOut( __METHOD__ . '-stuff5' );
00489 
00490                 // execute template
00491                 wfProfileIn( __METHOD__ . '-execute' );
00492                 $res = $tpl->execute();
00493                 wfProfileOut( __METHOD__ . '-execute' );
00494 
00495                 // result may be an error
00496                 $this->printOrError( $res );
00497 
00498                 if ( $oldContext ) {
00499                         $this->setContext( $oldContext );
00500                 }
00501                 wfProfileOut( __METHOD__ );
00502         }
00503 
00512         function formatLanguageName( $name ) {
00513                 return $this->getLanguage()->ucfirst( $name );
00514         }
00515 
00524         function printOrError( $str ) {
00525                 echo $str;
00526         }
00527 
00537         function useCombinedLoginLink() {
00538                 global $wgUseCombinedLoginLink;
00539                 return $wgUseCombinedLoginLink;
00540         }
00541 
00546         protected function buildPersonalUrls() {
00547                 global $wgSecureLogin;
00548 
00549                 $title = $this->getTitle();
00550                 $request = $this->getRequest();
00551                 $pageurl = $title->getLocalURL();
00552                 wfProfileIn( __METHOD__ );
00553 
00554                 /* set up the default links for the personal toolbar */
00555                 $personal_urls = array();
00556 
00557                 # Due to bug 32276, if a user does not have read permissions,
00558                 # $this->getTitle() will just give Special:Badtitle, which is
00559                 # not especially useful as a returnto parameter. Use the title
00560                 # from the request instead, if there was one.
00561                 if ( $this->getUser()->isAllowed( 'read' ) ) {
00562                         $page = $this->getTitle();
00563                 } else {
00564                         $page = Title::newFromText( $request->getVal( 'title', '' ) );
00565                 }
00566                 $page = $request->getVal( 'returnto', $page );
00567                 $a = array();
00568                 if ( strval( $page ) !== '' ) {
00569                         $a['returnto'] = $page;
00570                         $query = $request->getVal( 'returntoquery', $this->thisquery );
00571                         if( $query != '' ) {
00572                                 $a['returntoquery'] = $query;
00573                         }
00574                 }
00575 
00576                 if ( $wgSecureLogin && $request->detectProtocol() === 'https' ) {
00577                         $a['wpStickHTTPS'] = true;
00578                 }
00579 
00580                 $returnto = wfArrayToCgi( $a );
00581                 if( $this->loggedin ) {
00582                         $personal_urls['userpage'] = array(
00583                                 'text' => $this->username,
00584                                 'href' => &$this->userpageUrlDetails['href'],
00585                                 'class' => $this->userpageUrlDetails['exists'] ? false : 'new',
00586                                 'active' => ( $this->userpageUrlDetails['href'] == $pageurl ),
00587                                 'dir' => 'auto'
00588                         );
00589                         $usertalkUrlDetails = $this->makeTalkUrlDetails( $this->userpage );
00590                         $personal_urls['mytalk'] = array(
00591                                 'text' => $this->msg( 'mytalk' )->text(),
00592                                 'href' => &$usertalkUrlDetails['href'],
00593                                 'class' => $usertalkUrlDetails['exists'] ? false : 'new',
00594                                 'active' => ( $usertalkUrlDetails['href'] == $pageurl )
00595                         );
00596                         $href = self::makeSpecialUrl( 'Preferences' );
00597                         $personal_urls['preferences'] = array(
00598                                 'text' => $this->msg( 'mypreferences' )->text(),
00599                                 'href' => $href,
00600                                 'active' => ( $href == $pageurl )
00601                         );
00602                         $href = self::makeSpecialUrl( 'Watchlist' );
00603                         $personal_urls['watchlist'] = array(
00604                                 'text' => $this->msg( 'mywatchlist' )->text(),
00605                                 'href' => $href,
00606                                 'active' => ( $href == $pageurl )
00607                         );
00608 
00609                         # We need to do an explicit check for Special:Contributions, as we
00610                         # have to match both the title, and the target, which could come
00611                         # from request values (Special:Contributions?target=Jimbo_Wales)
00612                         # or be specified in "sub page" form
00613                         # (Special:Contributions/Jimbo_Wales). The plot
00614                         # thickens, because the Title object is altered for special pages,
00615                         # so it doesn't contain the original alias-with-subpage.
00616                         $origTitle = Title::newFromText( $request->getText( 'title' ) );
00617                         if( $origTitle instanceof Title && $origTitle->isSpecialPage() ) {
00618                                 list( $spName, $spPar ) = SpecialPageFactory::resolveAlias( $origTitle->getText() );
00619                                 $active = $spName == 'Contributions'
00620                                         && ( ( $spPar && $spPar == $this->username )
00621                                                 || $request->getText( 'target' ) == $this->username );
00622                         } else {
00623                                 $active = false;
00624                         }
00625 
00626                         $href = self::makeSpecialUrlSubpage( 'Contributions', $this->username );
00627                         $personal_urls['mycontris'] = array(
00628                                 'text' => $this->msg( 'mycontris' )->text(),
00629                                 'href' => $href,
00630                                 'active' => $active
00631                         );
00632                         $personal_urls['logout'] = array(
00633                                 'text' => $this->msg( 'userlogout' )->text(),
00634                                 'href' => self::makeSpecialUrl( 'Userlogout',
00635                                         // userlogout link must always contain an & character, otherwise we might not be able
00636                                         // to detect a buggy precaching proxy (bug 17790)
00637                                         $title->isSpecial( 'Preferences' ) ? 'noreturnto' : $returnto
00638                                 ),
00639                                 'active' => false
00640                         );
00641                 } else {
00642                         $useCombinedLoginLink = $this->useCombinedLoginLink();
00643                         $loginlink = $this->getUser()->isAllowed( 'createaccount' ) && $useCombinedLoginLink
00644                                 ? 'nav-login-createaccount'
00645                                 : 'login';
00646                         $is_signup = $request->getText( 'type' ) == 'signup';
00647 
00648                         # anonlogin & login are the same
00649                         $proto = $wgSecureLogin ? PROTO_HTTPS : null;
00650 
00651                         $login_id = $this->showIPinHeader() ? 'anonlogin' : 'login';
00652                         $login_url = array(
00653                                 'text' => $this->msg( $loginlink )->text(),
00654                                 'href' => self::makeSpecialUrl( 'Userlogin', $returnto, $proto ),
00655                                 'active' => $title->isSpecial( 'Userlogin' ) && ( $loginlink == 'nav-login-createaccount' || !$is_signup ),
00656                                 'class' => $wgSecureLogin ? 'link-https' : ''
00657                         );
00658                         $createaccount_url = array(
00659                                 'text' => $this->msg( 'createaccount' )->text(),
00660                                 'href' => self::makeSpecialUrl( 'Userlogin', "$returnto&type=signup", $proto ),
00661                                 'active' => $title->isSpecial( 'Userlogin' ) && $is_signup,
00662                                 'class' => $wgSecureLogin ? 'link-https' : ''
00663                         );
00664 
00665                         if( $this->showIPinHeader() ) {
00666                                 $href = &$this->userpageUrlDetails['href'];
00667                                 $personal_urls['anonuserpage'] = array(
00668                                         'text' => $this->username,
00669                                         'href' => $href,
00670                                         'class' => $this->userpageUrlDetails['exists'] ? false : 'new',
00671                                         'active' => ( $pageurl == $href )
00672                                 );
00673                                 $usertalkUrlDetails = $this->makeTalkUrlDetails( $this->userpage );
00674                                 $href = &$usertalkUrlDetails['href'];
00675                                 $personal_urls['anontalk'] = array(
00676                                         'text' => $this->msg( 'anontalk' )->text(),
00677                                         'href' => $href,
00678                                         'class' => $usertalkUrlDetails['exists'] ? false : 'new',
00679                                         'active' => ( $pageurl == $href )
00680                                 );
00681                         }
00682 
00683                         if ( $this->getUser()->isAllowed( 'createaccount' ) && !$useCombinedLoginLink ) {
00684                                 $personal_urls['createaccount'] = $createaccount_url;
00685                         }
00686 
00687                         $personal_urls[$login_id] = $login_url;
00688                 }
00689 
00690                 wfRunHooks( 'PersonalUrls', array( &$personal_urls, &$title ) );
00691                 wfProfileOut( __METHOD__ );
00692                 return $personal_urls;
00693         }
00694 
00706         function tabAction( $title, $message, $selected, $query = '', $checkEdit = false ) {
00707                 $classes = array();
00708                 if( $selected ) {
00709                         $classes[] = 'selected';
00710                 }
00711                 if( $checkEdit && !$title->isKnown() ) {
00712                         $classes[] = 'new';
00713                         if ( $query !== '' ) {
00714                                 $query = 'action=edit&redlink=1&' . $query;
00715                         } else {
00716                                 $query = 'action=edit&redlink=1';
00717                         }
00718                 }
00719 
00720                 // wfMessageFallback will nicely accept $message as an array of fallbacks
00721                 // or just a single key
00722                 $msg = wfMessageFallback( $message )->setContext( $this->getContext() );
00723                 if ( is_array( $message ) ) {
00724                         // for hook compatibility just keep the last message name
00725                         $message = end( $message );
00726                 }
00727                 if ( $msg->exists() ) {
00728                         $text = $msg->text();
00729                 } else {
00730                         global $wgContLang;
00731                         $text = $wgContLang->getFormattedNsText(
00732                                 MWNamespace::getSubject( $title->getNamespace() ) );
00733                 }
00734 
00735                 $result = array();
00736                 if( !wfRunHooks( 'SkinTemplateTabAction', array( &$this,
00737                                 $title, $message, $selected, $checkEdit,
00738                                 &$classes, &$query, &$text, &$result ) ) ) {
00739                         return $result;
00740                 }
00741 
00742                 return array(
00743                         'class' => implode( ' ', $classes ),
00744                         'text' => $text,
00745                         'href' => $title->getLocalUrl( $query ),
00746                         'primary' => true );
00747         }
00748 
00749         function makeTalkUrlDetails( $name, $urlaction = '' ) {
00750                 $title = Title::newFromText( $name );
00751                 if( !is_object( $title ) ) {
00752                         throw new MWException( __METHOD__ . " given invalid pagename $name" );
00753                 }
00754                 $title = $title->getTalkPage();
00755                 self::checkTitle( $title, $name );
00756                 return array(
00757                         'href' => $title->getLocalURL( $urlaction ),
00758                         'exists' => $title->getArticleID() != 0,
00759                 );
00760         }
00761 
00762         function makeArticleUrlDetails( $name, $urlaction = '' ) {
00763                 $title = Title::newFromText( $name );
00764                 $title= $title->getSubjectPage();
00765                 self::checkTitle( $title, $name );
00766                 return array(
00767                         'href' => $title->getLocalURL( $urlaction ),
00768                         'exists' => $title->getArticleID() != 0,
00769                 );
00770         }
00771 
00805         protected function buildContentNavigationUrls() {
00806                 global $wgDisableLangConversion;
00807 
00808                 wfProfileIn( __METHOD__ );
00809 
00810                 // Display tabs for the relevant title rather than always the title itself
00811                 $title = $this->getRelevantTitle();
00812                 $onPage = $title->equals( $this->getTitle() );
00813 
00814                 $out = $this->getOutput();
00815                 $request = $this->getRequest();
00816                 $user = $this->getUser();
00817 
00818                 $content_navigation = array(
00819                         'namespaces' => array(),
00820                         'views' => array(),
00821                         'actions' => array(),
00822                         'variants' => array()
00823                 );
00824 
00825                 // parameters
00826                 $action = $request->getVal( 'action', 'view' );
00827 
00828                 $userCanRead = $title->quickUserCan( 'read', $user );
00829 
00830                 $preventActiveTabs = false;
00831                 wfRunHooks( 'SkinTemplatePreventOtherActiveTabs', array( &$this, &$preventActiveTabs ) );
00832 
00833                 // Checks if page is some kind of content
00834                 if( $title->canExist() ) {
00835                         // Gets page objects for the related namespaces
00836                         $subjectPage = $title->getSubjectPage();
00837                         $talkPage = $title->getTalkPage();
00838 
00839                         // Determines if this is a talk page
00840                         $isTalk = $title->isTalkPage();
00841 
00842                         // Generates XML IDs from namespace names
00843                         $subjectId = $title->getNamespaceKey( '' );
00844 
00845                         if ( $subjectId == 'main' ) {
00846                                 $talkId = 'talk';
00847                         } else {
00848                                 $talkId = "{$subjectId}_talk";
00849                         }
00850 
00851                         $skname = $this->skinname;
00852 
00853                         // Adds namespace links
00854                         $subjectMsg = array( "nstab-$subjectId" );
00855                         if ( $subjectPage->isMainPage() ) {
00856                                 array_unshift( $subjectMsg, 'mainpage-nstab' );
00857                         }
00858                         $content_navigation['namespaces'][$subjectId] = $this->tabAction(
00859                                 $subjectPage, $subjectMsg, !$isTalk && !$preventActiveTabs, '', $userCanRead
00860                         );
00861                         $content_navigation['namespaces'][$subjectId]['context'] = 'subject';
00862                         $content_navigation['namespaces'][$talkId] = $this->tabAction(
00863                                 $talkPage, array( "nstab-$talkId", 'talk' ), $isTalk && !$preventActiveTabs, '', $userCanRead
00864                         );
00865                         $content_navigation['namespaces'][$talkId]['context'] = 'talk';
00866 
00867                         if ( $userCanRead ) {
00868                                 // Adds view view link
00869                                 if ( $title->exists() ) {
00870                                         $content_navigation['views']['view'] = $this->tabAction(
00871                                                 $isTalk ? $talkPage : $subjectPage,
00872                                                 array( "$skname-view-view", 'view' ),
00873                                                 ( $onPage && ( $action == 'view' || $action == 'purge' ) ), '', true
00874                                         );
00875                                         // signal to hide this from simple content_actions
00876                                         $content_navigation['views']['view']['redundant'] = true;
00877                                 }
00878 
00879                                 wfProfileIn( __METHOD__ . '-edit' );
00880 
00881                                 // Checks if user can edit the current page if it exists or create it otherwise
00882                                 if ( $title->quickUserCan( 'edit', $user ) && ( $title->exists() || $title->quickUserCan( 'create', $user ) ) ) {
00883                                         // Builds CSS class for talk page links
00884                                         $isTalkClass = $isTalk ? ' istalk' : '';
00885                                         // Whether the user is editing the page
00886                                         $isEditing = $onPage && ( $action == 'edit' || $action == 'submit' );
00887                                         // Whether to show the "Add a new section" tab
00888                                         // Checks if this is a current rev of talk page and is not forced to be hidden
00889                                         $showNewSection = !$out->forceHideNewSectionLink()
00890                                                 && ( ( $isTalk && $this->isRevisionCurrent() ) || $out->showNewSectionLink() );
00891                                         $section = $request->getVal( 'section' );
00892 
00893                                         $msgKey = $title->exists() || ( $title->getNamespace() == NS_MEDIAWIKI && $title->getDefaultMessageText() !== false ) ?
00894                                                 'edit' : 'create';
00895                                         $content_navigation['views']['edit'] = array(
00896                                                 'class' => ( $isEditing && ( $section !== 'new' || !$showNewSection ) ? 'selected' : '' ) . $isTalkClass,
00897                                                 'text' => wfMessageFallback( "$skname-view-$msgKey", $msgKey )->setContext( $this->getContext() )->text(),
00898                                                 'href' => $title->getLocalURL( $this->editUrlOptions() ),
00899                                                 'primary' => true, // don't collapse this in vector
00900                                         );
00901 
00902                                         // section link
00903                                         if ( $showNewSection ) {
00904                                                 // Adds new section link
00905                                                 //$content_navigation['actions']['addsection']
00906                                                 $content_navigation['views']['addsection'] = array(
00907                                                         'class' => ( $isEditing && $section == 'new' ) ? 'selected' : false,
00908                                                         'text' => wfMessageFallback( "$skname-action-addsection", 'addsection' )->setContext( $this->getContext() )->text(),
00909                                                         'href' => $title->getLocalURL( 'action=edit&section=new' )
00910                                                 );
00911                                         }
00912                                 // Checks if the page has some kind of viewable content
00913                                 } elseif ( $title->hasSourceText() ) {
00914                                         // Adds view source view link
00915                                         $content_navigation['views']['viewsource'] = array(
00916                                                 'class' => ( $onPage && $action == 'edit' ) ? 'selected' : false,
00917                                                 'text' => wfMessageFallback( "$skname-action-viewsource", 'viewsource' )->setContext( $this->getContext() )->text(),
00918                                                 'href' => $title->getLocalURL( $this->editUrlOptions() ),
00919                                                 'primary' => true, // don't collapse this in vector
00920                                         );
00921                                 }
00922                                 wfProfileOut( __METHOD__ . '-edit' );
00923 
00924                                 wfProfileIn( __METHOD__ . '-live' );
00925                                 // Checks if the page exists
00926                                 if ( $title->exists() ) {
00927                                         // Adds history view link
00928                                         $content_navigation['views']['history'] = array(
00929                                                 'class' => ( $onPage && $action == 'history' ) ? 'selected' : false,
00930                                                 'text' => wfMessageFallback( "$skname-view-history", 'history_short' )->setContext( $this->getContext() )->text(),
00931                                                 'href' => $title->getLocalURL( 'action=history' ),
00932                                                 'rel' => 'archives',
00933                                         );
00934 
00935                                         if ( $title->quickUserCan( 'delete', $user ) ) {
00936                                                 $content_navigation['actions']['delete'] = array(
00937                                                         'class' => ( $onPage && $action == 'delete' ) ? 'selected' : false,
00938                                                         'text' => wfMessageFallback( "$skname-action-delete", 'delete' )->setContext( $this->getContext() )->text(),
00939                                                         'href' => $title->getLocalURL( 'action=delete' )
00940                                                 );
00941                                         }
00942 
00943                                         if ( $title->quickUserCan( 'move', $user ) ) {
00944                                                 $moveTitle = SpecialPage::getTitleFor( 'Movepage', $title->getPrefixedDBkey() );
00945                                                 $content_navigation['actions']['move'] = array(
00946                                                         'class' => $this->getTitle()->isSpecial( 'Movepage' ) ? 'selected' : false,
00947                                                         'text' => wfMessageFallback( "$skname-action-move", 'move' )->setContext( $this->getContext() )->text(),
00948                                                         'href' => $moveTitle->getLocalURL()
00949                                                 );
00950                                         }
00951                                 } else {
00952                                         // article doesn't exist or is deleted
00953                                         if ( $user->isAllowed( 'deletedhistory' ) ) {
00954                                                 $n = $title->isDeleted();
00955                                                 if ( $n ) {
00956                                                         $undelTitle = SpecialPage::getTitleFor( 'Undelete' );
00957                                                         // If the user can't undelete but can view deleted history show them a "View .. deleted" tab instead
00958                                                         $msgKey = $user->isAllowed( 'undelete' ) ? 'undelete' : 'viewdeleted';
00959                                                         $content_navigation['actions']['undelete'] = array(
00960                                                                 'class' => $this->getTitle()->isSpecial( 'Undelete' ) ? 'selected' : false,
00961                                                                 'text' => wfMessageFallback( "$skname-action-$msgKey", "{$msgKey}_short" )
00962                                                                         ->setContext( $this->getContext() )->numParams( $n )->text(),
00963                                                                 'href' => $undelTitle->getLocalURL( array( 'target' => $title->getPrefixedDBkey() ) )
00964                                                         );
00965                                                 }
00966                                         }
00967                                 }
00968 
00969                                 if ( $title->getNamespace() !== NS_MEDIAWIKI && $title->quickUserCan( 'protect', $user ) && $title->getRestrictionTypes() ) {
00970                                         $mode = $title->isProtected() ? 'unprotect' : 'protect';
00971                                         $content_navigation['actions'][$mode] = array(
00972                                                 'class' => ( $onPage && $action == $mode ) ? 'selected' : false,
00973                                                 'text' => wfMessageFallback( "$skname-action-$mode", $mode )->setContext( $this->getContext() )->text(),
00974                                                 'href' => $title->getLocalURL( "action=$mode" )
00975                                         );
00976                                 }
00977 
00978                                 wfProfileOut( __METHOD__ . '-live' );
00979 
00980                                 // Checks if the user is logged in
00981                                 if ( $this->loggedin ) {
00991                                         $mode = $user->isWatched( $title ) ? 'unwatch' : 'watch';
00992                                         $token = WatchAction::getWatchToken( $title, $user, $mode );
00993                                         $content_navigation['actions'][$mode] = array(
00994                                                 'class' => $onPage && ( $action == 'watch' || $action == 'unwatch' ) ? 'selected' : false,
00995                                                 // uses 'watch' or 'unwatch' message
00996                                                 'text' => $this->msg( $mode )->text(),
00997                                                 'href' => $title->getLocalURL( array( 'action' => $mode, 'token' => $token ) )
00998                                         );
00999                                 }
01000                         }
01001 
01002                         wfRunHooks( 'SkinTemplateNavigation', array( &$this, &$content_navigation ) );
01003 
01004                         if ( $userCanRead && !$wgDisableLangConversion ) {
01005                                 $pageLang = $title->getPageLanguage();
01006                                 // Gets list of language variants
01007                                 $variants = $pageLang->getVariants();
01008                                 // Checks that language conversion is enabled and variants exist
01009                                 // And if it is not in the special namespace
01010                                 if ( count( $variants ) > 1 ) {
01011                                         // Gets preferred variant (note that user preference is
01012                                         // only possible for wiki content language variant)
01013                                         $preferred = $pageLang->getPreferredVariant();
01014                                         if ( Action::getActionName( $this ) === 'view' ) {
01015                                                 $params = $request->getQueryValues();
01016                                                 unset( $params['title'] );
01017                                         } else {
01018                                                 $params = array();
01019                                         }
01020                                         // Loops over each variant
01021                                         foreach( $variants as $code ) {
01022                                                 // Gets variant name from language code
01023                                                 $varname = $pageLang->getVariantname( $code );
01024                                                 // Checks if the variant is marked as disabled
01025                                                 if( $varname == 'disable' ) {
01026                                                         // Skips this variant
01027                                                         continue;
01028                                                 }
01029                                                 // Appends variant link
01030                                                 $content_navigation['variants'][] = array(
01031                                                         'class' => ( $code == $preferred ) ? 'selected' : false,
01032                                                         'text' => $varname,
01033                                                         'href' => $title->getLocalURL( array( 'variant' => $code ) + $params ),
01034                                                         'lang' => $code,
01035                                                         'hreflang' => $code
01036                                                 );
01037                                         }
01038                                 }
01039                         }
01040                 } else {
01041                         // If it's not content, it's got to be a special page
01042                         $content_navigation['namespaces']['special'] = array(
01043                                 'class' => 'selected',
01044                                 'text' => $this->msg( 'nstab-special' )->text(),
01045                                 'href' => $request->getRequestURL(), // @see: bug 2457, bug 2510
01046                                 'context' => 'subject'
01047                         );
01048 
01049                         wfRunHooks( 'SkinTemplateNavigation::SpecialPage',
01050                                 array( &$this, &$content_navigation ) );
01051                 }
01052 
01053                 // Equiv to SkinTemplateContentActions
01054                 wfRunHooks( 'SkinTemplateNavigation::Universal', array( &$this, &$content_navigation ) );
01055 
01056                 // Setup xml ids and tooltip info
01057                 foreach ( $content_navigation as $section => &$links ) {
01058                         foreach ( $links as $key => &$link ) {
01059                                 $xmlID = $key;
01060                                 if ( isset( $link['context'] ) && $link['context'] == 'subject' ) {
01061                                         $xmlID = 'ca-nstab-' . $xmlID;
01062                                 } elseif ( isset( $link['context'] ) && $link['context'] == 'talk' ) {
01063                                         $xmlID = 'ca-talk';
01064                                 } elseif ( $section == 'variants' ) {
01065                                         $xmlID = 'ca-varlang-' . $xmlID;
01066                                 } else {
01067                                         $xmlID = 'ca-' . $xmlID;
01068                                 }
01069                                 $link['id'] = $xmlID;
01070                         }
01071                 }
01072 
01073                 # We don't want to give the watch tab an accesskey if the
01074                 # page is being edited, because that conflicts with the
01075                 # accesskey on the watch checkbox.  We also don't want to
01076                 # give the edit tab an accesskey, because that's fairly
01077                 # superfluous and conflicts with an accesskey (Ctrl-E) often
01078                 # used for editing in Safari.
01079                 if ( in_array( $action, array( 'edit', 'submit' ) ) ) {
01080                         if ( isset( $content_navigation['views']['edit'] ) ) {
01081                                 $content_navigation['views']['edit']['tooltiponly'] = true;
01082                         }
01083                         if ( isset( $content_navigation['actions']['watch'] ) ) {
01084                                 $content_navigation['actions']['watch']['tooltiponly'] = true;
01085                         }
01086                         if ( isset( $content_navigation['actions']['unwatch'] ) ) {
01087                                 $content_navigation['actions']['unwatch']['tooltiponly'] = true;
01088                         }
01089                 }
01090 
01091                 wfProfileOut( __METHOD__ );
01092 
01093                 return $content_navigation;
01094         }
01095 
01101         function buildContentActionUrls( $content_navigation ) {
01102 
01103                 wfProfileIn( __METHOD__ );
01104 
01105                 // content_actions has been replaced with content_navigation for backwards
01106                 // compatibility and also for skins that just want simple tabs content_actions
01107                 // is now built by flattening the content_navigation arrays into one
01108 
01109                 $content_actions = array();
01110 
01111                 foreach ( $content_navigation as $links ) {
01112 
01113                         foreach ( $links as $key => $value ) {
01114 
01115                                 if ( isset( $value['redundant'] ) && $value['redundant'] ) {
01116                                         // Redundant tabs are dropped from content_actions
01117                                         continue;
01118                                 }
01119 
01120                                 // content_actions used to have ids built using the "ca-$key" pattern
01121                                 // so the xmlID based id is much closer to the actual $key that we want
01122                                 // for that reason we'll just strip out the ca- if present and use
01123                                 // the latter potion of the "id" as the $key
01124                                 if ( isset( $value['id'] ) && substr( $value['id'], 0, 3 ) == 'ca-' ) {
01125                                         $key = substr( $value['id'], 3 );
01126                                 }
01127 
01128                                 if ( isset( $content_actions[$key] ) ) {
01129                                         wfDebug( __METHOD__ . ": Found a duplicate key for $key while flattening content_navigation into content_actions." );
01130                                         continue;
01131                                 }
01132 
01133                                 $content_actions[$key] = $value;
01134 
01135                         }
01136 
01137                 }
01138 
01139                 wfProfileOut( __METHOD__ );
01140 
01141                 return $content_actions;
01142         }
01143 
01149         protected function buildNavUrls() {
01150                 global $wgUploadNavigationUrl;
01151 
01152                 wfProfileIn( __METHOD__ );
01153 
01154                 $out = $this->getOutput();
01155                 $request = $this->getRequest();
01156 
01157                 $nav_urls = array();
01158                 $nav_urls['mainpage'] = array( 'href' => self::makeMainPageUrl() );
01159                 if( $wgUploadNavigationUrl ) {
01160                         $nav_urls['upload'] = array( 'href' => $wgUploadNavigationUrl );
01161                 } elseif( UploadBase::isEnabled() && UploadBase::isAllowed( $this->getUser() ) === true ) {
01162                         $nav_urls['upload'] = array( 'href' => self::makeSpecialUrl( 'Upload' ) );
01163                 } else {
01164                         $nav_urls['upload'] = false;
01165                 }
01166                 $nav_urls['specialpages'] = array( 'href' => self::makeSpecialUrl( 'Specialpages' ) );
01167 
01168                 $nav_urls['print'] = false;
01169                 $nav_urls['permalink'] = false;
01170                 $nav_urls['info'] = false;
01171                 $nav_urls['whatlinkshere'] = false;
01172                 $nav_urls['recentchangeslinked'] = false;
01173                 $nav_urls['contributions'] = false;
01174                 $nav_urls['log'] = false;
01175                 $nav_urls['blockip'] = false;
01176                 $nav_urls['emailuser'] = false;
01177                 $nav_urls['userrights'] = false;
01178 
01179                 // A print stylesheet is attached to all pages, but nobody ever
01180                 // figures that out. :)  Add a link...
01181                 if ( $out->isArticle() ) {
01182                         if ( !$out->isPrintable() ) {
01183                                 $nav_urls['print'] = array(
01184                                         'text' => $this->msg( 'printableversion' )->text(),
01185                                         'href' => $this->getTitle()->getLocalURL(
01186                                                 $request->appendQueryValue( 'printable', 'yes', true ) )
01187                                 );
01188                         }
01189 
01190                         // Also add a "permalink" while we're at it
01191                         $revid = $this->getRevisionId();
01192                         if ( $revid ) {
01193                                 $nav_urls['permalink'] = array(
01194                                         'text' => $this->msg( 'permalink' )->text(),
01195                                         'href' => $this->getTitle()->getLocalURL( "oldid=$revid" )
01196                                 );
01197                         }
01198 
01199                         // Use the copy of revision ID in case this undocumented, shady hook tries to mess with internals
01200                         wfRunHooks( 'SkinTemplateBuildNavUrlsNav_urlsAfterPermalink',
01201                                 array( &$this, &$nav_urls, &$revid, &$revid ) );
01202                 }
01203 
01204                 if ( $out->isArticleRelated() ) {
01205                         $nav_urls['whatlinkshere'] = array(
01206                                 'href' => SpecialPage::getTitleFor( 'Whatlinkshere', $this->thispage )->getLocalUrl()
01207                         );
01208 
01209                         $nav_urls['info'] = array(
01210                                 'text' => $this->msg( 'pageinfo-toolboxlink' )->text(),
01211                                 'href' => $this->getTitle()->getLocalURL( "action=info" )
01212                         );
01213 
01214                         if ( $this->getTitle()->getArticleID() ) {
01215                                 $nav_urls['recentchangeslinked'] = array(
01216                                         'href' => SpecialPage::getTitleFor( 'Recentchangeslinked', $this->thispage )->getLocalUrl()
01217                                 );
01218                         }
01219                 }
01220 
01221                 $user = $this->getRelevantUser();
01222                 if ( $user ) {
01223                         $rootUser = $user->getName();
01224 
01225                         $nav_urls['contributions'] = array(
01226                                 'text' => $this->msg( 'contributions', $rootUser )->text(),
01227                                 'href' => self::makeSpecialUrlSubpage( 'Contributions', $rootUser )
01228                         );
01229 
01230                         $nav_urls['log'] = array(
01231                                 'href' => self::makeSpecialUrlSubpage( 'Log', $rootUser )
01232                         );
01233 
01234                         if ( $this->getUser()->isAllowed( 'block' ) ) {
01235                                 $nav_urls['blockip'] = array(
01236                                         'href' => self::makeSpecialUrlSubpage( 'Block', $rootUser )
01237                                 );
01238                         }
01239 
01240                         if ( $this->showEmailUser( $user ) ) {
01241                                 $nav_urls['emailuser'] = array(
01242                                         'href' => self::makeSpecialUrlSubpage( 'Emailuser', $rootUser )
01243                                 );
01244                         }
01245 
01246                         $sur = new UserrightsPage;
01247                         if ( $sur->userCanExecute( $this->getUser() ) ) {
01248                                 $nav_urls['userrights'] = array(
01249                                         'href' => self::makeSpecialUrlSubpage( 'Userrights', $rootUser )
01250                                 );
01251                         }
01252                 }
01253 
01254                 wfProfileOut( __METHOD__ );
01255                 return $nav_urls;
01256         }
01257 
01263         function getNameSpaceKey() {
01264                 return $this->getTitle()->getNamespaceKey();
01265         }
01266 
01267         public function commonPrintStylesheet() {
01268                 return false;
01269         }
01270 }
01271 
01277 abstract class QuickTemplate {
01281         function __construct() {
01282                 $this->data = array();
01283                 $this->translator = new MediaWiki_I18N();
01284         }
01285 
01291         public function set( $name, $value ) {
01292                 $this->data[$name] = $value;
01293         }
01294 
01299         public function setRef( $name, &$value ) {
01300                 $this->data[$name] =& $value;
01301         }
01302 
01306         public function setTranslator( &$t ) {
01307                 $this->translator = &$t;
01308         }
01309 
01314         abstract public function execute();
01315 
01319         function text( $str ) {
01320                 echo htmlspecialchars( $this->data[$str] );
01321         }
01322 
01326         function jstext( $str ) {
01327                 echo Xml::escapeJsString( $this->data[$str] );
01328         }
01329 
01333         function html( $str ) {
01334                 echo $this->data[$str];
01335         }
01336 
01340         function msg( $str ) {
01341                 echo htmlspecialchars( $this->translator->translate( $str ) );
01342         }
01343 
01347         function msgHtml( $str ) {
01348                 echo $this->translator->translate( $str );
01349         }
01350 
01355         function msgWiki( $str ) {
01356                 global $wgOut;
01357 
01358                 $text = $this->translator->translate( $str );
01359                 echo $wgOut->parse( $text );
01360         }
01361 
01366         function haveData( $str ) {
01367                 return isset( $this->data[$str] );
01368         }
01369 
01375         function haveMsg( $str ) {
01376                 $msg = $this->translator->translate( $str );
01377                 return ( $msg != '-' ) && ( $msg != '' ); # ????
01378         }
01379 
01385         public function getSkin() {
01386                 return $this->data['skin'];
01387         }
01388 }
01389 
01395 abstract class BaseTemplate extends QuickTemplate {
01396 
01403         public function getMsg( $name ) {
01404                 return $this->getSkin()->msg( $name );
01405         }
01406 
01407         function msg( $str ) {
01408                 echo $this->getMsg( $str )->escaped();
01409         }
01410 
01411         function msgHtml( $str ) {
01412                 echo $this->getMsg( $str )->text();
01413         }
01414 
01415         function msgWiki( $str ) {
01416                 echo $this->getMsg( $str )->parseAsBlock();
01417         }
01418 
01426         function getToolbox() {
01427                 wfProfileIn( __METHOD__ );
01428 
01429                 $toolbox = array();
01430                 if ( isset( $this->data['nav_urls']['whatlinkshere'] ) && $this->data['nav_urls']['whatlinkshere'] ) {
01431                         $toolbox['whatlinkshere'] = $this->data['nav_urls']['whatlinkshere'];
01432                         $toolbox['whatlinkshere']['id'] = 't-whatlinkshere';
01433                 }
01434                 if ( isset( $this->data['nav_urls']['recentchangeslinked'] ) && $this->data['nav_urls']['recentchangeslinked'] ) {
01435                         $toolbox['recentchangeslinked'] = $this->data['nav_urls']['recentchangeslinked'];
01436                         $toolbox['recentchangeslinked']['msg'] = 'recentchangeslinked-toolbox';
01437                         $toolbox['recentchangeslinked']['id'] = 't-recentchangeslinked';
01438                 }
01439                 if ( isset( $this->data['feeds'] ) && $this->data['feeds'] ) {
01440                         $toolbox['feeds']['id'] = 'feedlinks';
01441                         $toolbox['feeds']['links'] = array();
01442                         foreach ( $this->data['feeds'] as $key => $feed ) {
01443                                 $toolbox['feeds']['links'][$key] = $feed;
01444                                 $toolbox['feeds']['links'][$key]['id'] = "feed-$key";
01445                                 $toolbox['feeds']['links'][$key]['rel'] = 'alternate';
01446                                 $toolbox['feeds']['links'][$key]['type'] = "application/{$key}+xml";
01447                                 $toolbox['feeds']['links'][$key]['class'] = 'feedlink';
01448                         }
01449                 }
01450                 foreach ( array( 'contributions', 'log', 'blockip', 'emailuser', 'userrights', 'upload', 'specialpages' ) as $special ) {
01451                         if ( isset( $this->data['nav_urls'][$special] ) && $this->data['nav_urls'][$special] ) {
01452                                 $toolbox[$special] = $this->data['nav_urls'][$special];
01453                                 $toolbox[$special]['id'] = "t-$special";
01454                         }
01455                 }
01456                 if ( isset( $this->data['nav_urls']['print'] ) && $this->data['nav_urls']['print'] ) {
01457                         $toolbox['print'] = $this->data['nav_urls']['print'];
01458                         $toolbox['print']['id'] = 't-print';
01459                         $toolbox['print']['rel'] = 'alternate';
01460                         $toolbox['print']['msg'] = 'printableversion';
01461                 }
01462                 if ( isset( $this->data['nav_urls']['permalink'] ) && $this->data['nav_urls']['permalink'] ) {
01463                         $toolbox['permalink'] = $this->data['nav_urls']['permalink'];
01464                         if ( $toolbox['permalink']['href'] === '' ) {
01465                                 unset( $toolbox['permalink']['href'] );
01466                                 $toolbox['ispermalink']['tooltiponly'] = true;
01467                                 $toolbox['ispermalink']['id'] = 't-ispermalink';
01468                                 $toolbox['ispermalink']['msg'] = 'permalink';
01469                         } else {
01470                                 $toolbox['permalink']['id'] = 't-permalink';
01471                         }
01472                 }
01473                 if ( isset( $this->data['nav_urls']['info'] ) && $this->data['nav_urls']['info'] ) {
01474                         $toolbox['info'] = $this->data['nav_urls']['info'];
01475                         $toolbox['info']['id'] = 't-info';
01476                 }
01477 
01478                 wfRunHooks( 'BaseTemplateToolbox', array( &$this, &$toolbox ) );
01479                 wfProfileOut( __METHOD__ );
01480                 return $toolbox;
01481         }
01482 
01493         function getPersonalTools() {
01494                 $personal_tools = array();
01495                 foreach ( $this->data['personal_urls'] as $key => $plink ) {
01496                         # The class on a personal_urls item is meant to go on the <a> instead
01497                         # of the <li> so we have to use a single item "links" array instead
01498                         # of using most of the personal_url's keys directly.
01499                         $ptool = array(
01500                                 'links' => array(
01501                                         array( 'single-id' => "pt-$key" ),
01502                                 ),
01503                                 'id' => "pt-$key",
01504                         );
01505                         if ( isset( $plink['active'] ) ) {
01506                                 $ptool['active'] = $plink['active'];
01507                         }
01508                         foreach ( array( 'href', 'class', 'text' ) as $k ) {
01509                                 if ( isset( $plink[$k] ) )
01510                                         $ptool['links'][0][$k] = $plink[$k];
01511                         }
01512                         $personal_tools[$key] = $ptool;
01513                 }
01514                 return $personal_tools;
01515         }
01516 
01517         function getSidebar( $options = array() ) {
01518                 // Force the rendering of the following portals
01519                 $sidebar = $this->data['sidebar'];
01520                 if ( !isset( $sidebar['SEARCH'] ) ) {
01521                         $sidebar['SEARCH'] = true;
01522                 }
01523                 if ( !isset( $sidebar['TOOLBOX'] ) ) {
01524                         $sidebar['TOOLBOX'] = true;
01525                 }
01526                 if ( !isset( $sidebar['LANGUAGES'] ) ) {
01527                         $sidebar['LANGUAGES'] = true;
01528                 }
01529 
01530                 if ( !isset( $options['search'] ) || $options['search'] !== true ) {
01531                         unset( $sidebar['SEARCH'] );
01532                 }
01533                 if ( isset( $options['toolbox'] ) && $options['toolbox'] === false ) {
01534                         unset( $sidebar['TOOLBOX'] );
01535                 }
01536                 if ( isset( $options['languages'] ) && $options['languages'] === false ) {
01537                         unset( $sidebar['LANGUAGES'] );
01538                 }
01539 
01540                 $boxes = array();
01541                 foreach ( $sidebar as $boxName => $content ) {
01542                         if ( $content === false ) {
01543                                 continue;
01544                         }
01545                         switch ( $boxName ) {
01546                         case 'SEARCH':
01547                                 // Search is a special case, skins should custom implement this
01548                                 $boxes[$boxName] = array(
01549                                         'id'        => 'p-search',
01550                                         'header'    => $this->getMsg( 'search' )->text(),
01551                                         'generated' => false,
01552                                         'content'   => true,
01553                                 );
01554                                 break;
01555                         case 'TOOLBOX':
01556                                 $msgObj = $this->getMsg( 'toolbox' );
01557                                 $boxes[$boxName] = array(
01558                                         'id'        => 'p-tb',
01559                                         'header'    => $msgObj->exists() ? $msgObj->text() : 'toolbox',
01560                                         'generated' => false,
01561                                         'content'   => $this->getToolbox(),
01562                                 );
01563                                 break;
01564                         case 'LANGUAGES':
01565                                 if ( $this->data['language_urls'] ) {
01566                                         $msgObj = $this->getMsg( 'otherlanguages' );
01567                                         $boxes[$boxName] = array(
01568                                                 'id'        => 'p-lang',
01569                                                 'header'    => $msgObj->exists() ? $msgObj->text() : 'otherlanguages',
01570                                                 'generated' => false,
01571                                                 'content'   => $this->data['language_urls'],
01572                                         );
01573                                 }
01574                                 break;
01575                         default:
01576                                 $msgObj = $this->getMsg( $boxName );
01577                                 $boxes[$boxName] = array(
01578                                         'id'        => "p-$boxName",
01579                                         'header'    => $msgObj->exists() ? $msgObj->text() : $boxName,
01580                                         'generated' => true,
01581                                         'content'   => $content,
01582                                 );
01583                                 break;
01584                         }
01585                 }
01586 
01587                 // HACK: Compatibility with extensions still using SkinTemplateToolboxEnd
01588                 $hookContents = null;
01589                 if ( isset( $boxes['TOOLBOX'] ) ) {
01590                         ob_start();
01591                         // We pass an extra 'true' at the end so extensions using BaseTemplateToolbox
01592                         // can abort and avoid outputting double toolbox links
01593                         wfRunHooks( 'SkinTemplateToolboxEnd', array( &$this, true ) );
01594                         $hookContents = ob_get_contents();
01595                         ob_end_clean();
01596                         if ( !trim( $hookContents ) ) {
01597                                 $hookContents = null;
01598                         }
01599                 }
01600                 // END hack
01601 
01602                 if ( isset( $options['htmlOnly'] ) && $options['htmlOnly'] === true ) {
01603                         foreach ( $boxes as $boxName => $box ) {
01604                                 if ( is_array( $box['content'] ) ) {
01605                                         $content = '<ul>';
01606                                         foreach ( $box['content'] as $key => $val ) {
01607                                                 $content .= "\n " . $this->makeListItem( $key, $val );
01608                                         }
01609                                         // HACK, shove the toolbox end onto the toolbox if we're rendering itself
01610                                         if ( $hookContents ) {
01611                                                 $content .= "\n $hookContents";
01612                                         }
01613                                         // END hack
01614                                         $content .= "\n</ul>\n";
01615                                         $boxes[$boxName]['content'] = $content;
01616                                 }
01617                         }
01618                 } else {
01619                         if ( $hookContents ) {
01620                                 $boxes['TOOLBOXEND'] = array(
01621                                         'id'        => 'p-toolboxend',
01622                                         'header'    => $boxes['TOOLBOX']['header'],
01623                                         'generated' => false,
01624                                         'content'   => "<ul>{$hookContents}</ul>",
01625                                 );
01626                                 // HACK: Make sure that TOOLBOXEND is sorted next to TOOLBOX
01627                                 $boxes2 = array();
01628                                 foreach ( $boxes as $key => $box ) {
01629                                         if ( $key === 'TOOLBOXEND' ) {
01630                                                 continue;
01631                                         }
01632                                         $boxes2[$key] = $box;
01633                                         if ( $key === 'TOOLBOX' ) {
01634                                                 $boxes2['TOOLBOXEND'] = $boxes['TOOLBOXEND'];
01635                                         }
01636                                 }
01637                                 $boxes = $boxes2;
01638                                 // END hack
01639                         }
01640                 }
01641 
01642                 return $boxes;
01643         }
01644 
01684         function makeLink( $key, $item, $options = array() ) {
01685                 if ( isset( $item['text'] ) ) {
01686                         $text = $item['text'];
01687                 } else {
01688                         $text = $this->translator->translate( isset( $item['msg'] ) ? $item['msg'] : $key );
01689                 }
01690 
01691                 $html = htmlspecialchars( $text );
01692 
01693                 if ( isset( $options['text-wrapper'] ) ) {
01694                         $wrapper = $options['text-wrapper'];
01695                         if ( isset( $wrapper['tag'] ) ) {
01696                                 $wrapper = array( $wrapper );
01697                         }
01698                         while ( count( $wrapper ) > 0 ) {
01699                                 $element = array_pop( $wrapper );
01700                                 $html = Html::rawElement( $element['tag'], isset( $element['attributes'] ) ? $element['attributes'] : null, $html );
01701                         }
01702                 }
01703 
01704                 if ( isset( $item['href'] ) || isset( $options['link-fallback'] ) ) {
01705                         $attrs = $item;
01706                         foreach ( array( 'single-id', 'text', 'msg', 'tooltiponly' ) as $k ) {
01707                                 unset( $attrs[$k] );
01708                         }
01709 
01710                         if ( isset( $item['id'] ) && !isset( $item['single-id'] ) ) {
01711                                 $item['single-id'] = $item['id'];
01712                         }
01713                         if ( isset( $item['single-id'] ) ) {
01714                                 if ( isset( $item['tooltiponly'] ) && $item['tooltiponly'] ) {
01715                                         $title = Linker::titleAttrib( $item['single-id'] );
01716                                         if ( $title !== false ) {
01717                                                 $attrs['title'] = $title;
01718                                         }
01719                                 } else {
01720                                         $tip = Linker::tooltipAndAccesskeyAttribs( $item['single-id'] );
01721                                         if ( isset( $tip['title'] ) && $tip['title'] !== false ) {
01722                                                 $attrs['title'] = $tip['title'];
01723                                         }
01724                                         if ( isset( $tip['accesskey'] ) && $tip['accesskey'] !== false ) {
01725                                                 $attrs['accesskey'] = $tip['accesskey'];
01726                                         }
01727                                 }
01728                         }
01729                         if ( isset( $options['link-class'] ) ) {
01730                                 if ( isset( $attrs['class'] ) ) {
01731                                         $attrs['class'] .= " {$options['link-class']}";
01732                                 } else {
01733                                         $attrs['class'] = $options['link-class'];
01734                                 }
01735                         }
01736                         $html = Html::rawElement( isset( $attrs['href'] ) ? 'a' : $options['link-fallback'], $attrs, $html );
01737                 }
01738 
01739                 return $html;
01740         }
01741 
01769         function makeListItem( $key, $item, $options = array() ) {
01770                 if ( isset( $item['links'] ) ) {
01771                         $links = array();
01772                         foreach ( $item['links'] as $linkKey => $link ) {
01773                                 $links[] = $this->makeLink( $linkKey, $link, $options );
01774                         }
01775                         $html = implode( ' ', $links );
01776                 } else {
01777                         $link = $item;
01778                         // These keys are used by makeListItem and shouldn't be passed on to the link
01779                         foreach ( array( 'id', 'class', 'active', 'tag' ) as $k ) {
01780                                 unset( $link[$k] );
01781                         }
01782                         if ( isset( $item['id'] ) && !isset( $item['single-id'] ) ) {
01783                                 // The id goes on the <li> not on the <a> for single links
01784                                 // but makeSidebarLink still needs to know what id to use when
01785                                 // generating tooltips and accesskeys.
01786                                 $link['single-id'] = $item['id'];
01787                         }
01788                         $html = $this->makeLink( $key, $link, $options );
01789                 }
01790 
01791                 $attrs = array();
01792                 foreach ( array( 'id', 'class' ) as $attr ) {
01793                         if ( isset( $item[$attr] ) ) {
01794                                 $attrs[$attr] = $item[$attr];
01795                         }
01796                 }
01797                 if ( isset( $item['active'] ) && $item['active'] ) {
01798                         if ( !isset( $attrs['class'] ) ) {
01799                                 $attrs['class'] = '';
01800                         }
01801                         $attrs['class'] .= ' active';
01802                         $attrs['class'] = trim( $attrs['class'] );
01803                 }
01804                 return Html::rawElement( isset( $options['tag'] ) ? $options['tag'] : 'li', $attrs, $html );
01805         }
01806 
01807         function makeSearchInput( $attrs = array() ) {
01808                 $realAttrs = array(
01809                         'type' => 'search',
01810                         'name' => 'search',
01811                         'value' => isset( $this->data['search'] ) ? $this->data['search'] : '',
01812                 );
01813                 $realAttrs = array_merge( $realAttrs, Linker::tooltipAndAccesskeyAttribs( 'search' ), $attrs );
01814                 return Html::element( 'input', $realAttrs );
01815         }
01816 
01817         function makeSearchButton( $mode, $attrs = array() ) {
01818                 switch( $mode ) {
01819                         case 'go':
01820                         case 'fulltext':
01821                                 $realAttrs = array(
01822                                         'type' => 'submit',
01823                                         'name' => $mode,
01824                                         'value' => $this->translator->translate(
01825                                                 $mode == 'go' ? 'searcharticle' : 'searchbutton' ),
01826                                 );
01827                                 $realAttrs = array_merge(
01828                                         $realAttrs,
01829                                         Linker::tooltipAndAccesskeyAttribs( "search-$mode" ),
01830                                         $attrs
01831                                 );
01832                                 return Html::element( 'input', $realAttrs );
01833                         case 'image':
01834                                 $buttonAttrs = array(
01835                                         'type' => 'submit',
01836                                         'name' => 'button',
01837                                 );
01838                                 $buttonAttrs = array_merge(
01839                                         $buttonAttrs,
01840                                         Linker::tooltipAndAccesskeyAttribs( 'search-fulltext' ),
01841                                         $attrs
01842                                 );
01843                                 unset( $buttonAttrs['src'] );
01844                                 unset( $buttonAttrs['alt'] );
01845                                 unset( $buttonAttrs['width'] );
01846                                 unset( $buttonAttrs['height'] );
01847                                 $imgAttrs = array(
01848                                         'src' => $attrs['src'],
01849                                         'alt' => isset( $attrs['alt'] )
01850                                                 ? $attrs['alt']
01851                                                 : $this->translator->translate( 'searchbutton' ),
01852                                         'width' => isset( $attrs['width'] ) ? $attrs['width'] : null,
01853                                         'height' => isset( $attrs['height'] ) ? $attrs['height'] : null,
01854                                 );
01855                                 return Html::rawElement( 'button', $buttonAttrs, Html::element( 'img', $imgAttrs ) );
01856                         default:
01857                                 throw new MWException( 'Unknown mode passed to BaseTemplate::makeSearchButton' );
01858                 }
01859         }
01860 
01869         function getFooterLinks( $option = null ) {
01870                 $footerlinks = $this->data['footerlinks'];
01871 
01872                 // Reduce footer links down to only those which are being used
01873                 $validFooterLinks = array();
01874                 foreach( $footerlinks as $category => $links ) {
01875                         $validFooterLinks[$category] = array();
01876                         foreach( $links as $link ) {
01877                                 if( isset( $this->data[$link] ) && $this->data[$link] ) {
01878                                         $validFooterLinks[$category][] = $link;
01879                                 }
01880                         }
01881                         if ( count( $validFooterLinks[$category] ) <= 0 ) {
01882                                 unset( $validFooterLinks[$category] );
01883                         }
01884                 }
01885 
01886                 if ( $option == 'flat' ) {
01887                         // fold footerlinks into a single array using a bit of trickery
01888                         $validFooterLinks = call_user_func_array(
01889                                 'array_merge',
01890                                 array_values( $validFooterLinks )
01891                         );
01892                 }
01893 
01894                 return $validFooterLinks;
01895         }
01896 
01908         function getFooterIcons( $option = null ) {
01909                 // Generate additional footer icons
01910                 $footericons = $this->data['footericons'];
01911 
01912                 if ( $option == 'icononly' ) {
01913                         // Unset any icons which don't have an image
01914                         foreach ( $footericons as &$footerIconsBlock ) {
01915                                 foreach ( $footerIconsBlock as $footerIconKey => $footerIcon ) {
01916                                         if ( !is_string( $footerIcon ) && !isset( $footerIcon['src'] ) ) {
01917                                                 unset( $footerIconsBlock[$footerIconKey] );
01918                                         }
01919                                 }
01920                         }
01921                         // Redo removal of any empty blocks
01922                         foreach ( $footericons as $footerIconsKey => &$footerIconsBlock ) {
01923                                 if ( count( $footerIconsBlock ) <= 0 ) {
01924                                         unset( $footericons[$footerIconsKey] );
01925                                 }
01926                         }
01927                 } elseif ( $option == 'nocopyright' ) {
01928                         unset( $footericons['copyright']['copyright'] );
01929                         if ( count( $footericons['copyright'] ) <= 0 ) {
01930                                 unset( $footericons['copyright'] );
01931                         }
01932                 }
01933 
01934                 return $footericons;
01935         }
01936 
01942         function printTrail() { ?>
01943 <?php $this->html( 'bottomscripts' ); /* JS call to runBodyOnloadHook */ ?>
01944 <?php $this->html( 'reporttime' ) ?>
01945 <?php echo MWDebug::getDebugHTML( $this->getSkin()->getContext() );
01946         }
01947 
01948 }