MediaWiki  REL1_21
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 fallback 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->hasSubjectNamespace( NS_USER ) ) {
00294                         $rootUser = $title->getRootText();
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                         if ( $n ) {
00617                                 if ( $this->getUser()->isAllowed( 'undelete' ) ) {
00618                                         $msg = 'thisisdeleted';
00619                                 } else {
00620                                         $msg = 'viewdeleted';
00621                                 }
00622 
00623                                 return $this->msg( $msg )->rawParams(
00624                                         Linker::linkKnown(
00625                                                 SpecialPage::getTitleFor( 'Undelete', $this->getTitle()->getPrefixedDBkey() ),
00626                                                 $this->msg( 'restorelink' )->numParams( $n )->escaped() )
00627                                         )->text();
00628                         }
00629                 }
00630 
00631                 return '';
00632         }
00633 
00637         function subPageSubtitle() {
00638                 global $wgLang;
00639                 $out = $this->getOutput();
00640                 $subpages = '';
00641 
00642                 if ( !wfRunHooks( 'SkinSubPageSubtitle', array( &$subpages, $this, $out ) ) ) {
00643                         return $subpages;
00644                 }
00645 
00646                 if ( $out->isArticle() && MWNamespace::hasSubpages( $out->getTitle()->getNamespace() ) ) {
00647                         $ptext = $this->getTitle()->getPrefixedText();
00648                         if ( preg_match( '/\//', $ptext ) ) {
00649                                 $links = explode( '/', $ptext );
00650                                 array_pop( $links );
00651                                 $c = 0;
00652                                 $growinglink = '';
00653                                 $display = '';
00654 
00655                                 foreach ( $links as $link ) {
00656                                         $growinglink .= $link;
00657                                         $display .= $link;
00658                                         $linkObj = Title::newFromText( $growinglink );
00659 
00660                                         if ( is_object( $linkObj ) && $linkObj->isKnown() ) {
00661                                                 $getlink = Linker::linkKnown(
00662                                                         $linkObj,
00663                                                         htmlspecialchars( $display )
00664                                                 );
00665 
00666                                                 $c++;
00667 
00668                                                 if ( $c > 1 ) {
00669                                                         $subpages .= $wgLang->getDirMarkEntity() . $this->msg( 'pipe-separator' )->escaped();
00670                                                 } else  {
00671                                                         $subpages .= '&lt; ';
00672                                                 }
00673 
00674                                                 $subpages .= $getlink;
00675                                                 $display = '';
00676                                         } else {
00677                                                 $display .= '/';
00678                                         }
00679                                         $growinglink .= '/';
00680                                 }
00681                         }
00682                 }
00683 
00684                 return $subpages;
00685         }
00686 
00691         function showIPinHeader() {
00692                 global $wgShowIPinHeader;
00693                 return $wgShowIPinHeader && session_id() != '';
00694         }
00695 
00699         function getSearchLink() {
00700                 $searchPage = SpecialPage::getTitleFor( 'Search' );
00701                 return $searchPage->getLocalURL();
00702         }
00703 
00707         function escapeSearchLink() {
00708                 return htmlspecialchars( $this->getSearchLink() );
00709         }
00710 
00715         function getCopyright( $type = 'detect' ) {
00716                 global $wgRightsPage, $wgRightsUrl, $wgRightsText, $wgContLang;
00717 
00718                 if ( $type == 'detect' ) {
00719                         if ( !$this->isRevisionCurrent() && !$this->msg( 'history_copyright' )->inContentLanguage()->isDisabled() ) {
00720                                 $type = 'history';
00721                         } else {
00722                                 $type = 'normal';
00723                         }
00724                 }
00725 
00726                 if ( $type == 'history' ) {
00727                         $msg = 'history_copyright';
00728                 } else {
00729                         $msg = 'copyright';
00730                 }
00731 
00732                 if ( $wgRightsPage ) {
00733                         $title = Title::newFromText( $wgRightsPage );
00734                         $link = Linker::linkKnown( $title, $wgRightsText );
00735                 } elseif ( $wgRightsUrl ) {
00736                         $link = Linker::makeExternalLink( $wgRightsUrl, $wgRightsText );
00737                 } elseif ( $wgRightsText ) {
00738                         $link = $wgRightsText;
00739                 } else {
00740                         # Give up now
00741                         return '';
00742                 }
00743 
00744                 // Allow for site and per-namespace customization of copyright notice.
00745                 $forContent = true;
00746 
00747                 wfRunHooks( 'SkinCopyrightFooter', array( $this->getTitle(), $type, &$msg, &$link, &$forContent ) );
00748 
00749                 $msgObj = $this->msg( $msg )->rawParams( $link );
00750                 if ( $forContent ) {
00751                         $msg = $msgObj->inContentLanguage()->text();
00752                         if ( $this->getLanguage()->getCode() !== $wgContLang->getCode() ) {
00753                                 $msg = Html::rawElement( 'span', array( 'lang' => $wgContLang->getHtmlCode(), 'dir' => $wgContLang->getDir() ), $msg );
00754                         }
00755                         return $msg;
00756                 } else {
00757                         return $msgObj->text();
00758                 }
00759         }
00760 
00764         function getCopyrightIcon() {
00765                 global $wgRightsUrl, $wgRightsText, $wgRightsIcon, $wgCopyrightIcon;
00766 
00767                 $out = '';
00768 
00769                 if ( isset( $wgCopyrightIcon ) && $wgCopyrightIcon ) {
00770                         $out = $wgCopyrightIcon;
00771                 } elseif ( $wgRightsIcon ) {
00772                         $icon = htmlspecialchars( $wgRightsIcon );
00773 
00774                         if ( $wgRightsUrl ) {
00775                                 $url = htmlspecialchars( $wgRightsUrl );
00776                                 $out .= '<a href="' . $url . '">';
00777                         }
00778 
00779                         $text = htmlspecialchars( $wgRightsText );
00780                         $out .= "<img src=\"$icon\" alt=\"$text\" width=\"88\" height=\"31\" />";
00781 
00782                         if ( $wgRightsUrl ) {
00783                                 $out .= '</a>';
00784                         }
00785                 }
00786 
00787                 return $out;
00788         }
00789 
00794         function getPoweredBy() {
00795                 global $wgStylePath;
00796 
00797                 $url = htmlspecialchars( "$wgStylePath/common/images/poweredby_mediawiki_88x31.png" );
00798                 $text = '<a href="//www.mediawiki.org/"><img src="' . $url . '" height="31" width="88" alt="Powered by MediaWiki" /></a>';
00799                 wfRunHooks( 'SkinGetPoweredBy', array( &$text, $this ) );
00800                 return $text;
00801         }
00802 
00808         protected function lastModified() {
00809                 $timestamp = $this->getOutput()->getRevisionTimestamp();
00810 
00811                 # No cached timestamp, load it from the database
00812                 if ( $timestamp === null ) {
00813                         $timestamp = Revision::getTimestampFromId( $this->getTitle(), $this->getRevisionId() );
00814                 }
00815 
00816                 if ( $timestamp ) {
00817                         $d = $this->getLanguage()->userDate( $timestamp, $this->getUser() );
00818                         $t = $this->getLanguage()->userTime( $timestamp, $this->getUser() );
00819                         $s = ' ' . $this->msg( 'lastmodifiedat', $d, $t )->text();
00820                 } else {
00821                         $s = '';
00822                 }
00823 
00824                 if ( wfGetLB()->getLaggedSlaveMode() ) {
00825                         $s .= ' <strong>' . $this->msg( 'laggedslavemode' )->text() . '</strong>';
00826                 }
00827 
00828                 return $s;
00829         }
00830 
00835         function logoText( $align = '' ) {
00836                 if ( $align != '' ) {
00837                         $a = " style='float: {$align};'";
00838                 } else {
00839                         $a = '';
00840                 }
00841 
00842                 $mp = $this->msg( 'mainpage' )->escaped();
00843                 $mptitle = Title::newMainPage();
00844                 $url = ( is_object( $mptitle ) ? htmlspecialchars( $mptitle->getLocalURL() ) : '' );
00845 
00846                 $logourl = $this->getLogo();
00847                 $s = "<a href='{$url}'><img{$a} src='{$logourl}' alt='[{$mp}]' /></a>";
00848 
00849                 return $s;
00850         }
00851 
00858         function makeFooterIcon( $icon, $withImage = 'withImage' ) {
00859                 if ( is_string( $icon ) ) {
00860                         $html = $icon;
00861                 } else { // Assuming array
00862                         $url = isset( $icon["url"] ) ? $icon["url"] : null;
00863                         unset( $icon["url"] );
00864                         if ( isset( $icon["src"] ) && $withImage === 'withImage' ) {
00865                                 $html = Html::element( 'img', $icon ); // do this the lazy way, just pass icon data as an attribute array
00866                         } else {
00867                                 $html = htmlspecialchars( $icon["alt"] );
00868                         }
00869                         if ( $url ) {
00870                                 $html = Html::rawElement( 'a', array( "href" => $url ), $html );
00871                         }
00872                 }
00873                 return $html;
00874         }
00875 
00880         function mainPageLink() {
00881                 $s = Linker::linkKnown(
00882                         Title::newMainPage(),
00883                         $this->msg( 'mainpage' )->escaped()
00884                 );
00885 
00886                 return $s;
00887         }
00888 
00894         public function footerLink( $desc, $page ) {
00895                 // if the link description has been set to "-" in the default language,
00896                 if ( $this->msg( $desc )->inContentLanguage()->isDisabled() ) {
00897                         // then it is disabled, for all languages.
00898                         return '';
00899                 } else {
00900                         // Otherwise, we display the link for the user, described in their
00901                         // language (which may or may not be the same as the default language),
00902                         // but we make the link target be the one site-wide page.
00903                         $title = Title::newFromText( $this->msg( $page )->inContentLanguage()->text() );
00904 
00905                         return Linker::linkKnown(
00906                                 $title,
00907                                 $this->msg( $desc )->escaped()
00908                         );
00909                 }
00910         }
00911 
00916         function privacyLink() {
00917                 return $this->footerLink( 'privacy', 'privacypage' );
00918         }
00919 
00924         function aboutLink() {
00925                 return $this->footerLink( 'aboutsite', 'aboutpage' );
00926         }
00927 
00932         function disclaimerLink() {
00933                 return $this->footerLink( 'disclaimers', 'disclaimerpage' );
00934         }
00935 
00943         function editUrlOptions() {
00944                 $options = array( 'action' => 'edit' );
00945 
00946                 if ( !$this->isRevisionCurrent() ) {
00947                         $options['oldid'] = intval( $this->getRevisionId() );
00948                 }
00949 
00950                 return $options;
00951         }
00952 
00957         function showEmailUser( $id ) {
00958                 if ( $id instanceof User ) {
00959                         $targetUser = $id;
00960                 } else {
00961                         $targetUser = User::newFromId( $id );
00962                 }
00963                 return $this->getUser()->canSendEmail() && # the sending user must have a confirmed email address
00964                         $targetUser->canReceiveEmail(); # the target user must have a confirmed email address and allow emails from users
00965         }
00966 
00974         function getCommonStylePath( $name ) {
00975                 global $wgStylePath, $wgStyleVersion;
00976                 return "$wgStylePath/common/$name?$wgStyleVersion";
00977         }
00978 
00986         function getSkinStylePath( $name ) {
00987                 global $wgStylePath, $wgStyleVersion;
00988                 return "$wgStylePath/{$this->stylename}/$name?$wgStyleVersion";
00989         }
00990 
00991         /* these are used extensively in SkinTemplate, but also some other places */
00992 
00997         static function makeMainPageUrl( $urlaction = '' ) {
00998                 $title = Title::newMainPage();
00999                 self::checkTitle( $title, '' );
01000 
01001                 return $title->getLocalURL( $urlaction );
01002         }
01003 
01015         static function makeSpecialUrl( $name, $urlaction = '', $proto = null ) {
01016                 $title = SpecialPage::getSafeTitleFor( $name );
01017                 if( is_null( $proto ) ) {
01018                         return $title->getLocalURL( $urlaction );
01019                 } else {
01020                         return $title->getFullURL( $urlaction, false, $proto );
01021                 }
01022         }
01023 
01030         static function makeSpecialUrlSubpage( $name, $subpage, $urlaction = '' ) {
01031                 $title = SpecialPage::getSafeTitleFor( $name, $subpage );
01032                 return $title->getLocalURL( $urlaction );
01033         }
01034 
01040         static function makeI18nUrl( $name, $urlaction = '' ) {
01041                 $title = Title::newFromText( wfMessage( $name )->inContentLanguage()->text() );
01042                 self::checkTitle( $title, $name );
01043                 return $title->getLocalURL( $urlaction );
01044         }
01045 
01051         static function makeUrl( $name, $urlaction = '' ) {
01052                 $title = Title::newFromText( $name );
01053                 self::checkTitle( $title, $name );
01054 
01055                 return $title->getLocalURL( $urlaction );
01056         }
01057 
01064         static function makeInternalOrExternalUrl( $name ) {
01065                 if ( preg_match( '/^(?i:' . wfUrlProtocols() . ')/', $name ) ) {
01066                         return $name;
01067                 } else {
01068                         return self::makeUrl( $name );
01069                 }
01070         }
01071 
01079         static function makeNSUrl( $name, $urlaction = '', $namespace = NS_MAIN ) {
01080                 $title = Title::makeTitleSafe( $namespace, $name );
01081                 self::checkTitle( $title, $name );
01082 
01083                 return $title->getLocalURL( $urlaction );
01084         }
01085 
01092         static function makeUrlDetails( $name, $urlaction = '' ) {
01093                 $title = Title::newFromText( $name );
01094                 self::checkTitle( $title, $name );
01095 
01096                 return array(
01097                         'href' => $title->getLocalURL( $urlaction ),
01098                         'exists' => $title->getArticleID() != 0,
01099                 );
01100         }
01101 
01108         static function makeKnownUrlDetails( $name, $urlaction = '' ) {
01109                 $title = Title::newFromText( $name );
01110                 self::checkTitle( $title, $name );
01111 
01112                 return array(
01113                         'href' => $title->getLocalURL( $urlaction ),
01114                         'exists' => true
01115                 );
01116         }
01117 
01124         static function checkTitle( &$title, $name ) {
01125                 if ( !is_object( $title ) ) {
01126                         $title = Title::newFromText( $name );
01127                         if ( !is_object( $title ) ) {
01128                                 $title = Title::newFromText( '--error: link target missing--' );
01129                         }
01130                 }
01131         }
01132 
01154         function buildSidebar() {
01155                 global $wgMemc, $wgEnableSidebarCache, $wgSidebarCacheExpiry;
01156                 wfProfileIn( __METHOD__ );
01157 
01158                 $key = wfMemcKey( 'sidebar', $this->getLanguage()->getCode() );
01159 
01160                 if ( $wgEnableSidebarCache ) {
01161                         $cachedsidebar = $wgMemc->get( $key );
01162                         if ( $cachedsidebar ) {
01163                                 wfProfileOut( __METHOD__ );
01164                                 return $cachedsidebar;
01165                         }
01166                 }
01167 
01168                 $bar = array();
01169                 $this->addToSidebar( $bar, 'sidebar' );
01170 
01171                 wfRunHooks( 'SkinBuildSidebar', array( $this, &$bar ) );
01172                 if ( $wgEnableSidebarCache ) {
01173                         $wgMemc->set( $key, $bar, $wgSidebarCacheExpiry );
01174                 }
01175 
01176                 wfProfileOut( __METHOD__ );
01177                 return $bar;
01178         }
01188         function addToSidebar( &$bar, $message ) {
01189                 $this->addToSidebarPlain( $bar, wfMessage( $message )->inContentLanguage()->plain() );
01190         }
01191 
01199         function addToSidebarPlain( &$bar, $text ) {
01200                 $lines = explode( "\n", $text );
01201 
01202                 $heading = '';
01203 
01204                 foreach ( $lines as $line ) {
01205                         if ( strpos( $line, '*' ) !== 0 ) {
01206                                 continue;
01207                         }
01208                         $line = rtrim( $line, "\r" ); // for Windows compat
01209 
01210                         if ( strpos( $line, '**' ) !== 0 ) {
01211                                 $heading = trim( $line, '* ' );
01212                                 if ( !array_key_exists( $heading, $bar ) ) {
01213                                         $bar[$heading] = array();
01214                                 }
01215                         } else {
01216                                 $line = trim( $line, '* ' );
01217 
01218                                 if ( strpos( $line, '|' ) !== false ) { // sanity check
01219                                         $line = MessageCache::singleton()->transform( $line, false, null, $this->getTitle() );
01220                                         $line = array_map( 'trim', explode( '|', $line, 2 ) );
01221                                         if ( count( $line ) !== 2 ) {
01222                                                 // Second sanity check, could be hit by people doing
01223                                                 // funky stuff with parserfuncs... (bug 33321)
01224                                                 continue;
01225                                         }
01226 
01227                                         $extraAttribs = array();
01228 
01229                                         $msgLink = $this->msg( $line[0] )->inContentLanguage();
01230                                         if ( $msgLink->exists() ) {
01231                                                 $link = $msgLink->text();
01232                                                 if ( $link == '-' ) {
01233                                                         continue;
01234                                                 }
01235                                         } else {
01236                                                 $link = $line[0];
01237                                         }
01238                                         $msgText = $this->msg( $line[1] );
01239                                         if ( $msgText->exists() ) {
01240                                                 $text = $msgText->text();
01241                                         } else {
01242                                                 $text = $line[1];
01243                                         }
01244 
01245                                         if ( preg_match( '/^(?i:' . wfUrlProtocols() . ')/', $link ) ) {
01246                                                 $href = $link;
01247 
01248                                                 // Parser::getExternalLinkAttribs won't work here because of the Namespace things
01249                                                 global $wgNoFollowLinks, $wgNoFollowDomainExceptions;
01250                                                 if ( $wgNoFollowLinks && !wfMatchesDomainList( $href, $wgNoFollowDomainExceptions ) ) {
01251                                                         $extraAttribs['rel'] = 'nofollow';
01252                                                 }
01253 
01254                                                 global $wgExternalLinkTarget;
01255                                                 if ( $wgExternalLinkTarget) {
01256                                                         $extraAttribs['target'] = $wgExternalLinkTarget;
01257                                                 }
01258                                         } else {
01259                                                 $title = Title::newFromText( $link );
01260 
01261                                                 if ( $title ) {
01262                                                         $title = $title->fixSpecialName();
01263                                                         $href = $title->getLinkURL();
01264                                                 } else {
01265                                                         $href = 'INVALID-TITLE';
01266                                                 }
01267                                         }
01268 
01269                                         $bar[$heading][] = array_merge( array(
01270                                                 'text' => $text,
01271                                                 'href' => $href,
01272                                                 'id' => 'n-' . Sanitizer::escapeId( strtr( $line[1], ' ', '-' ), 'noninitial' ),
01273                                                 'active' => false
01274                                         ), $extraAttribs );
01275                                 } else {
01276                                         continue;
01277                                 }
01278                         }
01279                 }
01280 
01281                 return $bar;
01282         }
01283 
01291         public function commonPrintStylesheet() {
01292                 return true;
01293         }
01294 
01299         function getNewtalks() {
01300                 $out = $this->getOutput();
01301 
01302                 $newtalks = $this->getUser()->getNewMessageLinks();
01303                 $ntl = '';
01304 
01305                 if ( count( $newtalks ) == 1 && $newtalks[0]['wiki'] === wfWikiID() ) {
01306                         $uTalkTitle = $this->getUser()->getTalkPage();
01307 
01308                         if ( !$uTalkTitle->equals( $out->getTitle() ) ) {
01309                                 $lastSeenRev = isset( $newtalks[0]['rev'] ) ? $newtalks[0]['rev'] : null;
01310                                 $nofAuthors = 0;
01311                                 if ( $lastSeenRev !== null ) {
01312                                         $plural = true; // Default if we have a last seen revision: if unknown, use plural
01313                                         $latestRev = Revision::newFromTitle( $uTalkTitle, false, Revision::READ_NORMAL );
01314                                         if ( $latestRev !== null ) {
01315                                                 // Singular if only 1 unseen revision, plural if several unseen revisions.
01316                                                 $plural = $latestRev->getParentId() !== $lastSeenRev->getId();
01317                                                 $nofAuthors = $uTalkTitle->countAuthorsBetween(
01318                                                         $lastSeenRev, $latestRev, 10, 'include_new' );
01319                                         }
01320                                 } else {
01321                                         // Singular if no revision -> diff link will show latest change only in any case
01322                                         $plural = false;
01323                                 }
01324                                 $plural = $plural ? 2 : 1;
01325                                 // 2 signifies "more than one revision". We don't know how many, and even if we did,
01326                                 // the number of revisions or authors is not necessarily the same as the number of
01327                                 // "messages".
01328                                 $newMessagesLink = Linker::linkKnown(
01329                                         $uTalkTitle,
01330                                         $this->msg( 'newmessageslinkplural' )->params( $plural )->escaped(),
01331                                         array(),
01332                                         array( 'redirect' => 'no' )
01333                                 );
01334 
01335                                 $newMessagesDiffLink = Linker::linkKnown(
01336                                         $uTalkTitle,
01337                                         $this->msg( 'newmessagesdifflinkplural' )->params( $plural )->escaped(),
01338                                         array(),
01339                                         $lastSeenRev !== null
01340                                                 ? array( 'oldid' => $lastSeenRev->getId(), 'diff' => 'cur' )
01341                                                 : array( 'diff' => 'cur' )
01342                                 );
01343 
01344                                 if ( $nofAuthors >= 1 && $nofAuthors <= 10 ) {
01345                                         $ntl = $this->msg(
01346                                                 'youhavenewmessagesfromusers',
01347                                                 $newMessagesLink,
01348                                                 $newMessagesDiffLink
01349                                         )->numParams( $nofAuthors );
01350                                 } else {
01351                                         // $nofAuthors === 11 signifies "11 or more" ("more than 10")
01352                                         $ntl = $this->msg(
01353                                                 $nofAuthors > 10 ? 'youhavenewmessagesmanyusers' : 'youhavenewmessages',
01354                                                 $newMessagesLink,
01355                                                 $newMessagesDiffLink
01356                                         );
01357                                 }
01358                                 $ntl = $ntl->text();
01359                                 # Disable Squid cache
01360                                 $out->setSquidMaxage( 0 );
01361                         }
01362                 } elseif ( count( $newtalks ) ) {
01363                         // _>" " for BC <= 1.16
01364                         $sep = str_replace( '_', ' ', $this->msg( 'newtalkseparator' )->escaped() );
01365                         $msgs = array();
01366 
01367                         foreach ( $newtalks as $newtalk ) {
01368                                 $msgs[] = Xml::element(
01369                                         'a',
01370                                         array( 'href' => $newtalk['link'] ), $newtalk['wiki']
01371                                 );
01372                         }
01373                         $parts = implode( $sep, $msgs );
01374                         $ntl = $this->msg( 'youhavenewmessagesmulti' )->rawParams( $parts )->escaped();
01375                         $out->setSquidMaxage( 0 );
01376                 }
01377 
01378                 return $ntl;
01379         }
01380 
01387         private function getCachedNotice( $name ) {
01388                 global $wgRenderHashAppend, $parserMemc, $wgContLang;
01389 
01390                 wfProfileIn( __METHOD__ );
01391 
01392                 $needParse = false;
01393 
01394                 if( $name === 'default' ) {
01395                         // special case
01396                         global $wgSiteNotice;
01397                         $notice = $wgSiteNotice;
01398                         if( empty( $notice ) ) {
01399                                 wfProfileOut( __METHOD__ );
01400                                 return false;
01401                         }
01402                 } else {
01403                         $msg = $this->msg( $name )->inContentLanguage();
01404                         if( $msg->isDisabled() ) {
01405                                 wfProfileOut( __METHOD__ );
01406                                 return false;
01407                         }
01408                         $notice = $msg->plain();
01409                 }
01410 
01411                 // Use the extra hash appender to let eg SSL variants separately cache.
01412                 $key = wfMemcKey( $name . $wgRenderHashAppend );
01413                 $cachedNotice = $parserMemc->get( $key );
01414                 if( is_array( $cachedNotice ) ) {
01415                         if( md5( $notice ) == $cachedNotice['hash'] ) {
01416                                 $notice = $cachedNotice['html'];
01417                         } else {
01418                                 $needParse = true;
01419                         }
01420                 } else {
01421                         $needParse = true;
01422                 }
01423 
01424                 if ( $needParse ) {
01425                         $parsed = $this->getOutput()->parse( $notice );
01426                         $parserMemc->set( $key, array( 'html' => $parsed, 'hash' => md5( $notice ) ), 600 );
01427                         $notice = $parsed;
01428                 }
01429 
01430                 $notice = Html::rawElement( 'div', array( 'id' => 'localNotice',
01431                         'lang' => $wgContLang->getHtmlCode(), 'dir' => $wgContLang->getDir() ), $notice );
01432                 wfProfileOut( __METHOD__ );
01433                 return $notice;
01434         }
01435 
01441         function getNamespaceNotice() {
01442                 wfProfileIn( __METHOD__ );
01443 
01444                 $key = 'namespacenotice-' . $this->getTitle()->getNsText();
01445                 $namespaceNotice = $this->getCachedNotice( $key );
01446                 if ( $namespaceNotice && substr( $namespaceNotice, 0, 7 ) != '<p>&lt;' ) {
01447                         $namespaceNotice = '<div id="namespacebanner">' . $namespaceNotice . '</div>';
01448                 } else {
01449                         $namespaceNotice = '';
01450                 }
01451 
01452                 wfProfileOut( __METHOD__ );
01453                 return $namespaceNotice;
01454         }
01455 
01461         function getSiteNotice() {
01462                 wfProfileIn( __METHOD__ );
01463                 $siteNotice = '';
01464 
01465                 if ( wfRunHooks( 'SiteNoticeBefore', array( &$siteNotice, $this ) ) ) {
01466                         if ( is_object( $this->getUser() ) && $this->getUser()->isLoggedIn() ) {
01467                                 $siteNotice = $this->getCachedNotice( 'sitenotice' );
01468                         } else {
01469                                 $anonNotice = $this->getCachedNotice( 'anonnotice' );
01470                                 if ( !$anonNotice ) {
01471                                         $siteNotice = $this->getCachedNotice( 'sitenotice' );
01472                                 } else {
01473                                         $siteNotice = $anonNotice;
01474                                 }
01475                         }
01476                         if ( !$siteNotice ) {
01477                                 $siteNotice = $this->getCachedNotice( 'default' );
01478                         }
01479                 }
01480 
01481                 wfRunHooks( 'SiteNoticeAfter', array( &$siteNotice, $this ) );
01482                 wfProfileOut( __METHOD__ );
01483                 return $siteNotice;
01484         }
01485 
01499         public function doEditSectionLink( Title $nt, $section, $tooltip = null, $lang = false ) {
01500                 // HTML generated here should probably have userlangattributes
01501                 // added to it for LTR text on RTL pages
01502 
01503                 $lang = wfGetLangObj( $lang );
01504 
01505                 $attribs = array();
01506                 if ( !is_null( $tooltip ) ) {
01507                         # Bug 25462: undo double-escaping.
01508                         $tooltip = Sanitizer::decodeCharReferences( $tooltip );
01509                         $attribs['title'] = wfMessage( 'editsectionhint' )->rawParams( $tooltip )
01510                                 ->inLanguage( $lang )->text();
01511                 }
01512                 $link = Linker::link( $nt, wfMessage( 'editsection' )->inLanguage( $lang )->text(),
01513                         $attribs,
01514                         array( 'action' => 'edit', 'section' => $section ),
01515                         array( 'noclasses', 'known' )
01516                 );
01517 
01518                 # Run the old hook.  This takes up half of the function . . . hopefully
01519                 # we can rid of it someday.
01520                 $attribs = '';
01521                 if ( $tooltip ) {
01522                         $attribs = wfMessage( 'editsectionhint' )->rawParams( $tooltip )
01523                                 ->inLanguage( $lang )->escaped();
01524                         $attribs = " title=\"$attribs\"";
01525                 }
01526                 $result = null;
01527                 wfRunHooks( 'EditSectionLink', array( &$this, $nt, $section, $attribs, $link, &$result, $lang ) );
01528                 if ( !is_null( $result ) ) {
01529                         # For reverse compatibility, add the brackets *after* the hook is
01530                         # run, and even add them to hook-provided text.  (This is the main
01531                         # reason that the EditSectionLink hook is deprecated in favor of
01532                         # DoEditSectionLink: it can't change the brackets or the span.)
01533                         $result = wfMessage( 'editsection-brackets' )->rawParams( $result )
01534                                 ->inLanguage( $lang )->escaped();
01535                         return "<span class=\"editsection\">$result</span>";
01536                 }
01537 
01538                 # Add the brackets and the span, and *then* run the nice new hook, with
01539                 # clean and non-redundant arguments.
01540                 $result = wfMessage( 'editsection-brackets' )->rawParams( $link )
01541                         ->inLanguage( $lang )->escaped();
01542                 $result = "<span class=\"editsection\">$result</span>";
01543 
01544                 wfRunHooks( 'DoEditSectionLink', array( $this, $nt, $section, $tooltip, &$result, $lang ) );
01545                 return $result;
01546         }
01547 
01557         function __call( $fname, $args ) {
01558                 $realFunction = array( 'Linker', $fname );
01559                 if ( is_callable( $realFunction ) ) {
01560                         return call_user_func_array( $realFunction, $args );
01561                 } else {
01562                         $className = get_class( $this );
01563                         throw new MWException( "Call to undefined method $className::$fname" );
01564                 }
01565         }
01566 
01567 }