MediaWiki  REL1_19
Skin.php
Go to the documentation of this file.
00001 <?php
00006 if ( !defined( 'MEDIAWIKI' ) ) {
00007         die( 1 );
00008 }
00009 
00018 abstract class Skin extends ContextSource {
00019         protected $skinname = 'standard';
00020         protected $mRelevantTitle = null;
00021         protected $mRelevantUser = null;
00022 
00027         static function getSkinNames() {
00028                 global $wgValidSkinNames;
00029                 static $skinsInitialised = false;
00030 
00031                 if ( !$skinsInitialised || !count( $wgValidSkinNames ) ) {
00032                         # Get a list of available skins
00033                         # Build using the regular expression '^(.*).php$'
00034                         # Array keys are all lower case, array value keep the case used by filename
00035                         #
00036                         wfProfileIn( __METHOD__ . '-init' );
00037 
00038                         global $wgStyleDirectory;
00039 
00040                         $skinDir = dir( $wgStyleDirectory );
00041 
00042                         # while code from www.php.net
00043                         while ( false !== ( $file = $skinDir->read() ) ) {
00044                                 // Skip non-PHP files, hidden files, and '.dep' includes
00045                                 $matches = array();
00046 
00047                                 if ( preg_match( '/^([^.]*)\.php$/', $file, $matches ) ) {
00048                                         $aSkin = $matches[1];
00049                                         $wgValidSkinNames[strtolower( $aSkin )] = $aSkin;
00050                                 }
00051                         }
00052                         $skinDir->close();
00053                         $skinsInitialised = true;
00054                         wfProfileOut( __METHOD__ . '-init' );
00055                 }
00056                 return $wgValidSkinNames;
00057         }
00058  
00063         static function getSkinNameMessages() {
00064                 $messages = array();
00065                 foreach( self::getSkinNames() as $skinKey => $skinName ) {
00066                         $messages[] = "skinname-$skinKey";
00067                 }
00068                 return $messages;
00069         }
00070 
00077         public static function getUsableSkins() {
00078                 global $wgSkipSkins;
00079 
00080                 $usableSkins = self::getSkinNames();
00081 
00082                 foreach ( $wgSkipSkins as $skip ) {
00083                         unset( $usableSkins[$skip] );
00084                 }
00085 
00086                 return $usableSkins;
00087         }
00088 
00096         static function normalizeKey( $key ) {
00097                 global $wgDefaultSkin;
00098 
00099                 $skinNames = Skin::getSkinNames();
00100 
00101                 if ( $key == '' ) {
00102                         // Don't return the default immediately;
00103                         // in a misconfiguration we need to fall back.
00104                         $key = $wgDefaultSkin;
00105                 }
00106 
00107                 if ( isset( $skinNames[$key] ) ) {
00108                         return $key;
00109                 }
00110 
00111                 // Older versions of the software used a numeric setting
00112                 // in the user preferences.
00113                 $fallback = array(
00114                         0 => $wgDefaultSkin,
00115                         1 => 'nostalgia',
00116                         2 => 'cologneblue'
00117                 );
00118 
00119                 if ( isset( $fallback[$key] ) ) {
00120                         $key = $fallback[$key];
00121                 }
00122 
00123                 if ( isset( $skinNames[$key] ) ) {
00124                         return $key;
00125                 } elseif ( isset( $skinNames[$wgDefaultSkin] ) ) {
00126                         return $wgDefaultSkin;
00127                 } else {
00128                         return 'vector';
00129                 }
00130         }
00131 
00137         static function &newFromKey( $key ) {
00138                 global $wgStyleDirectory;
00139 
00140                 $key = Skin::normalizeKey( $key );
00141 
00142                 $skinNames = Skin::getSkinNames();
00143                 $skinName = $skinNames[$key];
00144                 $className = "Skin{$skinName}";
00145 
00146                 # Grab the skin class and initialise it.
00147                 if ( !MWInit::classExists( $className ) ) {
00148 
00149                         if ( !defined( 'MW_COMPILED' ) ) {
00150                                 // Preload base classes to work around APC/PHP5 bug
00151                                 $deps = "{$wgStyleDirectory}/{$skinName}.deps.php";
00152                                 if ( file_exists( $deps ) ) {
00153                                         include_once( $deps );
00154                                 }
00155                                 require_once( "{$wgStyleDirectory}/{$skinName}.php" );
00156                         }
00157 
00158                         # Check if we got if not failback to default skin
00159                         if ( !MWInit::classExists( $className ) ) {
00160                                 # DO NOT die if the class isn't found. This breaks maintenance
00161                                 # scripts and can cause a user account to be unrecoverable
00162                                 # except by SQL manipulation if a previously valid skin name
00163                                 # is no longer valid.
00164                                 wfDebug( "Skin class does not exist: $className\n" );
00165                                 $className = 'SkinVector';
00166                                 if ( !defined( 'MW_COMPILED' ) ) {
00167                                         require_once( "{$wgStyleDirectory}/Vector.php" );
00168                                 }
00169                         }
00170                 }
00171                 $skin = new $className( $key );
00172                 return $skin;
00173         }
00174 
00176         public function getSkinName() {
00177                 return $this->skinname;
00178         }
00179 
00183         function initPage( OutputPage $out ) {
00184                 wfProfileIn( __METHOD__ );
00185 
00186                 $this->preloadExistence();
00187 
00188                 wfProfileOut( __METHOD__ );
00189         }
00190 
00194         function preloadExistence() {
00195                 $user = $this->getUser();
00196 
00197                 // User/talk link
00198                 $titles = array( $user->getUserPage(), $user->getTalkPage() );
00199 
00200                 // Other tab link
00201                 if ( $this->getTitle()->isSpecialPage() ) {
00202                         // nothing
00203                 } elseif ( $this->getTitle()->isTalkPage() ) {
00204                         $titles[] = $this->getTitle()->getSubjectPage();
00205                 } else {
00206                         $titles[] = $this->getTitle()->getTalkPage();
00207                 }
00208 
00209                 $lb = new LinkBatch( $titles );
00210                 $lb->setCaller( __METHOD__ );
00211                 $lb->execute();
00212         }
00213 
00219         public function getRevisionId() {
00220                 return $this->getOutput()->getRevisionId();
00221         }
00222 
00228         public function isRevisionCurrent() {
00229                 $revID = $this->getRevisionId();
00230                 return $revID == 0 || $revID == $this->getTitle()->getLatestRevID();
00231         }
00232 
00238         public function setRelevantTitle( $t ) {
00239                 $this->mRelevantTitle = $t;
00240         }
00241 
00252         public function getRelevantTitle() {
00253                 if ( isset($this->mRelevantTitle) ) {
00254                         return $this->mRelevantTitle;
00255                 }
00256                 return $this->getTitle();
00257         }
00258 
00264         public function setRelevantUser( $u ) {
00265                 $this->mRelevantUser = $u;
00266         }
00267 
00276         public function getRelevantUser() {
00277                 if ( isset($this->mRelevantUser) ) {
00278                         return $this->mRelevantUser;
00279                 }
00280                 $title = $this->getRelevantTitle();
00281                 if( $title->getNamespace() == NS_USER || $title->getNamespace() == NS_USER_TALK ) {
00282                         $rootUser = strtok( $title->getText(), '/' );
00283                         if ( User::isIP( $rootUser ) ) {
00284                                 $this->mRelevantUser = User::newFromName( $rootUser, false );
00285                         } else {
00286                                 $user = User::newFromName( $rootUser, false );
00287                                 if ( $user && $user->isLoggedIn() ) {
00288                                         $this->mRelevantUser = $user;
00289                                 }
00290                         }
00291                         return $this->mRelevantUser;
00292                 }
00293                 return null;
00294         }
00295 
00300         abstract function outputPage( OutputPage $out = null );
00301 
00306         static function makeVariablesScript( $data ) {
00307                 if ( $data ) {
00308                         return Html::inlineScript(
00309                                 ResourceLoader::makeLoaderConditionalScript( ResourceLoader::makeConfigSetScript( $data ) )
00310                         );
00311                 } else {
00312                         return '';
00313                 }
00314         }
00315 
00323         public static function makeGlobalVariablesScript( $unused ) {
00324                 global $wgOut;
00325 
00326                 wfDeprecated( __METHOD__, '1.19' );
00327 
00328                 return self::makeVariablesScript( $wgOut->getJSVars() );
00329         }
00330 
00336         public static function getDynamicStylesheetQuery() {
00337                 global $wgSquidMaxage;
00338 
00339                 return array(
00340                                 'action' => 'raw',
00341                                 'maxage' => $wgSquidMaxage,
00342                                 'usemsgcache' => 'yes',
00343                                 'ctype' => 'text/css',
00344                                 'smaxage' => $wgSquidMaxage,
00345                         );
00346         }
00347 
00356         abstract function setupSkinUserCss( OutputPage $out );
00357 
00363         function getPageClasses( $title ) {
00364                 $numeric = 'ns-' . $title->getNamespace();
00365 
00366                 if ( $title->isSpecialPage() ) {
00367                         $type = 'ns-special';
00368                         // bug 23315: provide a class based on the canonical special page name without subpages
00369                         list( $canonicalName ) = SpecialPageFactory::resolveAlias( $title->getDBkey() );
00370                         if ( $canonicalName ) {
00371                                 $type .= ' ' . Sanitizer::escapeClass( "mw-special-$canonicalName" );
00372                         } else {
00373                                 $type .= ' mw-invalidspecialpage';
00374                         }
00375                 } elseif ( $title->isTalkPage() ) {
00376                         $type = 'ns-talk';
00377                 } else {
00378                         $type = 'ns-subject';
00379                 }
00380 
00381                 $name = Sanitizer::escapeClass( 'page-' . $title->getPrefixedText() );
00382 
00383                 return "$numeric $type $name";
00384         }
00385 
00393         function addToBodyAttributes( $out, &$bodyAttrs ) {
00394                 // does nothing by default
00395         }
00396 
00401         function getLogo() {
00402                 global $wgLogo;
00403                 return $wgLogo;
00404         }
00405 
00409         function getCategoryLinks() {
00410                 global $wgUseCategoryBrowser;
00411 
00412                 $out = $this->getOutput();
00413                 $allCats = $out->getCategoryLinks();
00414 
00415                 if ( !count( $allCats ) ) {
00416                         return '';
00417                 }
00418 
00419                 $embed = "<li>";
00420                 $pop = "</li>";
00421 
00422                 $s = '';
00423                 $colon = $this->msg( 'colon-separator' )->escaped();
00424 
00425                 if ( !empty( $allCats['normal'] ) ) {
00426                         $t = $embed . implode( "{$pop}{$embed}" , $allCats['normal'] ) . $pop;
00427 
00428                         $msg = $this->msg( 'pagecategories', count( $allCats['normal'] ) )->escaped();
00429                         $linkPage = wfMessage( 'pagecategorieslink' )->inContentLanguage()->text();
00430                         $s .= '<div id="mw-normal-catlinks" class="mw-normal-catlinks">' .
00431                                 Linker::link( Title::newFromText( $linkPage ), $msg )
00432                                 . $colon . '<ul>' . $t . '</ul>' . '</div>';
00433                 }
00434 
00435                 # Hidden categories
00436                 if ( isset( $allCats['hidden'] ) ) {
00437                         if ( $this->getUser()->getBoolOption( 'showhiddencats' ) ) {
00438                                 $class = ' mw-hidden-cats-user-shown';
00439                         } elseif ( $this->getTitle()->getNamespace() == NS_CATEGORY ) {
00440                                 $class = ' mw-hidden-cats-ns-shown';
00441                         } else {
00442                                 $class = ' mw-hidden-cats-hidden';
00443                         }
00444 
00445                         $s .= "<div id=\"mw-hidden-catlinks\" class=\"mw-hidden-catlinks$class\">" .
00446                                 $this->msg( 'hidden-categories', count( $allCats['hidden'] ) )->escaped() .
00447                                 $colon . '<ul>' . $embed . implode( "{$pop}{$embed}" , $allCats['hidden'] ) . $pop . '</ul>' .
00448                                 '</div>';
00449                 }
00450 
00451                 # optional 'dmoz-like' category browser. Will be shown under the list
00452                 # of categories an article belong to
00453                 if ( $wgUseCategoryBrowser ) {
00454                         $s .= '<br /><hr />';
00455 
00456                         # get a big array of the parents tree
00457                         $parenttree = $this->getTitle()->getParentCategoryTree();
00458                         # Skin object passed by reference cause it can not be
00459                         # accessed under the method subfunction drawCategoryBrowser
00460                         $tempout = explode( "\n", $this->drawCategoryBrowser( $parenttree ) );
00461                         # Clean out bogus first entry and sort them
00462                         unset( $tempout[0] );
00463                         asort( $tempout );
00464                         # Output one per line
00465                         $s .= implode( "<br />\n", $tempout );
00466                 }
00467 
00468                 return $s;
00469         }
00470 
00476         function drawCategoryBrowser( $tree ) {
00477                 $return = '';
00478 
00479                 foreach ( $tree as $element => $parent ) {
00480                         if ( empty( $parent ) ) {
00481                                 # element start a new list
00482                                 $return .= "\n";
00483                         } else {
00484                                 # grab the others elements
00485                                 $return .= $this->drawCategoryBrowser( $parent ) . ' &gt; ';
00486                         }
00487 
00488                         # add our current element to the list
00489                         $eltitle = Title::newFromText( $element );
00490                         $return .=  Linker::link( $eltitle, htmlspecialchars( $eltitle->getText() ) );
00491                 }
00492 
00493                 return $return;
00494         }
00495 
00499         function getCategories() {
00500                 $out = $this->getOutput();
00501 
00502                 $catlinks = $this->getCategoryLinks();
00503 
00504                 $classes = 'catlinks';
00505 
00506                 // Check what we're showing
00507                 $allCats = $out->getCategoryLinks();
00508                 $showHidden = $this->getUser()->getBoolOption( 'showhiddencats' ) ||
00509                                                 $this->getTitle()->getNamespace() == NS_CATEGORY;
00510 
00511                 if ( empty( $allCats['normal'] ) && !( !empty( $allCats['hidden'] ) && $showHidden ) ) {
00512                         $classes .= ' catlinks-allhidden';
00513                 }
00514 
00515                 return "<div id='catlinks' class='$classes'>{$catlinks}</div>";
00516         }
00517 
00532         protected function afterContentHook() {
00533                 $data = '';
00534 
00535                 if ( wfRunHooks( 'SkinAfterContent', array( &$data, $this ) ) ) {
00536                         // adding just some spaces shouldn't toggle the output
00537                         // of the whole <div/>, so we use trim() here
00538                         if ( trim( $data ) != '' ) {
00539                                 // Doing this here instead of in the skins to
00540                                 // ensure that the div has the same ID in all
00541                                 // skins
00542                                 $data = "<div id='mw-data-after-content'>\n" .
00543                                         "\t$data\n" .
00544                                         "</div>\n";
00545                         }
00546                 } else {
00547                         wfDebug( "Hook SkinAfterContent changed output processing.\n" );
00548                 }
00549 
00550                 return $data;
00551         }
00552 
00558         protected function generateDebugHTML() {
00559                 global $wgShowDebug;
00560 
00561                 $html = MWDebug::getDebugHTML( $this->getContext() );
00562 
00563                 if ( $wgShowDebug ) {
00564                         $listInternals = $this->formatDebugHTML( $this->getOutput()->mDebugtext );
00565                         $html .= "\n<hr />\n<strong>Debug data:</strong><ul id=\"mw-debug-html\">" .
00566                                 $listInternals . "</ul>\n";
00567                 }
00568 
00569                 return $html;
00570         }
00571 
00576         private function formatDebugHTML( $debugText ) {
00577                 global $wgDebugTimestamps;
00578 
00579                 $lines = explode( "\n", $debugText );
00580                 $curIdent = 0;
00581                 $ret = '<li>';
00582 
00583                 foreach ( $lines as $line ) {
00584                         $pre = '';
00585                         if ( $wgDebugTimestamps ) {
00586                                 $matches = array();
00587                                 if ( preg_match( '/^(\d+\.\d+ {1,3}\d+.\dM\s{2})/', $line, $matches ) ) {
00588                                         $pre = $matches[1];
00589                                         $line = substr( $line, strlen( $pre ) );
00590                                 }
00591                         }
00592                         $display = ltrim( $line );
00593                         $ident = strlen( $line ) - strlen( $display );
00594                         $diff = $ident - $curIdent;
00595 
00596                         $display = $pre . $display;
00597                         if ( $display == '' ) {
00598                                 $display = "\xc2\xa0";
00599                         }
00600 
00601                         if ( !$ident && $diff < 0 && substr( $display, 0, 9 ) != 'Entering ' && substr( $display, 0, 8 ) != 'Exiting ' ) {
00602                                 $ident = $curIdent;
00603                                 $diff = 0;
00604                                 $display = '<span style="background:yellow;">' . htmlspecialchars( $display ) . '</span>';
00605                         } else {
00606                                 $display = htmlspecialchars( $display );
00607                         }
00608 
00609                         if ( $diff < 0 ) {
00610                                 $ret .= str_repeat( "</li></ul>\n", -$diff ) . "</li><li>\n";
00611                         } elseif ( $diff == 0 ) {
00612                                 $ret .= "</li><li>\n";
00613                         } else {
00614                                 $ret .= str_repeat( "<ul><li>\n", $diff );
00615                         }
00616                         $ret .= "<tt>$display</tt>\n";
00617 
00618                         $curIdent = $ident;
00619                 }
00620 
00621                 $ret .= str_repeat( '</li></ul>', $curIdent ) . '</li>';
00622 
00623                 return $ret;
00624         }
00625 
00631         function bottomScripts() {
00632                 // TODO and the suckage continues. This function is really just a wrapper around
00633                 // OutputPage::getBottomScripts() which takes a Skin param. This should be cleaned
00634                 // up at some point
00635                 $bottomScriptText = $this->getOutput()->getBottomScripts();
00636                 wfRunHooks( 'SkinAfterBottomScripts', array( $this, &$bottomScriptText ) );
00637 
00638                 return $bottomScriptText;
00639         }
00640 
00647         function printSource() {
00648                 $oldid = $this->getRevisionId();
00649                 if ( $oldid ) {
00650                         $url = htmlspecialchars( $this->getTitle()->getCanonicalURL( 'oldid=' . $oldid ) );
00651                 } else {
00652                         // oldid not available for non existing pages
00653                         $url = htmlspecialchars( $this->getTitle()->getCanonicalURL() );
00654                 }
00655                 return $this->msg( 'retrievedfrom', '<a href="' . $url . '">' . $url . '</a>' )->text();
00656         }
00657 
00661         function getUndeleteLink() {
00662                 $action = $this->getRequest()->getVal( 'action', 'view' );
00663 
00664                 if ( $this->getUser()->isAllowed( 'deletedhistory' ) &&
00665                         ( $this->getTitle()->getArticleId() == 0 || $action == 'history' ) ) {
00666                         $n = $this->getTitle()->isDeleted();
00667 
00668 
00669                         if ( $n ) {
00670                                 if ( $this->getUser()->isAllowed( 'undelete' ) ) {
00671                                         $msg = 'thisisdeleted';
00672                                 } else {
00673                                         $msg = 'viewdeleted';
00674                                 }
00675 
00676                                 return $this->msg( $msg )->rawParams(
00677                                         Linker::linkKnown(
00678                                                 SpecialPage::getTitleFor( 'Undelete', $this->getTitle()->getPrefixedDBkey() ),
00679                                                 $this->msg( 'restorelink' )->numParams( $n )->escaped() )
00680                                         )->text();
00681                         }
00682                 }
00683 
00684                 return '';
00685         }
00686 
00690         function subPageSubtitle() {
00691                 $out = $this->getOutput();
00692                 $subpages = '';
00693 
00694                 if ( !wfRunHooks( 'SkinSubPageSubtitle', array( &$subpages, $this, $out ) ) ) {
00695                         return $subpages;
00696                 }
00697 
00698                 if ( $out->isArticle() && MWNamespace::hasSubpages( $out->getTitle()->getNamespace() ) ) {
00699                         $ptext = $this->getTitle()->getPrefixedText();
00700                         if ( preg_match( '/\//', $ptext ) ) {
00701                                 $links = explode( '/', $ptext );
00702                                 array_pop( $links );
00703                                 $c = 0;
00704                                 $growinglink = '';
00705                                 $display = '';
00706 
00707                                 foreach ( $links as $link ) {
00708                                         $growinglink .= $link;
00709                                         $display .= $link;
00710                                         $linkObj = Title::newFromText( $growinglink );
00711 
00712                                         if ( is_object( $linkObj ) && $linkObj->exists() ) {
00713                                                 $getlink = Linker::linkKnown(
00714                                                         $linkObj,
00715                                                         htmlspecialchars( $display )
00716                                                 );
00717 
00718                                                 $c++;
00719 
00720                                                 if ( $c > 1 ) {
00721                                                         $subpages .= $this->msg( 'pipe-separator' )->escaped();
00722                                                 } else  {
00723                                                         $subpages .= '&lt; ';
00724                                                 }
00725 
00726                                                 $subpages .= $getlink;
00727                                                 $display = '';
00728                                         } else {
00729                                                 $display .= '/';
00730                                         }
00731                                         $growinglink .= '/';
00732                                 }
00733                         }
00734                 }
00735 
00736                 return $subpages;
00737         }
00738 
00743         function showIPinHeader() {
00744                 global $wgShowIPinHeader;
00745                 return $wgShowIPinHeader && session_id() != '';
00746         }
00747 
00751         function getSearchLink() {
00752                 $searchPage = SpecialPage::getTitleFor( 'Search' );
00753                 return $searchPage->getLocalURL();
00754         }
00755 
00759         function escapeSearchLink() {
00760                 return htmlspecialchars( $this->getSearchLink() );
00761         }
00762 
00767         function getCopyright( $type = 'detect' ) {
00768                 global $wgRightsPage, $wgRightsUrl, $wgRightsText, $wgContLang;
00769 
00770                 if ( $type == 'detect' ) {
00771                         if ( !$this->isRevisionCurrent() && !$this->msg( 'history_copyright' )->inContentLanguage()->isDisabled() ) {
00772                                 $type = 'history';
00773                         } else {
00774                                 $type = 'normal';
00775                         }
00776                 }
00777 
00778                 if ( $type == 'history' ) {
00779                         $msg = 'history_copyright';
00780                 } else {
00781                         $msg = 'copyright';
00782                 }
00783 
00784                 if ( $wgRightsPage ) {
00785                         $title = Title::newFromText( $wgRightsPage );
00786                         $link = Linker::linkKnown( $title, $wgRightsText );
00787                 } elseif ( $wgRightsUrl ) {
00788                         $link = Linker::makeExternalLink( $wgRightsUrl, $wgRightsText );
00789                 } elseif ( $wgRightsText ) {
00790                         $link = $wgRightsText;
00791                 } else {
00792                         # Give up now
00793                         return '';
00794                 }
00795 
00796                 // Allow for site and per-namespace customization of copyright notice.
00797                 $forContent = true;
00798 
00799                 wfRunHooks( 'SkinCopyrightFooter', array( $this->getTitle(), $type, &$msg, &$link, &$forContent ) );
00800 
00801                 $msgObj = $this->msg( $msg )->rawParams( $link );
00802                 if ( $forContent ) {
00803                         $msg = $msgObj->inContentLanguage()->text();
00804                         if ( $this->getLanguage()->getCode() !== $wgContLang->getCode() ) {
00805                                 $msg = Html::rawElement( 'span', array( 'lang' => $wgContLang->getHtmlCode(), 'dir' => $wgContLang->getDir() ), $msg );
00806                         }
00807                         return $msg;
00808                 } else {
00809                         return $msgObj->text();
00810                 }
00811         }
00812 
00816         function getCopyrightIcon() {
00817                 global $wgRightsUrl, $wgRightsText, $wgRightsIcon, $wgCopyrightIcon;
00818 
00819                 $out = '';
00820 
00821                 if ( isset( $wgCopyrightIcon ) && $wgCopyrightIcon ) {
00822                         $out = $wgCopyrightIcon;
00823                 } elseif ( $wgRightsIcon ) {
00824                         $icon = htmlspecialchars( $wgRightsIcon );
00825 
00826                         if ( $wgRightsUrl ) {
00827                                 $url = htmlspecialchars( $wgRightsUrl );
00828                                 $out .= '<a href="' . $url . '">';
00829                         }
00830 
00831                         $text = htmlspecialchars( $wgRightsText );
00832                         $out .= "<img src=\"$icon\" alt=\"$text\" width=\"88\" height=\"31\" />";
00833 
00834                         if ( $wgRightsUrl ) {
00835                                 $out .= '</a>';
00836                         }
00837                 }
00838 
00839                 return $out;
00840         }
00841 
00846         function getPoweredBy() {
00847                 global $wgStylePath;
00848 
00849                 $url = htmlspecialchars( "$wgStylePath/common/images/poweredby_mediawiki_88x31.png" );
00850                 $text = '<a href="//www.mediawiki.org/"><img src="' . $url . '" height="31" width="88" alt="Powered by MediaWiki" /></a>';
00851                 wfRunHooks( 'SkinGetPoweredBy', array( &$text, $this ) );
00852                 return $text;
00853         }
00854 
00860         protected function lastModified() {
00861                 $timestamp = $this->getOutput()->getRevisionTimestamp();
00862 
00863                 # No cached timestamp, load it from the database
00864                 if ( $timestamp === null ) {
00865                         $timestamp = Revision::getTimestampFromId( $this->getTitle(), $this->getRevisionId() );
00866                 }
00867 
00868                 if ( $timestamp ) {
00869                         $d = $this->getLanguage()->userDate( $timestamp, $this->getUser() );
00870                         $t = $this->getLanguage()->userTime( $timestamp, $this->getUser() );
00871                         $s = ' ' . $this->msg( 'lastmodifiedat', $d, $t )->text();
00872                 } else {
00873                         $s = '';
00874                 }
00875 
00876                 if ( wfGetLB()->getLaggedSlaveMode() ) {
00877                         $s .= ' <strong>' . $this->msg( 'laggedslavemode' )->text() . '</strong>';
00878                 }
00879 
00880                 return $s;
00881         }
00882 
00887         function logoText( $align = '' ) {
00888                 if ( $align != '' ) {
00889                         $a = " align='{$align}'";
00890                 } else {
00891                         $a = '';
00892                 }
00893 
00894                 $mp = $this->msg( 'mainpage' )->escaped();
00895                 $mptitle = Title::newMainPage();
00896                 $url = ( is_object( $mptitle ) ? htmlspecialchars( $mptitle->getLocalURL() ) : '' );
00897 
00898                 $logourl = $this->getLogo();
00899                 $s = "<a href='{$url}'><img{$a} src='{$logourl}' alt='[{$mp}]' /></a>";
00900 
00901                 return $s;
00902         }
00903 
00910         function makeFooterIcon( $icon, $withImage = 'withImage' ) {
00911                 if ( is_string( $icon ) ) {
00912                         $html = $icon;
00913                 } else { // Assuming array
00914                         $url = isset($icon["url"]) ? $icon["url"] : null;
00915                         unset( $icon["url"] );
00916                         if ( isset( $icon["src"] ) && $withImage === 'withImage' ) {
00917                                 $html = Html::element( 'img', $icon ); // do this the lazy way, just pass icon data as an attribute array
00918                         } else {
00919                                 $html = htmlspecialchars( $icon["alt"] );
00920                         }
00921                         if ( $url ) {
00922                                 $html = Html::rawElement( 'a', array( "href" => $url ), $html );
00923                         }
00924                 }
00925                 return $html;
00926         }
00927 
00932         function mainPageLink() {
00933                 $s = Linker::linkKnown(
00934                         Title::newMainPage(),
00935                         $this->msg( 'mainpage' )->escaped()
00936                 );
00937 
00938                 return $s;
00939         }
00940 
00946         public function footerLink( $desc, $page ) {
00947                 // if the link description has been set to "-" in the default language,
00948                 if ( $this->msg( $desc )->inContentLanguage()->isDisabled() ) {
00949                         // then it is disabled, for all languages.
00950                         return '';
00951                 } else {
00952                         // Otherwise, we display the link for the user, described in their
00953                         // language (which may or may not be the same as the default language),
00954                         // but we make the link target be the one site-wide page.
00955                         $title = Title::newFromText( $this->msg( $page )->inContentLanguage()->text() );
00956 
00957                         return Linker::linkKnown(
00958                                 $title,
00959                                 $this->msg( $desc )->escaped()
00960                         );
00961                 }
00962         }
00963 
00968         function privacyLink() {
00969                 return $this->footerLink( 'privacy', 'privacypage' );
00970         }
00971 
00976         function aboutLink() {
00977                 return $this->footerLink( 'aboutsite', 'aboutpage' );
00978         }
00979 
00984         function disclaimerLink() {
00985                 return $this->footerLink( 'disclaimers', 'disclaimerpage' );
00986         }
00987 
00995         function editUrlOptions() {
00996                 $options = array( 'action' => 'edit' );
00997 
00998                 if ( !$this->isRevisionCurrent() ) {
00999                         $options['oldid'] = intval( $this->getRevisionId() );
01000                 }
01001 
01002                 return $options;
01003         }
01004 
01009         function showEmailUser( $id ) {
01010                 if ( $id instanceof User ) {
01011                         $targetUser = $id;
01012                 } else {
01013                         $targetUser = User::newFromId( $id );
01014                 }
01015                 return $this->getUser()->canSendEmail() && # the sending user must have a confirmed email address
01016                         $targetUser->canReceiveEmail(); # the target user must have a confirmed email address and allow emails from users
01017         }
01018 
01026         function getCommonStylePath( $name ) {
01027                 global $wgStylePath, $wgStyleVersion;
01028                 return "$wgStylePath/common/$name?$wgStyleVersion";
01029         }
01030 
01038         function getSkinStylePath( $name ) {
01039                 global $wgStylePath, $wgStyleVersion;
01040                 return "$wgStylePath/{$this->stylename}/$name?$wgStyleVersion";
01041         }
01042 
01043         /* these are used extensively in SkinTemplate, but also some other places */
01044 
01049         static function makeMainPageUrl( $urlaction = '' ) {
01050                 $title = Title::newMainPage();
01051                 self::checkTitle( $title, '' );
01052 
01053                 return $title->getLocalURL( $urlaction );
01054         }
01055 
01061         static function makeSpecialUrl( $name, $urlaction = '' ) {
01062                 $title = SpecialPage::getSafeTitleFor( $name );
01063                 return $title->getLocalURL( $urlaction );
01064         }
01065 
01072         static function makeSpecialUrlSubpage( $name, $subpage, $urlaction = '' ) {
01073                 $title = SpecialPage::getSafeTitleFor( $name, $subpage );
01074                 return $title->getLocalURL( $urlaction );
01075         }
01076 
01082         static function makeI18nUrl( $name, $urlaction = '' ) {
01083                 $title = Title::newFromText( wfMsgForContent( $name ) );
01084                 self::checkTitle( $title, $name );
01085                 return $title->getLocalURL( $urlaction );
01086         }
01087 
01093         static function makeUrl( $name, $urlaction = '' ) {
01094                 $title = Title::newFromText( $name );
01095                 self::checkTitle( $title, $name );
01096 
01097                 return $title->getLocalURL( $urlaction );
01098         }
01099 
01106         static function makeInternalOrExternalUrl( $name ) {
01107                 if ( preg_match( '/^(?:' . wfUrlProtocols() . ')/', $name ) ) {
01108                         return $name;
01109                 } else {
01110                         return self::makeUrl( $name );
01111                 }
01112         }
01113 
01121         static function makeNSUrl( $name, $urlaction = '', $namespace = NS_MAIN ) {
01122                 $title = Title::makeTitleSafe( $namespace, $name );
01123                 self::checkTitle( $title, $name );
01124 
01125                 return $title->getLocalURL( $urlaction );
01126         }
01127 
01134         static function makeUrlDetails( $name, $urlaction = '' ) {
01135                 $title = Title::newFromText( $name );
01136                 self::checkTitle( $title, $name );
01137 
01138                 return array(
01139                         'href' => $title->getLocalURL( $urlaction ),
01140                         'exists' => $title->getArticleID() != 0,
01141                 );
01142         }
01143 
01150         static function makeKnownUrlDetails( $name, $urlaction = '' ) {
01151                 $title = Title::newFromText( $name );
01152                 self::checkTitle( $title, $name );
01153 
01154                 return array(
01155                         'href' => $title->getLocalURL( $urlaction ),
01156                         'exists' => true
01157                 );
01158         }
01159 
01166         static function checkTitle( &$title, $name ) {
01167                 if ( !is_object( $title ) ) {
01168                         $title = Title::newFromText( $name );
01169                         if ( !is_object( $title ) ) {
01170                                 $title = Title::newFromText( '--error: link target missing--' );
01171                         }
01172                 }
01173         }
01174 
01180         function buildSidebar() {
01181                 global $wgMemc, $wgEnableSidebarCache, $wgSidebarCacheExpiry;
01182                 wfProfileIn( __METHOD__ );
01183 
01184                 $key = wfMemcKey( 'sidebar', $this->getLanguage()->getCode() );
01185 
01186                 if ( $wgEnableSidebarCache ) {
01187                         $cachedsidebar = $wgMemc->get( $key );
01188                         if ( $cachedsidebar ) {
01189                                 wfProfileOut( __METHOD__ );
01190                                 return $cachedsidebar;
01191                         }
01192                 }
01193 
01194                 $bar = array();
01195                 $this->addToSidebar( $bar, 'sidebar' );
01196 
01197                 wfRunHooks( 'SkinBuildSidebar', array( $this, &$bar ) );
01198                 if ( $wgEnableSidebarCache ) {
01199                         $wgMemc->set( $key, $bar, $wgSidebarCacheExpiry );
01200                 }
01201 
01202                 wfProfileOut( __METHOD__ );
01203                 return $bar;
01204         }
01214         function addToSidebar( &$bar, $message ) {
01215                 $this->addToSidebarPlain( $bar, wfMsgForContentNoTrans( $message ) );
01216         }
01217 
01225         function addToSidebarPlain( &$bar, $text ) {
01226                 $lines = explode( "\n", $text );
01227 
01228                 $heading = '';
01229 
01230                 foreach ( $lines as $line ) {
01231                         if ( strpos( $line, '*' ) !== 0 ) {
01232                                 continue;
01233                         }
01234                         $line = rtrim( $line, "\r" ); // for Windows compat
01235 
01236                         if ( strpos( $line, '**' ) !== 0 ) {
01237                                 $heading = trim( $line, '* ' );
01238                                 if ( !array_key_exists( $heading, $bar ) ) {
01239                                         $bar[$heading] = array();
01240                                 }
01241                         } else {
01242                                 $line = trim( $line, '* ' );
01243 
01244                                 if ( strpos( $line, '|' ) !== false ) { // sanity check
01245                                         $line = MessageCache::singleton()->transform( $line, false, null, $this->getTitle() );
01246                                         $line = array_map( 'trim', explode( '|', $line, 2 ) );
01247                                         if ( count( $line ) !== 2 ) {
01248                                                 // Second sanity check, could be hit by people doing
01249                                                 // funky stuff with parserfuncs... (bug 33321)
01250                                                 continue;
01251                                         }
01252 
01253                                         $extraAttribs = array();
01254 
01255                                         $msgLink = $this->msg( $line[0] )->inContentLanguage();
01256                                         if ( $msgLink->exists() ) {
01257                                                 $link = $msgLink->text();
01258                                                 if ( $link == '-' ) {
01259                                                         continue;
01260                                                 }
01261                                         } else {
01262                                                 $link = $line[0];
01263                                         }
01264                                         $msgText = $this->msg( $line[1] );
01265                                         if ( $msgText->exists() ) {
01266                                                 $text = $msgText->text();
01267                                         } else {
01268                                                 $text = $line[1];
01269                                         }
01270 
01271                                         if ( preg_match( '/^(?:' . wfUrlProtocols() . ')/', $link ) ) {
01272                                                 $href = $link;
01273 
01274                                                 // Parser::getExternalLinkAttribs won't work here because of the Namespace things
01275                                                 global $wgNoFollowLinks, $wgNoFollowDomainExceptions;
01276                                                 if ( $wgNoFollowLinks && !wfMatchesDomainList( $href, $wgNoFollowDomainExceptions ) ) {
01277                                                         $extraAttribs['rel'] = 'nofollow';
01278                                                 }
01279 
01280                                                 global $wgExternalLinkTarget;
01281                                                 if ( $wgExternalLinkTarget) {
01282                                                         $extraAttribs['target'] = $wgExternalLinkTarget;
01283                                                 }
01284                                         } else {
01285                                                 $title = Title::newFromText( $link );
01286 
01287                                                 if ( $title ) {
01288                                                         $title = $title->fixSpecialName();
01289                                                         $href = $title->getLinkURL();
01290                                                 } else {
01291                                                         $href = 'INVALID-TITLE';
01292                                                 }
01293                                         }
01294 
01295                                         $bar[$heading][] = array_merge( array(
01296                                                 'text' => $text,
01297                                                 'href' => $href,
01298                                                 'id' => 'n-' . Sanitizer::escapeId( strtr( $line[1], ' ', '-' ), 'noninitial' ),
01299                                                 'active' => false
01300                                         ), $extraAttribs );
01301                                 } else {
01302                                         continue;
01303                                 }
01304                         }
01305                 }
01306 
01307                 return $bar;
01308         }
01309 
01317         public function commonPrintStylesheet() {
01318                 return true;
01319         }
01320 
01325         function getNewtalks() {
01326                 $out = $this->getOutput();
01327 
01328                 $newtalks = $this->getUser()->getNewMessageLinks();
01329                 $ntl = '';
01330 
01331                 if ( count( $newtalks ) == 1 && $newtalks[0]['wiki'] === wfWikiID() ) {
01332                         $userTitle = $this->getUser()->getUserPage();
01333                         $userTalkTitle = $userTitle->getTalkPage();
01334 
01335                         if ( !$userTalkTitle->equals( $out->getTitle() ) ) {
01336                                 $newMessagesLink = Linker::linkKnown(
01337                                         $userTalkTitle,
01338                                         $this->msg( 'newmessageslink' )->escaped(),
01339                                         array(),
01340                                         array( 'redirect' => 'no' )
01341                                 );
01342 
01343                                 $newMessagesDiffLink = Linker::linkKnown(
01344                                         $userTalkTitle,
01345                                         $this->msg( 'newmessagesdifflink' )->escaped(),
01346                                         array(),
01347                                         array( 'diff' => 'cur' )
01348                                 );
01349 
01350                                 $ntl = $this->msg(
01351                                         'youhavenewmessages',
01352                                         $newMessagesLink,
01353                                         $newMessagesDiffLink
01354                                 )->text();
01355                                 # Disable Squid cache
01356                                 $out->setSquidMaxage( 0 );
01357                         }
01358                 } elseif ( count( $newtalks ) ) {
01359                         // _>" " for BC <= 1.16
01360                         $sep = str_replace( '_', ' ', $this->msg( 'newtalkseparator' )->escaped() );
01361                         $msgs = array();
01362 
01363                         foreach ( $newtalks as $newtalk ) {
01364                                 $msgs[] = Xml::element(
01365                                         'a',
01366                                         array( 'href' => $newtalk['link'] ), $newtalk['wiki']
01367                                 );
01368                         }
01369                         $parts = implode( $sep, $msgs );
01370                         $ntl = $this->msg( 'youhavenewmessagesmulti' )->rawParams( $parts )->escaped();
01371                         $out->setSquidMaxage( 0 );
01372                 }
01373 
01374                 return $ntl;
01375         }
01376 
01383         private function getCachedNotice( $name ) {
01384                 global $wgRenderHashAppend, $parserMemc, $wgContLang;
01385 
01386                 wfProfileIn( __METHOD__ );
01387 
01388                 $needParse = false;
01389 
01390                 if( $name === 'default' ) {
01391                         // special case
01392                         global $wgSiteNotice;
01393                         $notice = $wgSiteNotice;
01394                         if( empty( $notice ) ) {
01395                                 wfProfileOut( __METHOD__ );
01396                                 return false;
01397                         }
01398                 } else {
01399                         $msg = $this->msg( $name )->inContentLanguage();
01400                         if( $msg->isDisabled() ) {
01401                                 wfProfileOut( __METHOD__ );
01402                                 return false;
01403                         }
01404                         $notice = $msg->plain();
01405                 }
01406 
01407                 // Use the extra hash appender to let eg SSL variants separately cache.
01408                 $key = wfMemcKey( $name . $wgRenderHashAppend );
01409                 $cachedNotice = $parserMemc->get( $key );
01410                 if( is_array( $cachedNotice ) ) {
01411                         if( md5( $notice ) == $cachedNotice['hash'] ) {
01412                                 $notice = $cachedNotice['html'];
01413                         } else {
01414                                 $needParse = true;
01415                         }
01416                 } else {
01417                         $needParse = true;
01418                 }
01419 
01420                 if ( $needParse ) {
01421                         $parsed = $this->getOutput()->parse( $notice );
01422                         $parserMemc->set( $key, array( 'html' => $parsed, 'hash' => md5( $notice ) ), 600 );
01423                         $notice = $parsed;
01424                 }
01425 
01426                 $notice = Html::rawElement( 'div', array( 'id' => 'localNotice',
01427                         'lang' => $wgContLang->getHtmlCode(), 'dir' => $wgContLang->getDir() ), $notice );
01428                 wfProfileOut( __METHOD__ );
01429                 return $notice;
01430         }
01431 
01437         function getNamespaceNotice() {
01438                 wfProfileIn( __METHOD__ );
01439 
01440                 $key = 'namespacenotice-' . $this->getTitle()->getNsText();
01441                 $namespaceNotice = $this->getCachedNotice( $key );
01442                 if ( $namespaceNotice && substr( $namespaceNotice, 0, 7 ) != '<p>&lt;' ) {
01443                         $namespaceNotice = '<div id="namespacebanner">' . $namespaceNotice . '</div>';
01444                 } else {
01445                         $namespaceNotice = '';
01446                 }
01447 
01448                 wfProfileOut( __METHOD__ );
01449                 return $namespaceNotice;
01450         }
01451 
01457         function getSiteNotice() {
01458                 wfProfileIn( __METHOD__ );
01459                 $siteNotice = '';
01460 
01461                 if ( wfRunHooks( 'SiteNoticeBefore', array( &$siteNotice, $this ) ) ) {
01462                         if ( is_object( $this->getUser() ) && $this->getUser()->isLoggedIn() ) {
01463                                 $siteNotice = $this->getCachedNotice( 'sitenotice' );
01464                         } else {
01465                                 $anonNotice = $this->getCachedNotice( 'anonnotice' );
01466                                 if ( !$anonNotice ) {
01467                                         $siteNotice = $this->getCachedNotice( 'sitenotice' );
01468                                 } else {
01469                                         $siteNotice = $anonNotice;
01470                                 }
01471                         }
01472                         if ( !$siteNotice ) {
01473                                 $siteNotice = $this->getCachedNotice( 'default' );
01474                         }
01475                 }
01476 
01477                 wfRunHooks( 'SiteNoticeAfter', array( &$siteNotice, $this ) );
01478                 wfProfileOut( __METHOD__ );
01479                 return $siteNotice;
01480         }
01481 
01495         public function doEditSectionLink( Title $nt, $section, $tooltip = null, $lang = false ) {
01496                 // HTML generated here should probably have userlangattributes
01497                 // added to it for LTR text on RTL pages
01498                 $attribs = array();
01499                 if ( !is_null( $tooltip ) ) {
01500                         # Bug 25462: undo double-escaping.
01501                         $tooltip = Sanitizer::decodeCharReferences( $tooltip );
01502                         $attribs['title'] = wfMsgExt( 'editsectionhint', array( 'language' => $lang, 'parsemag', 'replaceafter' ), $tooltip );
01503                 }
01504                 $link = Linker::link( $nt, wfMsgExt( 'editsection', array( 'language' => $lang ) ),
01505                         $attribs,
01506                         array( 'action' => 'edit', 'section' => $section ),
01507                         array( 'noclasses', 'known' )
01508                 );
01509 
01510                 # Run the old hook.  This takes up half of the function . . . hopefully
01511                 # we can rid of it someday.
01512                 $attribs = '';
01513                 if ( $tooltip ) {
01514                         $attribs = wfMsgExt( 'editsectionhint', array( 'language' => $lang, 'parsemag', 'escape', 'replaceafter' ), $tooltip );
01515                         $attribs = " title=\"$attribs\"";
01516                 }
01517                 $result = null;
01518                 wfRunHooks( 'EditSectionLink', array( &$this, $nt, $section, $attribs, $link, &$result, $lang ) );
01519                 if ( !is_null( $result ) ) {
01520                         # For reverse compatibility, add the brackets *after* the hook is
01521                         # run, and even add them to hook-provided text.  (This is the main
01522                         # reason that the EditSectionLink hook is deprecated in favor of
01523                         # DoEditSectionLink: it can't change the brackets or the span.)
01524                         $result = wfMsgExt( 'editsection-brackets', array( 'escape', 'replaceafter', 'language' => $lang ), $result );
01525                         return "<span class=\"editsection\">$result</span>";
01526                 }
01527 
01528                 # Add the brackets and the span, and *then* run the nice new hook, with
01529                 # clean and non-redundant arguments.
01530                 $result = wfMsgExt( 'editsection-brackets', array( 'escape', 'replaceafter', 'language' => $lang ), $link );
01531                 $result = "<span class=\"editsection\">$result</span>";
01532 
01533                 wfRunHooks( 'DoEditSectionLink', array( $this, $nt, $section, $tooltip, &$result, $lang ) );
01534                 return $result;
01535         }
01536 
01544         function __call( $fname, $args ) {
01545                 $realFunction = array( 'Linker', $fname );
01546                 if ( is_callable( $realFunction ) ) {
01547                         return call_user_func_array( $realFunction, $args );
01548                 } else {
01549                         $className = get_class( $this );
01550                         throw new MWException( "Call to undefined method $className::$fname" );
01551                 }
01552         }
01553 
01554 }