MediaWiki  REL1_20
Skin.php
Go to the documentation of this file.
00001 <?php
00035 abstract class Skin extends ContextSource {
00036         protected $skinname = 'standard';
00037         protected $mRelevantTitle = null;
00038         protected $mRelevantUser = null;
00039 
00044         static function getSkinNames() {
00045                 global $wgValidSkinNames;
00046                 static $skinsInitialised = false;
00047 
00048                 if ( !$skinsInitialised || !count( $wgValidSkinNames ) ) {
00049                         # Get a list of available skins
00050                         # Build using the regular expression '^(.*).php$'
00051                         # Array keys are all lower case, array value keep the case used by filename
00052                         #
00053                         wfProfileIn( __METHOD__ . '-init' );
00054 
00055                         global $wgStyleDirectory;
00056 
00057                         $skinDir = dir( $wgStyleDirectory );
00058 
00059                         # while code from www.php.net
00060                         while ( false !== ( $file = $skinDir->read() ) ) {
00061                                 // Skip non-PHP files, hidden files, and '.dep' includes
00062                                 $matches = array();
00063 
00064                                 if ( preg_match( '/^([^.]*)\.php$/', $file, $matches ) ) {
00065                                         $aSkin = $matches[1];
00066                                         $wgValidSkinNames[strtolower( $aSkin )] = $aSkin;
00067                                 }
00068                         }
00069                         $skinDir->close();
00070                         $skinsInitialised = true;
00071                         wfProfileOut( __METHOD__ . '-init' );
00072                 }
00073                 return $wgValidSkinNames;
00074         }
00075 
00080         static function getSkinNameMessages() {
00081                 $messages = array();
00082                 foreach( self::getSkinNames() as $skinKey => $skinName ) {
00083                         $messages[] = "skinname-$skinKey";
00084                 }
00085                 return $messages;
00086         }
00087 
00094         public static function getUsableSkins() {
00095                 global $wgSkipSkins;
00096 
00097                 $usableSkins = self::getSkinNames();
00098 
00099                 foreach ( $wgSkipSkins as $skip ) {
00100                         unset( $usableSkins[$skip] );
00101                 }
00102 
00103                 return $usableSkins;
00104         }
00105 
00113         static function normalizeKey( $key ) {
00114                 global $wgDefaultSkin;
00115 
00116                 $skinNames = Skin::getSkinNames();
00117 
00118                 if ( $key == '' || $key == 'default' ) {
00119                         // Don't return the default immediately;
00120                         // in a misconfiguration we need to fall back.
00121                         $key = $wgDefaultSkin;
00122                 }
00123 
00124                 if ( isset( $skinNames[$key] ) ) {
00125                         return $key;
00126                 }
00127 
00128                 // Older versions of the software used a numeric setting
00129                 // in the user preferences.
00130                 $fallback = array(
00131                         0 => $wgDefaultSkin,
00132                         1 => 'nostalgia',
00133                         2 => 'cologneblue'
00134                 );
00135 
00136                 if ( isset( $fallback[$key] ) ) {
00137                         $key = $fallback[$key];
00138                 }
00139 
00140                 if ( isset( $skinNames[$key] ) ) {
00141                         return $key;
00142                 } elseif ( isset( $skinNames[$wgDefaultSkin] ) ) {
00143                         return $wgDefaultSkin;
00144                 } else {
00145                         return 'vector';
00146                 }
00147         }
00148 
00154         static function &newFromKey( $key ) {
00155                 global $wgStyleDirectory;
00156 
00157                 $key = Skin::normalizeKey( $key );
00158 
00159                 $skinNames = Skin::getSkinNames();
00160                 $skinName = $skinNames[$key];
00161                 $className = "Skin{$skinName}";
00162 
00163                 # Grab the skin class and initialise it.
00164                 if ( !MWInit::classExists( $className ) ) {
00165 
00166                         if ( !defined( 'MW_COMPILED' ) ) {
00167                                 require_once( "{$wgStyleDirectory}/{$skinName}.php" );
00168                         }
00169 
00170                         # Check if we got if not failback to default skin
00171                         if ( !MWInit::classExists( $className ) ) {
00172                                 # DO NOT die if the class isn't found. This breaks maintenance
00173                                 # scripts and can cause a user account to be unrecoverable
00174                                 # except by SQL manipulation if a previously valid skin name
00175                                 # is no longer valid.
00176                                 wfDebug( "Skin class does not exist: $className\n" );
00177                                 $className = 'SkinVector';
00178                                 if ( !defined( 'MW_COMPILED' ) ) {
00179                                         require_once( "{$wgStyleDirectory}/Vector.php" );
00180                                 }
00181                         }
00182                 }
00183                 $skin = new $className( $key );
00184                 return $skin;
00185         }
00186 
00188         public function getSkinName() {
00189                 return $this->skinname;
00190         }
00191 
00195         function initPage( OutputPage $out ) {
00196                 wfProfileIn( __METHOD__ );
00197 
00198                 $this->preloadExistence();
00199 
00200                 wfProfileOut( __METHOD__ );
00201         }
00202 
00206         function preloadExistence() {
00207                 $user = $this->getUser();
00208 
00209                 // User/talk link
00210                 $titles = array( $user->getUserPage(), $user->getTalkPage() );
00211 
00212                 // Other tab link
00213                 if ( $this->getTitle()->isSpecialPage() ) {
00214                         // nothing
00215                 } elseif ( $this->getTitle()->isTalkPage() ) {
00216                         $titles[] = $this->getTitle()->getSubjectPage();
00217                 } else {
00218                         $titles[] = $this->getTitle()->getTalkPage();
00219                 }
00220 
00221                 $lb = new LinkBatch( $titles );
00222                 $lb->setCaller( __METHOD__ );
00223                 $lb->execute();
00224         }
00225 
00231         public function getRevisionId() {
00232                 return $this->getOutput()->getRevisionId();
00233         }
00234 
00240         public function isRevisionCurrent() {
00241                 $revID = $this->getRevisionId();
00242                 return $revID == 0 || $revID == $this->getTitle()->getLatestRevID();
00243         }
00244 
00250         public function setRelevantTitle( $t ) {
00251                 $this->mRelevantTitle = $t;
00252         }
00253 
00264         public function getRelevantTitle() {
00265                 if ( isset($this->mRelevantTitle) ) {
00266                         return $this->mRelevantTitle;
00267                 }
00268                 return $this->getTitle();
00269         }
00270 
00276         public function setRelevantUser( $u ) {
00277                 $this->mRelevantUser = $u;
00278         }
00279 
00288         public function getRelevantUser() {
00289                 if ( isset($this->mRelevantUser) ) {
00290                         return $this->mRelevantUser;
00291                 }
00292                 $title = $this->getRelevantTitle();
00293                 if( $title->getNamespace() == NS_USER || $title->getNamespace() == NS_USER_TALK ) {
00294                         $rootUser = strtok( $title->getText(), '/' );
00295                         if ( User::isIP( $rootUser ) ) {
00296                                 $this->mRelevantUser = User::newFromName( $rootUser, false );
00297                         } else {
00298                                 $user = User::newFromName( $rootUser, false );
00299                                 if ( $user && $user->isLoggedIn() ) {
00300                                         $this->mRelevantUser = $user;
00301                                 }
00302                         }
00303                         return $this->mRelevantUser;
00304                 }
00305                 return null;
00306         }
00307 
00312         abstract function outputPage( OutputPage $out = null );
00313 
00318         static function makeVariablesScript( $data ) {
00319                 if ( $data ) {
00320                         return Html::inlineScript(
00321                                 ResourceLoader::makeLoaderConditionalScript( ResourceLoader::makeConfigSetScript( $data ) )
00322                         );
00323                 } else {
00324                         return '';
00325                 }
00326         }
00327 
00335         public static function makeGlobalVariablesScript( $unused ) {
00336                 global $wgOut;
00337 
00338                 wfDeprecated( __METHOD__, '1.19' );
00339 
00340                 return self::makeVariablesScript( $wgOut->getJSVars() );
00341         }
00342 
00348         public static function getDynamicStylesheetQuery() {
00349                 global $wgSquidMaxage;
00350 
00351                 return array(
00352                                 'action' => 'raw',
00353                                 'maxage' => $wgSquidMaxage,
00354                                 'usemsgcache' => 'yes',
00355                                 'ctype' => 'text/css',
00356                                 'smaxage' => $wgSquidMaxage,
00357                         );
00358         }
00359 
00368         abstract function setupSkinUserCss( OutputPage $out );
00369 
00375         function getPageClasses( $title ) {
00376                 $numeric = 'ns-' . $title->getNamespace();
00377 
00378                 if ( $title->isSpecialPage() ) {
00379                         $type = 'ns-special';
00380                         // bug 23315: provide a class based on the canonical special page name without subpages
00381                         list( $canonicalName ) = SpecialPageFactory::resolveAlias( $title->getDBkey() );
00382                         if ( $canonicalName ) {
00383                                 $type .= ' ' . Sanitizer::escapeClass( "mw-special-$canonicalName" );
00384                         } else {
00385                                 $type .= ' mw-invalidspecialpage';
00386                         }
00387                 } elseif ( $title->isTalkPage() ) {
00388                         $type = 'ns-talk';
00389                 } else {
00390                         $type = 'ns-subject';
00391                 }
00392 
00393                 $name = Sanitizer::escapeClass( 'page-' . $title->getPrefixedText() );
00394 
00395                 return "$numeric $type $name";
00396         }
00397 
00405         function addToBodyAttributes( $out, &$bodyAttrs ) {
00406                 // does nothing by default
00407         }
00408 
00413         function getLogo() {
00414                 global $wgLogo;
00415                 return $wgLogo;
00416         }
00417 
00421         function getCategoryLinks() {
00422                 global $wgUseCategoryBrowser;
00423 
00424                 $out = $this->getOutput();
00425                 $allCats = $out->getCategoryLinks();
00426 
00427                 if ( !count( $allCats ) ) {
00428                         return '';
00429                 }
00430 
00431                 $embed = "<li>";
00432                 $pop = "</li>";
00433 
00434                 $s = '';
00435                 $colon = $this->msg( 'colon-separator' )->escaped();
00436 
00437                 if ( !empty( $allCats['normal'] ) ) {
00438                         $t = $embed . implode( "{$pop}{$embed}" , $allCats['normal'] ) . $pop;
00439 
00440                         $msg = $this->msg( 'pagecategories' )->numParams( count( $allCats['normal'] ) )->escaped();
00441                         $linkPage = wfMessage( 'pagecategorieslink' )->inContentLanguage()->text();
00442                         $s .= '<div id="mw-normal-catlinks" class="mw-normal-catlinks">' .
00443                                 Linker::link( Title::newFromText( $linkPage ), $msg )
00444                                 . $colon . '<ul>' . $t . '</ul>' . '</div>';
00445                 }
00446 
00447                 # Hidden categories
00448                 if ( isset( $allCats['hidden'] ) ) {
00449                         if ( $this->getUser()->getBoolOption( 'showhiddencats' ) ) {
00450                                 $class = ' mw-hidden-cats-user-shown';
00451                         } elseif ( $this->getTitle()->getNamespace() == NS_CATEGORY ) {
00452                                 $class = ' mw-hidden-cats-ns-shown';
00453                         } else {
00454                                 $class = ' mw-hidden-cats-hidden';
00455                         }
00456 
00457                         $s .= "<div id=\"mw-hidden-catlinks\" class=\"mw-hidden-catlinks$class\">" .
00458                                 $this->msg( 'hidden-categories' )->numParams( count( $allCats['hidden'] ) )->escaped() .
00459                                 $colon . '<ul>' . $embed . implode( "{$pop}{$embed}" , $allCats['hidden'] ) . $pop . '</ul>' .
00460                                 '</div>';
00461                 }
00462 
00463                 # optional 'dmoz-like' category browser. Will be shown under the list
00464                 # of categories an article belong to
00465                 if ( $wgUseCategoryBrowser ) {
00466                         $s .= '<br /><hr />';
00467 
00468                         # get a big array of the parents tree
00469                         $parenttree = $this->getTitle()->getParentCategoryTree();
00470                         # Skin object passed by reference cause it can not be
00471                         # accessed under the method subfunction drawCategoryBrowser
00472                         $tempout = explode( "\n", $this->drawCategoryBrowser( $parenttree ) );
00473                         # Clean out bogus first entry and sort them
00474                         unset( $tempout[0] );
00475                         asort( $tempout );
00476                         # Output one per line
00477                         $s .= implode( "<br />\n", $tempout );
00478                 }
00479 
00480                 return $s;
00481         }
00482 
00488         function drawCategoryBrowser( $tree ) {
00489                 $return = '';
00490 
00491                 foreach ( $tree as $element => $parent ) {
00492                         if ( empty( $parent ) ) {
00493                                 # element start a new list
00494                                 $return .= "\n";
00495                         } else {
00496                                 # grab the others elements
00497                                 $return .= $this->drawCategoryBrowser( $parent ) . ' &gt; ';
00498                         }
00499 
00500                         # add our current element to the list
00501                         $eltitle = Title::newFromText( $element );
00502                         $return .=  Linker::link( $eltitle, htmlspecialchars( $eltitle->getText() ) );
00503                 }
00504 
00505                 return $return;
00506         }
00507 
00511         function getCategories() {
00512                 $out = $this->getOutput();
00513 
00514                 $catlinks = $this->getCategoryLinks();
00515 
00516                 $classes = 'catlinks';
00517 
00518                 // Check what we're showing
00519                 $allCats = $out->getCategoryLinks();
00520                 $showHidden = $this->getUser()->getBoolOption( 'showhiddencats' ) ||
00521                                                 $this->getTitle()->getNamespace() == NS_CATEGORY;
00522 
00523                 if ( empty( $allCats['normal'] ) && !( !empty( $allCats['hidden'] ) && $showHidden ) ) {
00524                         $classes .= ' catlinks-allhidden';
00525                 }
00526 
00527                 return "<div id='catlinks' class='$classes'>{$catlinks}</div>";
00528         }
00529 
00544         protected function afterContentHook() {
00545                 $data = '';
00546 
00547                 if ( wfRunHooks( 'SkinAfterContent', array( &$data, $this ) ) ) {
00548                         // adding just some spaces shouldn't toggle the output
00549                         // of the whole <div/>, so we use trim() here
00550                         if ( trim( $data ) != '' ) {
00551                                 // Doing this here instead of in the skins to
00552                                 // ensure that the div has the same ID in all
00553                                 // skins
00554                                 $data = "<div id='mw-data-after-content'>\n" .
00555                                         "\t$data\n" .
00556                                         "</div>\n";
00557                         }
00558                 } else {
00559                         wfDebug( "Hook SkinAfterContent changed output processing.\n" );
00560                 }
00561 
00562                 return $data;
00563         }
00564 
00570         protected function generateDebugHTML() {
00571                 return MWDebug::getHTMLDebugLog();
00572         }
00573 
00579         function bottomScripts() {
00580                 // TODO and the suckage continues. This function is really just a wrapper around
00581                 // OutputPage::getBottomScripts() which takes a Skin param. This should be cleaned
00582                 // up at some point
00583                 $bottomScriptText = $this->getOutput()->getBottomScripts();
00584                 wfRunHooks( 'SkinAfterBottomScripts', array( $this, &$bottomScriptText ) );
00585 
00586                 return $bottomScriptText;
00587         }
00588 
00595         function printSource() {
00596                 $oldid = $this->getRevisionId();
00597                 if ( $oldid ) {
00598                         $url = htmlspecialchars( wfExpandIRI( $this->getTitle()->getCanonicalURL( 'oldid=' . $oldid ) ) );
00599                 } else {
00600                         // oldid not available for non existing pages
00601                         $url = htmlspecialchars( wfExpandIRI( $this->getTitle()->getCanonicalURL() ) );
00602                 }
00603                 return $this->msg( 'retrievedfrom', '<a href="' . $url . '">' . $url . '</a>' )->text();
00604         }
00605 
00609         function getUndeleteLink() {
00610                 $action = $this->getRequest()->getVal( 'action', 'view' );
00611 
00612                 if ( $this->getUser()->isAllowed( 'deletedhistory' ) &&
00613                         ( $this->getTitle()->getArticleID() == 0 || $action == 'history' ) ) {
00614                         $n = $this->getTitle()->isDeleted();
00615 
00616 
00617                         if ( $n ) {
00618                                 if ( $this->getUser()->isAllowed( 'undelete' ) ) {
00619                                         $msg = 'thisisdeleted';
00620                                 } else {
00621                                         $msg = 'viewdeleted';
00622                                 }
00623 
00624                                 return $this->msg( $msg )->rawParams(
00625                                         Linker::linkKnown(
00626                                                 SpecialPage::getTitleFor( 'Undelete', $this->getTitle()->getPrefixedDBkey() ),
00627                                                 $this->msg( 'restorelink' )->numParams( $n )->escaped() )
00628                                         )->text();
00629                         }
00630                 }
00631 
00632                 return '';
00633         }
00634 
00638         function subPageSubtitle() {
00639                 global $wgLang;
00640                 $out = $this->getOutput();
00641                 $subpages = '';
00642 
00643                 if ( !wfRunHooks( 'SkinSubPageSubtitle', array( &$subpages, $this, $out ) ) ) {
00644                         return $subpages;
00645                 }
00646 
00647                 if ( $out->isArticle() && MWNamespace::hasSubpages( $out->getTitle()->getNamespace() ) ) {
00648                         $ptext = $this->getTitle()->getPrefixedText();
00649                         if ( preg_match( '/\//', $ptext ) ) {
00650                                 $links = explode( '/', $ptext );
00651                                 array_pop( $links );
00652                                 $c = 0;
00653                                 $growinglink = '';
00654                                 $display = '';
00655 
00656                                 foreach ( $links as $link ) {
00657                                         $growinglink .= $link;
00658                                         $display .= $link;
00659                                         $linkObj = Title::newFromText( $growinglink );
00660 
00661                                         if ( is_object( $linkObj ) && $linkObj->isKnown() ) {
00662                                                 $getlink = Linker::linkKnown(
00663                                                         $linkObj,
00664                                                         htmlspecialchars( $display )
00665                                                 );
00666 
00667                                                 $c++;
00668 
00669                                                 if ( $c > 1 ) {
00670                                                         $subpages .= $wgLang->getDirMarkEntity() . $this->msg( 'pipe-separator' )->escaped();
00671                                                 } else  {
00672                                                         $subpages .= '&lt; ';
00673                                                 }
00674 
00675                                                 $subpages .= $getlink;
00676                                                 $display = '';
00677                                         } else {
00678                                                 $display .= '/';
00679                                         }
00680                                         $growinglink .= '/';
00681                                 }
00682                         }
00683                 }
00684 
00685                 return $subpages;
00686         }
00687 
00692         function showIPinHeader() {
00693                 global $wgShowIPinHeader;
00694                 return $wgShowIPinHeader && session_id() != '';
00695         }
00696 
00700         function getSearchLink() {
00701                 $searchPage = SpecialPage::getTitleFor( 'Search' );
00702                 return $searchPage->getLocalURL();
00703         }
00704 
00708         function escapeSearchLink() {
00709                 return htmlspecialchars( $this->getSearchLink() );
00710         }
00711 
00716         function getCopyright( $type = 'detect' ) {
00717                 global $wgRightsPage, $wgRightsUrl, $wgRightsText, $wgContLang;
00718 
00719                 if ( $type == 'detect' ) {
00720                         if ( !$this->isRevisionCurrent() && !$this->msg( 'history_copyright' )->inContentLanguage()->isDisabled() ) {
00721                                 $type = 'history';
00722                         } else {
00723                                 $type = 'normal';
00724                         }
00725                 }
00726 
00727                 if ( $type == 'history' ) {
00728                         $msg = 'history_copyright';
00729                 } else {
00730                         $msg = 'copyright';
00731                 }
00732 
00733                 if ( $wgRightsPage ) {
00734                         $title = Title::newFromText( $wgRightsPage );
00735                         $link = Linker::linkKnown( $title, $wgRightsText );
00736                 } elseif ( $wgRightsUrl ) {
00737                         $link = Linker::makeExternalLink( $wgRightsUrl, $wgRightsText );
00738                 } elseif ( $wgRightsText ) {
00739                         $link = $wgRightsText;
00740                 } else {
00741                         # Give up now
00742                         return '';
00743                 }
00744 
00745                 // Allow for site and per-namespace customization of copyright notice.
00746                 $forContent = true;
00747 
00748                 wfRunHooks( 'SkinCopyrightFooter', array( $this->getTitle(), $type, &$msg, &$link, &$forContent ) );
00749 
00750                 $msgObj = $this->msg( $msg )->rawParams( $link );
00751                 if ( $forContent ) {
00752                         $msg = $msgObj->inContentLanguage()->text();
00753                         if ( $this->getLanguage()->getCode() !== $wgContLang->getCode() ) {
00754                                 $msg = Html::rawElement( 'span', array( 'lang' => $wgContLang->getHtmlCode(), 'dir' => $wgContLang->getDir() ), $msg );
00755                         }
00756                         return $msg;
00757                 } else {
00758                         return $msgObj->text();
00759                 }
00760         }
00761 
00765         function getCopyrightIcon() {
00766                 global $wgRightsUrl, $wgRightsText, $wgRightsIcon, $wgCopyrightIcon;
00767 
00768                 $out = '';
00769 
00770                 if ( isset( $wgCopyrightIcon ) && $wgCopyrightIcon ) {
00771                         $out = $wgCopyrightIcon;
00772                 } elseif ( $wgRightsIcon ) {
00773                         $icon = htmlspecialchars( $wgRightsIcon );
00774 
00775                         if ( $wgRightsUrl ) {
00776                                 $url = htmlspecialchars( $wgRightsUrl );
00777                                 $out .= '<a href="' . $url . '">';
00778                         }
00779 
00780                         $text = htmlspecialchars( $wgRightsText );
00781                         $out .= "<img src=\"$icon\" alt=\"$text\" width=\"88\" height=\"31\" />";
00782 
00783                         if ( $wgRightsUrl ) {
00784                                 $out .= '</a>';
00785                         }
00786                 }
00787 
00788                 return $out;
00789         }
00790 
00795         function getPoweredBy() {
00796                 global $wgStylePath;
00797 
00798                 $url = htmlspecialchars( "$wgStylePath/common/images/poweredby_mediawiki_88x31.png" );
00799                 $text = '<a href="//www.mediawiki.org/"><img src="' . $url . '" height="31" width="88" alt="Powered by MediaWiki" /></a>';
00800                 wfRunHooks( 'SkinGetPoweredBy', array( &$text, $this ) );
00801                 return $text;
00802         }
00803 
00809         protected function lastModified() {
00810                 $timestamp = $this->getOutput()->getRevisionTimestamp();
00811 
00812                 # No cached timestamp, load it from the database
00813                 if ( $timestamp === null ) {
00814                         $timestamp = Revision::getTimestampFromId( $this->getTitle(), $this->getRevisionId() );
00815                 }
00816 
00817                 if ( $timestamp ) {
00818                         $d = $this->getLanguage()->userDate( $timestamp, $this->getUser() );
00819                         $t = $this->getLanguage()->userTime( $timestamp, $this->getUser() );
00820                         $s = ' ' . $this->msg( 'lastmodifiedat', $d, $t )->text();
00821                 } else {
00822                         $s = '';
00823                 }
00824 
00825                 if ( wfGetLB()->getLaggedSlaveMode() ) {
00826                         $s .= ' <strong>' . $this->msg( 'laggedslavemode' )->text() . '</strong>';
00827                 }
00828 
00829                 return $s;
00830         }
00831 
00836         function logoText( $align = '' ) {
00837                 if ( $align != '' ) {
00838                         $a = " style='float: {$align};'";
00839                 } else {
00840                         $a = '';
00841                 }
00842 
00843                 $mp = $this->msg( 'mainpage' )->escaped();
00844                 $mptitle = Title::newMainPage();
00845                 $url = ( is_object( $mptitle ) ? htmlspecialchars( $mptitle->getLocalURL() ) : '' );
00846 
00847                 $logourl = $this->getLogo();
00848                 $s = "<a href='{$url}'><img{$a} src='{$logourl}' alt='[{$mp}]' /></a>";
00849 
00850                 return $s;
00851         }
00852 
00859         function makeFooterIcon( $icon, $withImage = 'withImage' ) {
00860                 if ( is_string( $icon ) ) {
00861                         $html = $icon;
00862                 } else { // Assuming array
00863                         $url = isset($icon["url"]) ? $icon["url"] : null;
00864                         unset( $icon["url"] );
00865                         if ( isset( $icon["src"] ) && $withImage === 'withImage' ) {
00866                                 $html = Html::element( 'img', $icon ); // do this the lazy way, just pass icon data as an attribute array
00867                         } else {
00868                                 $html = htmlspecialchars( $icon["alt"] );
00869                         }
00870                         if ( $url ) {
00871                                 $html = Html::rawElement( 'a', array( "href" => $url ), $html );
00872                         }
00873                 }
00874                 return $html;
00875         }
00876 
00881         function mainPageLink() {
00882                 $s = Linker::linkKnown(
00883                         Title::newMainPage(),
00884                         $this->msg( 'mainpage' )->escaped()
00885                 );
00886 
00887                 return $s;
00888         }
00889 
00895         public function footerLink( $desc, $page ) {
00896                 // if the link description has been set to "-" in the default language,
00897                 if ( $this->msg( $desc )->inContentLanguage()->isDisabled() ) {
00898                         // then it is disabled, for all languages.
00899                         return '';
00900                 } else {
00901                         // Otherwise, we display the link for the user, described in their
00902                         // language (which may or may not be the same as the default language),
00903                         // but we make the link target be the one site-wide page.
00904                         $title = Title::newFromText( $this->msg( $page )->inContentLanguage()->text() );
00905 
00906                         return Linker::linkKnown(
00907                                 $title,
00908                                 $this->msg( $desc )->escaped()
00909                         );
00910                 }
00911         }
00912 
00917         function privacyLink() {
00918                 return $this->footerLink( 'privacy', 'privacypage' );
00919         }
00920 
00925         function aboutLink() {
00926                 return $this->footerLink( 'aboutsite', 'aboutpage' );
00927         }
00928 
00933         function disclaimerLink() {
00934                 return $this->footerLink( 'disclaimers', 'disclaimerpage' );
00935         }
00936 
00944         function editUrlOptions() {
00945                 $options = array( 'action' => 'edit' );
00946 
00947                 if ( !$this->isRevisionCurrent() ) {
00948                         $options['oldid'] = intval( $this->getRevisionId() );
00949                 }
00950 
00951                 return $options;
00952         }
00953 
00958         function showEmailUser( $id ) {
00959                 if ( $id instanceof User ) {
00960                         $targetUser = $id;
00961                 } else {
00962                         $targetUser = User::newFromId( $id );
00963                 }
00964                 return $this->getUser()->canSendEmail() && # the sending user must have a confirmed email address
00965                         $targetUser->canReceiveEmail(); # the target user must have a confirmed email address and allow emails from users
00966         }
00967 
00975         function getCommonStylePath( $name ) {
00976                 global $wgStylePath, $wgStyleVersion;
00977                 return "$wgStylePath/common/$name?$wgStyleVersion";
00978         }
00979 
00987         function getSkinStylePath( $name ) {
00988                 global $wgStylePath, $wgStyleVersion;
00989                 return "$wgStylePath/{$this->stylename}/$name?$wgStyleVersion";
00990         }
00991 
00992         /* these are used extensively in SkinTemplate, but also some other places */
00993 
00998         static function makeMainPageUrl( $urlaction = '' ) {
00999                 $title = Title::newMainPage();
01000                 self::checkTitle( $title, '' );
01001 
01002                 return $title->getLocalURL( $urlaction );
01003         }
01004 
01016         static function makeSpecialUrl( $name, $urlaction = '', $proto = null ) {
01017                 $title = SpecialPage::getSafeTitleFor( $name );
01018                 if( is_null( $proto ) ) {
01019                         return $title->getLocalURL( $urlaction );
01020                 } else {
01021                         return $title->getFullURL( $urlaction, false, $proto );
01022                 }
01023         }
01024 
01031         static function makeSpecialUrlSubpage( $name, $subpage, $urlaction = '' ) {
01032                 $title = SpecialPage::getSafeTitleFor( $name, $subpage );
01033                 return $title->getLocalURL( $urlaction );
01034         }
01035 
01041         static function makeI18nUrl( $name, $urlaction = '' ) {
01042                 $title = Title::newFromText( wfMessage( $name )->inContentLanguage()->text() );
01043                 self::checkTitle( $title, $name );
01044                 return $title->getLocalURL( $urlaction );
01045         }
01046 
01052         static function makeUrl( $name, $urlaction = '' ) {
01053                 $title = Title::newFromText( $name );
01054                 self::checkTitle( $title, $name );
01055 
01056                 return $title->getLocalURL( $urlaction );
01057         }
01058 
01065         static function makeInternalOrExternalUrl( $name ) {
01066                 if ( preg_match( '/^(?i:' . wfUrlProtocols() . ')/', $name ) ) {
01067                         return $name;
01068                 } else {
01069                         return self::makeUrl( $name );
01070                 }
01071         }
01072 
01080         static function makeNSUrl( $name, $urlaction = '', $namespace = NS_MAIN ) {
01081                 $title = Title::makeTitleSafe( $namespace, $name );
01082                 self::checkTitle( $title, $name );
01083 
01084                 return $title->getLocalURL( $urlaction );
01085         }
01086 
01093         static function makeUrlDetails( $name, $urlaction = '' ) {
01094                 $title = Title::newFromText( $name );
01095                 self::checkTitle( $title, $name );
01096 
01097                 return array(
01098                         'href' => $title->getLocalURL( $urlaction ),
01099                         'exists' => $title->getArticleID() != 0,
01100                 );
01101         }
01102 
01109         static function makeKnownUrlDetails( $name, $urlaction = '' ) {
01110                 $title = Title::newFromText( $name );
01111                 self::checkTitle( $title, $name );
01112 
01113                 return array(
01114                         'href' => $title->getLocalURL( $urlaction ),
01115                         'exists' => true
01116                 );
01117         }
01118 
01125         static function checkTitle( &$title, $name ) {
01126                 if ( !is_object( $title ) ) {
01127                         $title = Title::newFromText( $name );
01128                         if ( !is_object( $title ) ) {
01129                                 $title = Title::newFromText( '--error: link target missing--' );
01130                         }
01131                 }
01132         }
01133 
01139         function buildSidebar() {
01140                 global $wgMemc, $wgEnableSidebarCache, $wgSidebarCacheExpiry;
01141                 wfProfileIn( __METHOD__ );
01142 
01143                 $key = wfMemcKey( 'sidebar', $this->getLanguage()->getCode() );
01144 
01145                 if ( $wgEnableSidebarCache ) {
01146                         $cachedsidebar = $wgMemc->get( $key );
01147                         if ( $cachedsidebar ) {
01148                                 wfProfileOut( __METHOD__ );
01149                                 return $cachedsidebar;
01150                         }
01151                 }
01152 
01153                 $bar = array();
01154                 $this->addToSidebar( $bar, 'sidebar' );
01155 
01156                 wfRunHooks( 'SkinBuildSidebar', array( $this, &$bar ) );
01157                 if ( $wgEnableSidebarCache ) {
01158                         $wgMemc->set( $key, $bar, $wgSidebarCacheExpiry );
01159                 }
01160 
01161                 wfProfileOut( __METHOD__ );
01162                 return $bar;
01163         }
01173         function addToSidebar( &$bar, $message ) {
01174                 $this->addToSidebarPlain( $bar, wfMessage( $message )->inContentLanguage()->plain() );
01175         }
01176 
01184         function addToSidebarPlain( &$bar, $text ) {
01185                 $lines = explode( "\n", $text );
01186 
01187                 $heading = '';
01188 
01189                 foreach ( $lines as $line ) {
01190                         if ( strpos( $line, '*' ) !== 0 ) {
01191                                 continue;
01192                         }
01193                         $line = rtrim( $line, "\r" ); // for Windows compat
01194 
01195                         if ( strpos( $line, '**' ) !== 0 ) {
01196                                 $heading = trim( $line, '* ' );
01197                                 if ( !array_key_exists( $heading, $bar ) ) {
01198                                         $bar[$heading] = array();
01199                                 }
01200                         } else {
01201                                 $line = trim( $line, '* ' );
01202 
01203                                 if ( strpos( $line, '|' ) !== false ) { // sanity check
01204                                         $line = MessageCache::singleton()->transform( $line, false, null, $this->getTitle() );
01205                                         $line = array_map( 'trim', explode( '|', $line, 2 ) );
01206                                         if ( count( $line ) !== 2 ) {
01207                                                 // Second sanity check, could be hit by people doing
01208                                                 // funky stuff with parserfuncs... (bug 33321)
01209                                                 continue;
01210                                         }
01211 
01212                                         $extraAttribs = array();
01213 
01214                                         $msgLink = $this->msg( $line[0] )->inContentLanguage();
01215                                         if ( $msgLink->exists() ) {
01216                                                 $link = $msgLink->text();
01217                                                 if ( $link == '-' ) {
01218                                                         continue;
01219                                                 }
01220                                         } else {
01221                                                 $link = $line[0];
01222                                         }
01223                                         $msgText = $this->msg( $line[1] );
01224                                         if ( $msgText->exists() ) {
01225                                                 $text = $msgText->text();
01226                                         } else {
01227                                                 $text = $line[1];
01228                                         }
01229 
01230                                         if ( preg_match( '/^(?i:' . wfUrlProtocols() . ')/', $link ) ) {
01231                                                 $href = $link;
01232 
01233                                                 // Parser::getExternalLinkAttribs won't work here because of the Namespace things
01234                                                 global $wgNoFollowLinks, $wgNoFollowDomainExceptions;
01235                                                 if ( $wgNoFollowLinks && !wfMatchesDomainList( $href, $wgNoFollowDomainExceptions ) ) {
01236                                                         $extraAttribs['rel'] = 'nofollow';
01237                                                 }
01238 
01239                                                 global $wgExternalLinkTarget;
01240                                                 if ( $wgExternalLinkTarget) {
01241                                                         $extraAttribs['target'] = $wgExternalLinkTarget;
01242                                                 }
01243                                         } else {
01244                                                 $title = Title::newFromText( $link );
01245 
01246                                                 if ( $title ) {
01247                                                         $title = $title->fixSpecialName();
01248                                                         $href = $title->getLinkURL();
01249                                                 } else {
01250                                                         $href = 'INVALID-TITLE';
01251                                                 }
01252                                         }
01253 
01254                                         $bar[$heading][] = array_merge( array(
01255                                                 'text' => $text,
01256                                                 'href' => $href,
01257                                                 'id' => 'n-' . Sanitizer::escapeId( strtr( $line[1], ' ', '-' ), 'noninitial' ),
01258                                                 'active' => false
01259                                         ), $extraAttribs );
01260                                 } else {
01261                                         continue;
01262                                 }
01263                         }
01264                 }
01265 
01266                 return $bar;
01267         }
01268 
01276         public function commonPrintStylesheet() {
01277                 return true;
01278         }
01279 
01284         function getNewtalks() {
01285                 $out = $this->getOutput();
01286 
01287                 $newtalks = $this->getUser()->getNewMessageLinks();
01288                 $ntl = '';
01289 
01290                 if ( count( $newtalks ) == 1 && $newtalks[0]['wiki'] === wfWikiID() ) {
01291                         $uTalkTitle = $this->getUser()->getTalkPage();
01292 
01293                         if ( !$uTalkTitle->equals( $out->getTitle() ) ) {
01294                                 $lastSeenRev = isset( $newtalks[0]['rev'] ) ? $newtalks[0]['rev'] : null;
01295                                 $nofAuthors = 0;
01296                                 if ( $lastSeenRev !== null ) {
01297                                         $plural = true; // Default if we have a last seen revision: if unknown, use plural
01298                                         $latestRev = Revision::newFromTitle( $uTalkTitle, false, Revision::READ_NORMAL );
01299                                         if ( $latestRev !== null ) {
01300                                                 // Singular if only 1 unseen revision, plural if several unseen revisions.
01301                                                 $plural = $latestRev->getParentId() !== $lastSeenRev->getId();
01302                                                 $nofAuthors = $uTalkTitle->countAuthorsBetween(
01303                                                         $lastSeenRev, $latestRev, 10, 'include_new' );
01304                                         }
01305                                 } else {
01306                                         // Singular if no revision -> diff link will show latest change only in any case
01307                                         $plural = false;
01308                                 }
01309                                 $plural = $plural ? 2 : 1;
01310                                 // 2 signifies "more than one revision". We don't know how many, and even if we did,
01311                                 // the number of revisions or authors is not necessarily the same as the number of
01312                                 // "messages".
01313                                 $newMessagesLink = Linker::linkKnown(
01314                                         $uTalkTitle,
01315                                         $this->msg( 'newmessageslinkplural' )->params( $plural )->escaped(),
01316                                         array(),
01317                                         array( 'redirect' => 'no' )
01318                                 );
01319 
01320                                 $newMessagesDiffLink = Linker::linkKnown(
01321                                         $uTalkTitle,
01322                                         $this->msg( 'newmessagesdifflinkplural' )->params( $plural )->escaped(),
01323                                         array(),
01324                                         $lastSeenRev !== null
01325                                                 ? array( 'oldid' => $lastSeenRev->getId(), 'diff' => 'cur' )
01326                                                 : array( 'diff' => 'cur' )
01327                                 );
01328 
01329                                 if ( $nofAuthors >= 1 && $nofAuthors <= 10 ) {
01330                                         $ntl = $this->msg(
01331                                                 'youhavenewmessagesfromusers',
01332                                                 $newMessagesLink,
01333                                                 $newMessagesDiffLink
01334                                         )->numParams( $nofAuthors );
01335                                 } else {
01336                                         // $nofAuthors === 11 signifies "11 or more" ("more than 10")
01337                                         $ntl = $this->msg(
01338                                                 $nofAuthors > 10 ? 'youhavenewmessagesmanyusers' : 'youhavenewmessages',
01339                                                 $newMessagesLink,
01340                                                 $newMessagesDiffLink
01341                                         );
01342                                 }
01343                                 $ntl = $ntl->text();
01344                                 # Disable Squid cache
01345                                 $out->setSquidMaxage( 0 );
01346                         }
01347                 } elseif ( count( $newtalks ) ) {
01348                         // _>" " for BC <= 1.16
01349                         $sep = str_replace( '_', ' ', $this->msg( 'newtalkseparator' )->escaped() );
01350                         $msgs = array();
01351 
01352                         foreach ( $newtalks as $newtalk ) {
01353                                 $msgs[] = Xml::element(
01354                                         'a',
01355                                         array( 'href' => $newtalk['link'] ), $newtalk['wiki']
01356                                 );
01357                         }
01358                         $parts = implode( $sep, $msgs );
01359                         $ntl = $this->msg( 'youhavenewmessagesmulti' )->rawParams( $parts )->escaped();
01360                         $out->setSquidMaxage( 0 );
01361                 }
01362 
01363                 return $ntl;
01364         }
01365 
01372         private function getCachedNotice( $name ) {
01373                 global $wgRenderHashAppend, $parserMemc, $wgContLang;
01374 
01375                 wfProfileIn( __METHOD__ );
01376 
01377                 $needParse = false;
01378 
01379                 if( $name === 'default' ) {
01380                         // special case
01381                         global $wgSiteNotice;
01382                         $notice = $wgSiteNotice;
01383                         if( empty( $notice ) ) {
01384                                 wfProfileOut( __METHOD__ );
01385                                 return false;
01386                         }
01387                 } else {
01388                         $msg = $this->msg( $name )->inContentLanguage();
01389                         if( $msg->isDisabled() ) {
01390                                 wfProfileOut( __METHOD__ );
01391                                 return false;
01392                         }
01393                         $notice = $msg->plain();
01394                 }
01395 
01396                 // Use the extra hash appender to let eg SSL variants separately cache.
01397                 $key = wfMemcKey( $name . $wgRenderHashAppend );
01398                 $cachedNotice = $parserMemc->get( $key );
01399                 if( is_array( $cachedNotice ) ) {
01400                         if( md5( $notice ) == $cachedNotice['hash'] ) {
01401                                 $notice = $cachedNotice['html'];
01402                         } else {
01403                                 $needParse = true;
01404                         }
01405                 } else {
01406                         $needParse = true;
01407                 }
01408 
01409                 if ( $needParse ) {
01410                         $parsed = $this->getOutput()->parse( $notice );
01411                         $parserMemc->set( $key, array( 'html' => $parsed, 'hash' => md5( $notice ) ), 600 );
01412                         $notice = $parsed;
01413                 }
01414 
01415                 $notice = Html::rawElement( 'div', array( 'id' => 'localNotice',
01416                         'lang' => $wgContLang->getHtmlCode(), 'dir' => $wgContLang->getDir() ), $notice );
01417                 wfProfileOut( __METHOD__ );
01418                 return $notice;
01419         }
01420 
01426         function getNamespaceNotice() {
01427                 wfProfileIn( __METHOD__ );
01428 
01429                 $key = 'namespacenotice-' . $this->getTitle()->getNsText();
01430                 $namespaceNotice = $this->getCachedNotice( $key );
01431                 if ( $namespaceNotice && substr( $namespaceNotice, 0, 7 ) != '<p>&lt;' ) {
01432                         $namespaceNotice = '<div id="namespacebanner">' . $namespaceNotice . '</div>';
01433                 } else {
01434                         $namespaceNotice = '';
01435                 }
01436 
01437                 wfProfileOut( __METHOD__ );
01438                 return $namespaceNotice;
01439         }
01440 
01446         function getSiteNotice() {
01447                 wfProfileIn( __METHOD__ );
01448                 $siteNotice = '';
01449 
01450                 if ( wfRunHooks( 'SiteNoticeBefore', array( &$siteNotice, $this ) ) ) {
01451                         if ( is_object( $this->getUser() ) && $this->getUser()->isLoggedIn() ) {
01452                                 $siteNotice = $this->getCachedNotice( 'sitenotice' );
01453                         } else {
01454                                 $anonNotice = $this->getCachedNotice( 'anonnotice' );
01455                                 if ( !$anonNotice ) {
01456                                         $siteNotice = $this->getCachedNotice( 'sitenotice' );
01457                                 } else {
01458                                         $siteNotice = $anonNotice;
01459                                 }
01460                         }
01461                         if ( !$siteNotice ) {
01462                                 $siteNotice = $this->getCachedNotice( 'default' );
01463                         }
01464                 }
01465 
01466                 wfRunHooks( 'SiteNoticeAfter', array( &$siteNotice, $this ) );
01467                 wfProfileOut( __METHOD__ );
01468                 return $siteNotice;
01469         }
01470 
01484         public function doEditSectionLink( Title $nt, $section, $tooltip = null, $lang = false ) {
01485                 // HTML generated here should probably have userlangattributes
01486                 // added to it for LTR text on RTL pages
01487 
01488                 $lang = wfGetLangObj( $lang );
01489 
01490                 $attribs = array();
01491                 if ( !is_null( $tooltip ) ) {
01492                         # Bug 25462: undo double-escaping.
01493                         $tooltip = Sanitizer::decodeCharReferences( $tooltip );
01494                         $attribs['title'] = wfMessage( 'editsectionhint' )->rawParams( $tooltip )
01495                                 ->inLanguage( $lang )->text();
01496                 }
01497                 $link = Linker::link( $nt, wfMessage( 'editsection' )->inLanguage( $lang )->text(),
01498                         $attribs,
01499                         array( 'action' => 'edit', 'section' => $section ),
01500                         array( 'noclasses', 'known' )
01501                 );
01502 
01503                 # Run the old hook.  This takes up half of the function . . . hopefully
01504                 # we can rid of it someday.
01505                 $attribs = '';
01506                 if ( $tooltip ) {
01507                         $attribs = wfMessage( 'editsectionhint' )->rawParams( $tooltip )
01508                                 ->inLanguage( $lang )->escaped();
01509                         $attribs = " title=\"$attribs\"";
01510                 }
01511                 $result = null;
01512                 wfRunHooks( 'EditSectionLink', array( &$this, $nt, $section, $attribs, $link, &$result, $lang ) );
01513                 if ( !is_null( $result ) ) {
01514                         # For reverse compatibility, add the brackets *after* the hook is
01515                         # run, and even add them to hook-provided text.  (This is the main
01516                         # reason that the EditSectionLink hook is deprecated in favor of
01517                         # DoEditSectionLink: it can't change the brackets or the span.)
01518                         $result = wfMessage( 'editsection-brackets' )->rawParams( $result )
01519                                 ->inLanguage( $lang )->escaped();
01520                         return "<span class=\"editsection\">$result</span>";
01521                 }
01522 
01523                 # Add the brackets and the span, and *then* run the nice new hook, with
01524                 # clean and non-redundant arguments.
01525                 $result = wfMessage( 'editsection-brackets' )->rawParams( $link )
01526                         ->inLanguage( $lang )->escaped();
01527                 $result = "<span class=\"editsection\">$result</span>";
01528 
01529                 wfRunHooks( 'DoEditSectionLink', array( $this, $nt, $section, $tooltip, &$result, $lang ) );
01530                 return $result;
01531         }
01532 
01541         function __call( $fname, $args ) {
01542                 $realFunction = array( 'Linker', $fname );
01543                 if ( is_callable( $realFunction ) ) {
01544                         return call_user_func_array( $realFunction, $args );
01545                 } else {
01546                         $className = get_class( $this );
01547                         throw new MWException( "Call to undefined method $className::$fname" );
01548                 }
01549         }
01550 
01551 }