MediaWiki  REL1_24
Skin.php
Go to the documentation of this file.
00001 <?php
00034 abstract class Skin extends ContextSource {
00035     protected $skinname = null;
00036     protected $mRelevantTitle = null;
00037     protected $mRelevantUser = null;
00038 
00043     public $stylename = null;
00044 
00049     static function getSkinNames() {
00050         return SkinFactory::getDefaultInstance()->getSkinNames();
00051     }
00052 
00057     static function getSkinNameMessages() {
00058         $messages = array();
00059         foreach ( self::getSkinNames() as $skinKey => $skinName ) {
00060             $messages[] = "skinname-$skinKey";
00061         }
00062         return $messages;
00063     }
00064 
00072     public static function getAllowedSkins() {
00073         global $wgSkipSkins;
00074 
00075         $allowedSkins = self::getSkinNames();
00076 
00077         foreach ( $wgSkipSkins as $skip ) {
00078             unset( $allowedSkins[$skip] );
00079         }
00080 
00081         return $allowedSkins;
00082     }
00083 
00088     public static function getUsableSkins() {
00089         wfDeprecated( __METHOD__, '1.23' );
00090         return self::getAllowedSkins();
00091     }
00092 
00102     static function normalizeKey( $key ) {
00103         global $wgDefaultSkin, $wgFallbackSkin;
00104 
00105         $skinNames = Skin::getSkinNames();
00106 
00107         // Make keys lowercase for case-insensitive matching.
00108         $skinNames = array_change_key_case( $skinNames, CASE_LOWER );
00109         $key = strtolower( $key );
00110         $defaultSkin = strtolower( $wgDefaultSkin );
00111         $fallbackSkin = strtolower( $wgFallbackSkin );
00112 
00113         if ( $key == '' || $key == 'default' ) {
00114             // Don't return the default immediately;
00115             // in a misconfiguration we need to fall back.
00116             $key = $defaultSkin;
00117         }
00118 
00119         if ( isset( $skinNames[$key] ) ) {
00120             return $key;
00121         }
00122 
00123         // Older versions of the software used a numeric setting
00124         // in the user preferences.
00125         $fallback = array(
00126             0 => $defaultSkin,
00127             2 => 'cologneblue'
00128         );
00129 
00130         if ( isset( $fallback[$key] ) ) {
00131             $key = $fallback[$key];
00132         }
00133 
00134         if ( isset( $skinNames[$key] ) ) {
00135             return $key;
00136         } elseif ( isset( $skinNames[$defaultSkin] ) ) {
00137             return $defaultSkin;
00138         } else {
00139             return $fallbackSkin;
00140         }
00141     }
00142 
00149     static function &newFromKey( $key ) {
00150         wfDeprecated( __METHOD__, '1.24' );
00151 
00152         $key = Skin::normalizeKey( $key );
00153         $factory = SkinFactory::getDefaultInstance();
00154 
00155         // normalizeKey() guarantees that a skin with this key will exist.
00156         $skin = $factory->makeSkin( $key );
00157         return $skin;
00158     }
00159 
00163     public function getSkinName() {
00164         return $this->skinname;
00165     }
00166 
00170     function initPage( OutputPage $out ) {
00171         wfProfileIn( __METHOD__ );
00172 
00173         $this->preloadExistence();
00174 
00175         wfProfileOut( __METHOD__ );
00176     }
00177 
00184     public function getDefaultModules() {
00185         global $wgIncludeLegacyJavaScript, $wgPreloadJavaScriptMwUtil, $wgUseAjax,
00186             $wgAjaxWatch, $wgEnableAPI, $wgEnableWriteAPI;
00187 
00188         $out = $this->getOutput();
00189         $user = $out->getUser();
00190         $modules = array(
00191             // modules that enhance the page content in some way
00192             'content' => array(
00193                 'mediawiki.page.ready',
00194             ),
00195             // modules that exist for legacy reasons
00196             'legacy' => array(),
00197             // modules relating to search functionality
00198             'search' => array(),
00199             // modules relating to functionality relating to watching an article
00200             'watch' => array(),
00201             // modules which relate to the current users preferences
00202             'user' => array(),
00203         );
00204         if ( $wgIncludeLegacyJavaScript ) {
00205             $modules['legacy'][] = 'mediawiki.legacy.wikibits';
00206         }
00207 
00208         if ( $wgPreloadJavaScriptMwUtil ) {
00209             $modules['legacy'][] = 'mediawiki.util';
00210         }
00211 
00212         // Add various resources if required
00213         if ( $wgUseAjax ) {
00214             $modules['legacy'][] = 'mediawiki.legacy.ajax';
00215 
00216             if ( $wgEnableAPI ) {
00217                 if ( $wgEnableWriteAPI && $wgAjaxWatch && $user->isLoggedIn()
00218                     && $user->isAllowed( 'writeapi' )
00219                 ) {
00220                     $modules['watch'][] = 'mediawiki.page.watch.ajax';
00221                 }
00222 
00223                 $modules['search'][] = 'mediawiki.searchSuggest';
00224             }
00225         }
00226 
00227         if ( $user->getBoolOption( 'editsectiononrightclick' ) ) {
00228             $modules['user'][] = 'mediawiki.action.view.rightClickEdit';
00229         }
00230 
00231         // Crazy edit-on-double-click stuff
00232         if ( $out->isArticle() && $user->getOption( 'editondblclick' ) ) {
00233             $modules['user'][] = 'mediawiki.action.view.dblClickEdit';
00234         }
00235         return $modules;
00236     }
00237 
00241     function preloadExistence() {
00242         $user = $this->getUser();
00243 
00244         // User/talk link
00245         $titles = array( $user->getUserPage(), $user->getTalkPage() );
00246 
00247         // Other tab link
00248         if ( $this->getTitle()->isSpecialPage() ) {
00249             // nothing
00250         } elseif ( $this->getTitle()->isTalkPage() ) {
00251             $titles[] = $this->getTitle()->getSubjectPage();
00252         } else {
00253             $titles[] = $this->getTitle()->getTalkPage();
00254         }
00255 
00256         wfRunHooks( 'SkinPreloadExistence', array( &$titles, $this ) );
00257 
00258         $lb = new LinkBatch( $titles );
00259         $lb->setCaller( __METHOD__ );
00260         $lb->execute();
00261     }
00262 
00268     public function getRevisionId() {
00269         return $this->getOutput()->getRevisionId();
00270     }
00271 
00277     public function isRevisionCurrent() {
00278         $revID = $this->getRevisionId();
00279         return $revID == 0 || $revID == $this->getTitle()->getLatestRevID();
00280     }
00281 
00287     public function setRelevantTitle( $t ) {
00288         $this->mRelevantTitle = $t;
00289     }
00290 
00301     public function getRelevantTitle() {
00302         if ( isset( $this->mRelevantTitle ) ) {
00303             return $this->mRelevantTitle;
00304         }
00305         return $this->getTitle();
00306     }
00307 
00313     public function setRelevantUser( $u ) {
00314         $this->mRelevantUser = $u;
00315     }
00316 
00325     public function getRelevantUser() {
00326         if ( isset( $this->mRelevantUser ) ) {
00327             return $this->mRelevantUser;
00328         }
00329         $title = $this->getRelevantTitle();
00330         if ( $title->hasSubjectNamespace( NS_USER ) ) {
00331             $rootUser = $title->getRootText();
00332             if ( User::isIP( $rootUser ) ) {
00333                 $this->mRelevantUser = User::newFromName( $rootUser, false );
00334             } else {
00335                 $user = User::newFromName( $rootUser, false );
00336                 if ( $user && $user->isLoggedIn() ) {
00337                     $this->mRelevantUser = $user;
00338                 }
00339             }
00340             return $this->mRelevantUser;
00341         }
00342         return null;
00343     }
00344 
00349     abstract function outputPage( OutputPage $out = null );
00350 
00355     static function makeVariablesScript( $data ) {
00356         if ( $data ) {
00357             return Html::inlineScript(
00358                 ResourceLoader::makeLoaderConditionalScript( ResourceLoader::makeConfigSetScript( $data ) )
00359             );
00360         } else {
00361             return '';
00362         }
00363     }
00364 
00370     public static function getDynamicStylesheetQuery() {
00371         global $wgSquidMaxage;
00372 
00373         return array(
00374                 'action' => 'raw',
00375                 'maxage' => $wgSquidMaxage,
00376                 'usemsgcache' => 'yes',
00377                 'ctype' => 'text/css',
00378                 'smaxage' => $wgSquidMaxage,
00379             );
00380     }
00381 
00390     abstract function setupSkinUserCss( OutputPage $out );
00391 
00397     function getPageClasses( $title ) {
00398         $numeric = 'ns-' . $title->getNamespace();
00399 
00400         if ( $title->isSpecialPage() ) {
00401             $type = 'ns-special';
00402             // bug 23315: provide a class based on the canonical special page name without subpages
00403             list( $canonicalName ) = SpecialPageFactory::resolveAlias( $title->getDBkey() );
00404             if ( $canonicalName ) {
00405                 $type .= ' ' . Sanitizer::escapeClass( "mw-special-$canonicalName" );
00406             } else {
00407                 $type .= ' mw-invalidspecialpage';
00408             }
00409         } elseif ( $title->isTalkPage() ) {
00410             $type = 'ns-talk';
00411         } else {
00412             $type = 'ns-subject';
00413         }
00414 
00415         $name = Sanitizer::escapeClass( 'page-' . $title->getPrefixedText() );
00416 
00417         return "$numeric $type $name";
00418     }
00419 
00424     public function getHtmlElementAttributes() {
00425         $lang = $this->getLanguage();
00426         return array(
00427             'lang' => $lang->getHtmlCode(),
00428             'dir' => $lang->getDir(),
00429             'class' => 'client-nojs',
00430         );
00431     }
00432 
00440     function addToBodyAttributes( $out, &$bodyAttrs ) {
00441         // does nothing by default
00442     }
00443 
00448     function getLogo() {
00449         global $wgLogo;
00450         return $wgLogo;
00451     }
00452 
00456     function getCategoryLinks() {
00457         global $wgUseCategoryBrowser;
00458 
00459         $out = $this->getOutput();
00460         $allCats = $out->getCategoryLinks();
00461 
00462         if ( !count( $allCats ) ) {
00463             return '';
00464         }
00465 
00466         $embed = "<li>";
00467         $pop = "</li>";
00468 
00469         $s = '';
00470         $colon = $this->msg( 'colon-separator' )->escaped();
00471 
00472         if ( !empty( $allCats['normal'] ) ) {
00473             $t = $embed . implode( "{$pop}{$embed}", $allCats['normal'] ) . $pop;
00474 
00475             $msg = $this->msg( 'pagecategories' )->numParams( count( $allCats['normal'] ) )->escaped();
00476             $linkPage = wfMessage( 'pagecategorieslink' )->inContentLanguage()->text();
00477             $s .= '<div id="mw-normal-catlinks" class="mw-normal-catlinks">' .
00478                 Linker::link( Title::newFromText( $linkPage ), $msg )
00479                 . $colon . '<ul>' . $t . '</ul>' . '</div>';
00480         }
00481 
00482         # Hidden categories
00483         if ( isset( $allCats['hidden'] ) ) {
00484             if ( $this->getUser()->getBoolOption( 'showhiddencats' ) ) {
00485                 $class = ' mw-hidden-cats-user-shown';
00486             } elseif ( $this->getTitle()->getNamespace() == NS_CATEGORY ) {
00487                 $class = ' mw-hidden-cats-ns-shown';
00488             } else {
00489                 $class = ' mw-hidden-cats-hidden';
00490             }
00491 
00492             $s .= "<div id=\"mw-hidden-catlinks\" class=\"mw-hidden-catlinks$class\">" .
00493                 $this->msg( 'hidden-categories' )->numParams( count( $allCats['hidden'] ) )->escaped() .
00494                 $colon . '<ul>' . $embed . implode( "{$pop}{$embed}", $allCats['hidden'] ) . $pop . '</ul>' .
00495                 '</div>';
00496         }
00497 
00498         # optional 'dmoz-like' category browser. Will be shown under the list
00499         # of categories an article belong to
00500         if ( $wgUseCategoryBrowser ) {
00501             $s .= '<br /><hr />';
00502 
00503             # get a big array of the parents tree
00504             $parenttree = $this->getTitle()->getParentCategoryTree();
00505             # Skin object passed by reference cause it can not be
00506             # accessed under the method subfunction drawCategoryBrowser
00507             $tempout = explode( "\n", $this->drawCategoryBrowser( $parenttree ) );
00508             # Clean out bogus first entry and sort them
00509             unset( $tempout[0] );
00510             asort( $tempout );
00511             # Output one per line
00512             $s .= implode( "<br />\n", $tempout );
00513         }
00514 
00515         return $s;
00516     }
00517 
00523     function drawCategoryBrowser( $tree ) {
00524         $return = '';
00525 
00526         foreach ( $tree as $element => $parent ) {
00527             if ( empty( $parent ) ) {
00528                 # element start a new list
00529                 $return .= "\n";
00530             } else {
00531                 # grab the others elements
00532                 $return .= $this->drawCategoryBrowser( $parent ) . ' &gt; ';
00533             }
00534 
00535             # add our current element to the list
00536             $eltitle = Title::newFromText( $element );
00537             $return .= Linker::link( $eltitle, htmlspecialchars( $eltitle->getText() ) );
00538         }
00539 
00540         return $return;
00541     }
00542 
00546     function getCategories() {
00547         $out = $this->getOutput();
00548 
00549         $catlinks = $this->getCategoryLinks();
00550 
00551         $classes = 'catlinks';
00552 
00553         // Check what we're showing
00554         $allCats = $out->getCategoryLinks();
00555         $showHidden = $this->getUser()->getBoolOption( 'showhiddencats' ) ||
00556                         $this->getTitle()->getNamespace() == NS_CATEGORY;
00557 
00558         if ( empty( $allCats['normal'] ) && !( !empty( $allCats['hidden'] ) && $showHidden ) ) {
00559             $classes .= ' catlinks-allhidden';
00560         }
00561 
00562         return "<div id='catlinks' class='$classes'>{$catlinks}</div>";
00563     }
00564 
00579     protected function afterContentHook() {
00580         $data = '';
00581 
00582         if ( wfRunHooks( 'SkinAfterContent', array( &$data, $this ) ) ) {
00583             // adding just some spaces shouldn't toggle the output
00584             // of the whole <div/>, so we use trim() here
00585             if ( trim( $data ) != '' ) {
00586                 // Doing this here instead of in the skins to
00587                 // ensure that the div has the same ID in all
00588                 // skins
00589                 $data = "<div id='mw-data-after-content'>\n" .
00590                     "\t$data\n" .
00591                     "</div>\n";
00592             }
00593         } else {
00594             wfDebug( "Hook SkinAfterContent changed output processing.\n" );
00595         }
00596 
00597         return $data;
00598     }
00599 
00605     protected function generateDebugHTML() {
00606         return MWDebug::getHTMLDebugLog();
00607     }
00608 
00614     function bottomScripts() {
00615         // TODO and the suckage continues. This function is really just a wrapper around
00616         // OutputPage::getBottomScripts() which takes a Skin param. This should be cleaned
00617         // up at some point
00618         $bottomScriptText = $this->getOutput()->getBottomScripts();
00619         wfRunHooks( 'SkinAfterBottomScripts', array( $this, &$bottomScriptText ) );
00620 
00621         return $bottomScriptText;
00622     }
00623 
00630     function printSource() {
00631         $oldid = $this->getRevisionId();
00632         if ( $oldid ) {
00633             $canonicalUrl = $this->getTitle()->getCanonicalURL( 'oldid=' . $oldid );
00634             $url = htmlspecialchars( wfExpandIRI( $canonicalUrl ) );
00635         } else {
00636             // oldid not available for non existing pages
00637             $url = htmlspecialchars( wfExpandIRI( $this->getTitle()->getCanonicalURL() ) );
00638         }
00639 
00640         return $this->msg( 'retrievedfrom', '<a dir="ltr" href="' . $url
00641             . '">' . $url . '</a>' )->text();
00642     }
00643 
00647     function getUndeleteLink() {
00648         $action = $this->getRequest()->getVal( 'action', 'view' );
00649 
00650         if ( $this->getUser()->isAllowed( 'deletedhistory' ) &&
00651             ( $this->getTitle()->getArticleID() == 0 || $action == 'history' ) ) {
00652             $n = $this->getTitle()->isDeleted();
00653 
00654             if ( $n ) {
00655                 if ( $this->getUser()->isAllowed( 'undelete' ) ) {
00656                     $msg = 'thisisdeleted';
00657                 } else {
00658                     $msg = 'viewdeleted';
00659                 }
00660 
00661                 return $this->msg( $msg )->rawParams(
00662                     Linker::linkKnown(
00663                         SpecialPage::getTitleFor( 'Undelete', $this->getTitle()->getPrefixedDBkey() ),
00664                         $this->msg( 'restorelink' )->numParams( $n )->escaped() )
00665                     )->text();
00666             }
00667         }
00668 
00669         return '';
00670     }
00671 
00675     function subPageSubtitle() {
00676         $out = $this->getOutput();
00677         $subpages = '';
00678 
00679         if ( !wfRunHooks( 'SkinSubPageSubtitle', array( &$subpages, $this, $out ) ) ) {
00680             return $subpages;
00681         }
00682 
00683         if ( $out->isArticle() && MWNamespace::hasSubpages( $out->getTitle()->getNamespace() ) ) {
00684             $ptext = $this->getTitle()->getPrefixedText();
00685             if ( preg_match( '/\//', $ptext ) ) {
00686                 $links = explode( '/', $ptext );
00687                 array_pop( $links );
00688                 $c = 0;
00689                 $growinglink = '';
00690                 $display = '';
00691                 $lang = $this->getLanguage();
00692 
00693                 foreach ( $links as $link ) {
00694                     $growinglink .= $link;
00695                     $display .= $link;
00696                     $linkObj = Title::newFromText( $growinglink );
00697 
00698                     if ( is_object( $linkObj ) && $linkObj->isKnown() ) {
00699                         $getlink = Linker::linkKnown(
00700                             $linkObj,
00701                             htmlspecialchars( $display )
00702                         );
00703 
00704                         $c++;
00705 
00706                         if ( $c > 1 ) {
00707                             $subpages .= $lang->getDirMarkEntity() . $this->msg( 'pipe-separator' )->escaped();
00708                         } else {
00709                             $subpages .= '&lt; ';
00710                         }
00711 
00712                         $subpages .= $getlink;
00713                         $display = '';
00714                     } else {
00715                         $display .= '/';
00716                     }
00717                     $growinglink .= '/';
00718                 }
00719             }
00720         }
00721 
00722         return $subpages;
00723     }
00724 
00729     function showIPinHeader() {
00730         global $wgShowIPinHeader;
00731         return $wgShowIPinHeader && session_id() != '';
00732     }
00733 
00737     function getSearchLink() {
00738         $searchPage = SpecialPage::getTitleFor( 'Search' );
00739         return $searchPage->getLocalURL();
00740     }
00741 
00745     function escapeSearchLink() {
00746         return htmlspecialchars( $this->getSearchLink() );
00747     }
00748 
00753     function getCopyright( $type = 'detect' ) {
00754         global $wgRightsPage, $wgRightsUrl, $wgRightsText;
00755 
00756         if ( $type == 'detect' ) {
00757             if ( !$this->isRevisionCurrent()
00758                 && !$this->msg( 'history_copyright' )->inContentLanguage()->isDisabled()
00759             ) {
00760                 $type = 'history';
00761             } else {
00762                 $type = 'normal';
00763             }
00764         }
00765 
00766         if ( $type == 'history' ) {
00767             $msg = 'history_copyright';
00768         } else {
00769             $msg = 'copyright';
00770         }
00771 
00772         if ( $wgRightsPage ) {
00773             $title = Title::newFromText( $wgRightsPage );
00774             $link = Linker::linkKnown( $title, $wgRightsText );
00775         } elseif ( $wgRightsUrl ) {
00776             $link = Linker::makeExternalLink( $wgRightsUrl, $wgRightsText );
00777         } elseif ( $wgRightsText ) {
00778             $link = $wgRightsText;
00779         } else {
00780             # Give up now
00781             return '';
00782         }
00783 
00784         // Allow for site and per-namespace customization of copyright notice.
00785         // @todo Remove deprecated $forContent param from hook handlers and then remove here.
00786         $forContent = true;
00787 
00788         wfRunHooks(
00789             'SkinCopyrightFooter',
00790             array( $this->getTitle(), $type, &$msg, &$link, &$forContent )
00791         );
00792 
00793         return $this->msg( $msg )->rawParams( $link )->text();
00794     }
00795 
00799     function getCopyrightIcon() {
00800         global $wgRightsUrl, $wgRightsText, $wgRightsIcon, $wgCopyrightIcon;
00801 
00802         $out = '';
00803 
00804         if ( $wgCopyrightIcon ) {
00805             $out = $wgCopyrightIcon;
00806         } elseif ( $wgRightsIcon ) {
00807             $icon = htmlspecialchars( $wgRightsIcon );
00808 
00809             if ( $wgRightsUrl ) {
00810                 $url = htmlspecialchars( $wgRightsUrl );
00811                 $out .= '<a href="' . $url . '">';
00812             }
00813 
00814             $text = htmlspecialchars( $wgRightsText );
00815             $out .= "<img src=\"$icon\" alt=\"$text\" width=\"88\" height=\"31\" />";
00816 
00817             if ( $wgRightsUrl ) {
00818                 $out .= '</a>';
00819             }
00820         }
00821 
00822         return $out;
00823     }
00824 
00829     function getPoweredBy() {
00830         global $wgResourceBasePath;
00831 
00832         $url = htmlspecialchars( "$wgResourceBasePath/resources/assets/poweredby_mediawiki_88x31.png" );
00833         $text = '<a href="//www.mediawiki.org/"><img src="' . $url
00834             . '" height="31" width="88" alt="Powered by MediaWiki" /></a>';
00835         wfRunHooks( 'SkinGetPoweredBy', array( &$text, $this ) );
00836         return $text;
00837     }
00838 
00844     protected function lastModified() {
00845         $timestamp = $this->getOutput()->getRevisionTimestamp();
00846 
00847         # No cached timestamp, load it from the database
00848         if ( $timestamp === null ) {
00849             $timestamp = Revision::getTimestampFromId( $this->getTitle(), $this->getRevisionId() );
00850         }
00851 
00852         if ( $timestamp ) {
00853             $d = $this->getLanguage()->userDate( $timestamp, $this->getUser() );
00854             $t = $this->getLanguage()->userTime( $timestamp, $this->getUser() );
00855             $s = ' ' . $this->msg( 'lastmodifiedat', $d, $t )->text();
00856         } else {
00857             $s = '';
00858         }
00859 
00860         if ( wfGetLB()->getLaggedSlaveMode() ) {
00861             $s .= ' <strong>' . $this->msg( 'laggedslavemode' )->text() . '</strong>';
00862         }
00863 
00864         return $s;
00865     }
00866 
00871     function logoText( $align = '' ) {
00872         if ( $align != '' ) {
00873             $a = " style='float: {$align};'";
00874         } else {
00875             $a = '';
00876         }
00877 
00878         $mp = $this->msg( 'mainpage' )->escaped();
00879         $mptitle = Title::newMainPage();
00880         $url = ( is_object( $mptitle ) ? htmlspecialchars( $mptitle->getLocalURL() ) : '' );
00881 
00882         $logourl = $this->getLogo();
00883         $s = "<a href='{$url}'><img{$a} src='{$logourl}' alt='[{$mp}]' /></a>";
00884 
00885         return $s;
00886     }
00887 
00896     function makeFooterIcon( $icon, $withImage = 'withImage' ) {
00897         if ( is_string( $icon ) ) {
00898             $html = $icon;
00899         } else { // Assuming array
00900             $url = isset( $icon["url"] ) ? $icon["url"] : null;
00901             unset( $icon["url"] );
00902             if ( isset( $icon["src"] ) && $withImage === 'withImage' ) {
00903                 // do this the lazy way, just pass icon data as an attribute array
00904                 $html = Html::element( 'img', $icon );
00905             } else {
00906                 $html = htmlspecialchars( $icon["alt"] );
00907             }
00908             if ( $url ) {
00909                 $html = Html::rawElement( 'a', array( "href" => $url ), $html );
00910             }
00911         }
00912         return $html;
00913     }
00914 
00919     function mainPageLink() {
00920         $s = Linker::linkKnown(
00921             Title::newMainPage(),
00922             $this->msg( 'mainpage' )->escaped()
00923         );
00924 
00925         return $s;
00926     }
00927 
00934     public function footerLink( $desc, $page ) {
00935         // if the link description has been set to "-" in the default language,
00936         if ( $this->msg( $desc )->inContentLanguage()->isDisabled() ) {
00937             // then it is disabled, for all languages.
00938             return '';
00939         } else {
00940             // Otherwise, we display the link for the user, described in their
00941             // language (which may or may not be the same as the default language),
00942             // but we make the link target be the one site-wide page.
00943             $title = Title::newFromText( $this->msg( $page )->inContentLanguage()->text() );
00944 
00945             return Linker::linkKnown(
00946                 $title,
00947                 $this->msg( $desc )->escaped()
00948             );
00949         }
00950     }
00951 
00956     function privacyLink() {
00957         return $this->footerLink( 'privacy', 'privacypage' );
00958     }
00959 
00964     function aboutLink() {
00965         return $this->footerLink( 'aboutsite', 'aboutpage' );
00966     }
00967 
00972     function disclaimerLink() {
00973         return $this->footerLink( 'disclaimers', 'disclaimerpage' );
00974     }
00975 
00983     function editUrlOptions() {
00984         $options = array( 'action' => 'edit' );
00985 
00986         if ( !$this->isRevisionCurrent() ) {
00987             $options['oldid'] = intval( $this->getRevisionId() );
00988         }
00989 
00990         return $options;
00991     }
00992 
00997     function showEmailUser( $id ) {
00998         if ( $id instanceof User ) {
00999             $targetUser = $id;
01000         } else {
01001             $targetUser = User::newFromId( $id );
01002         }
01003 
01004         # The sending user must have a confirmed email address and the target
01005         # user must have a confirmed email address and allow emails from users.
01006         return $this->getUser()->canSendEmail() &&
01007             $targetUser->canReceiveEmail();
01008     }
01009 
01020     function getCommonStylePath( $name ) {
01021         wfDeprecated( __METHOD__, '1.24' );
01022         return '';
01023     }
01024 
01035     function getSkinStylePath( $name ) {
01036         global $wgStylePath, $wgStyleVersion;
01037 
01038         if ( $this->stylename === null ) {
01039             $class = get_class( $this );
01040             throw new MWException( "$class::\$stylename must be set to use getSkinStylePath()" );
01041         }
01042 
01043         return "$wgStylePath/{$this->stylename}/$name?$wgStyleVersion";
01044     }
01045 
01046     /* these are used extensively in SkinTemplate, but also some other places */
01047 
01052     static function makeMainPageUrl( $urlaction = '' ) {
01053         $title = Title::newMainPage();
01054         self::checkTitle( $title, '' );
01055 
01056         return $title->getLocalURL( $urlaction );
01057     }
01058 
01070     static function makeSpecialUrl( $name, $urlaction = '', $proto = null ) {
01071         $title = SpecialPage::getSafeTitleFor( $name );
01072         if ( is_null( $proto ) ) {
01073             return $title->getLocalURL( $urlaction );
01074         } else {
01075             return $title->getFullURL( $urlaction, false, $proto );
01076         }
01077     }
01078 
01085     static function makeSpecialUrlSubpage( $name, $subpage, $urlaction = '' ) {
01086         $title = SpecialPage::getSafeTitleFor( $name, $subpage );
01087         return $title->getLocalURL( $urlaction );
01088     }
01089 
01095     static function makeI18nUrl( $name, $urlaction = '' ) {
01096         $title = Title::newFromText( wfMessage( $name )->inContentLanguage()->text() );
01097         self::checkTitle( $title, $name );
01098         return $title->getLocalURL( $urlaction );
01099     }
01100 
01106     static function makeUrl( $name, $urlaction = '' ) {
01107         $title = Title::newFromText( $name );
01108         self::checkTitle( $title, $name );
01109 
01110         return $title->getLocalURL( $urlaction );
01111     }
01112 
01119     static function makeInternalOrExternalUrl( $name ) {
01120         if ( preg_match( '/^(?i:' . wfUrlProtocols() . ')/', $name ) ) {
01121             return $name;
01122         } else {
01123             return self::makeUrl( $name );
01124         }
01125     }
01126 
01134     static function makeNSUrl( $name, $urlaction = '', $namespace = NS_MAIN ) {
01135         $title = Title::makeTitleSafe( $namespace, $name );
01136         self::checkTitle( $title, $name );
01137 
01138         return $title->getLocalURL( $urlaction );
01139     }
01140 
01147     static function makeUrlDetails( $name, $urlaction = '' ) {
01148         $title = Title::newFromText( $name );
01149         self::checkTitle( $title, $name );
01150 
01151         return array(
01152             'href' => $title->getLocalURL( $urlaction ),
01153             'exists' => $title->getArticleID() != 0,
01154         );
01155     }
01156 
01163     static function makeKnownUrlDetails( $name, $urlaction = '' ) {
01164         $title = Title::newFromText( $name );
01165         self::checkTitle( $title, $name );
01166 
01167         return array(
01168             'href' => $title->getLocalURL( $urlaction ),
01169             'exists' => true
01170         );
01171     }
01172 
01179     static function checkTitle( &$title, $name ) {
01180         if ( !is_object( $title ) ) {
01181             $title = Title::newFromText( $name );
01182             if ( !is_object( $title ) ) {
01183                 $title = Title::newFromText( '--error: link target missing--' );
01184             }
01185         }
01186     }
01187 
01209     function buildSidebar() {
01210         global $wgMemc, $wgEnableSidebarCache, $wgSidebarCacheExpiry;
01211         wfProfileIn( __METHOD__ );
01212 
01213         $key = wfMemcKey( 'sidebar', $this->getLanguage()->getCode() );
01214 
01215         if ( $wgEnableSidebarCache ) {
01216             $cachedsidebar = $wgMemc->get( $key );
01217             if ( $cachedsidebar ) {
01218                 wfRunHooks( 'SidebarBeforeOutput', array( $this, &$cachedsidebar ) );
01219 
01220                 wfProfileOut( __METHOD__ );
01221                 return $cachedsidebar;
01222             }
01223         }
01224 
01225         $bar = array();
01226         $this->addToSidebar( $bar, 'sidebar' );
01227 
01228         wfRunHooks( 'SkinBuildSidebar', array( $this, &$bar ) );
01229         if ( $wgEnableSidebarCache ) {
01230             $wgMemc->set( $key, $bar, $wgSidebarCacheExpiry );
01231         }
01232 
01233         wfRunHooks( 'SidebarBeforeOutput', array( $this, &$bar ) );
01234 
01235         wfProfileOut( __METHOD__ );
01236         return $bar;
01237     }
01238 
01248     function addToSidebar( &$bar, $message ) {
01249         $this->addToSidebarPlain( $bar, wfMessage( $message )->inContentLanguage()->plain() );
01250     }
01251 
01259     function addToSidebarPlain( &$bar, $text ) {
01260         $lines = explode( "\n", $text );
01261 
01262         $heading = '';
01263 
01264         foreach ( $lines as $line ) {
01265             if ( strpos( $line, '*' ) !== 0 ) {
01266                 continue;
01267             }
01268             $line = rtrim( $line, "\r" ); // for Windows compat
01269 
01270             if ( strpos( $line, '**' ) !== 0 ) {
01271                 $heading = trim( $line, '* ' );
01272                 if ( !array_key_exists( $heading, $bar ) ) {
01273                     $bar[$heading] = array();
01274                 }
01275             } else {
01276                 $line = trim( $line, '* ' );
01277 
01278                 if ( strpos( $line, '|' ) !== false ) { // sanity check
01279                     $line = MessageCache::singleton()->transform( $line, false, null, $this->getTitle() );
01280                     $line = array_map( 'trim', explode( '|', $line, 2 ) );
01281                     if ( count( $line ) !== 2 ) {
01282                         // Second sanity check, could be hit by people doing
01283                         // funky stuff with parserfuncs... (bug 33321)
01284                         continue;
01285                     }
01286 
01287                     $extraAttribs = array();
01288 
01289                     $msgLink = $this->msg( $line[0] )->inContentLanguage();
01290                     if ( $msgLink->exists() ) {
01291                         $link = $msgLink->text();
01292                         if ( $link == '-' ) {
01293                             continue;
01294                         }
01295                     } else {
01296                         $link = $line[0];
01297                     }
01298                     $msgText = $this->msg( $line[1] );
01299                     if ( $msgText->exists() ) {
01300                         $text = $msgText->text();
01301                     } else {
01302                         $text = $line[1];
01303                     }
01304 
01305                     if ( preg_match( '/^(?i:' . wfUrlProtocols() . ')/', $link ) ) {
01306                         $href = $link;
01307 
01308                         // Parser::getExternalLinkAttribs won't work here because of the Namespace things
01309                         global $wgNoFollowLinks, $wgNoFollowDomainExceptions;
01310                         if ( $wgNoFollowLinks && !wfMatchesDomainList( $href, $wgNoFollowDomainExceptions ) ) {
01311                             $extraAttribs['rel'] = 'nofollow';
01312                         }
01313 
01314                         global $wgExternalLinkTarget;
01315                         if ( $wgExternalLinkTarget ) {
01316                             $extraAttribs['target'] = $wgExternalLinkTarget;
01317                         }
01318                     } else {
01319                         $title = Title::newFromText( $link );
01320 
01321                         if ( $title ) {
01322                             $title = $title->fixSpecialName();
01323                             $href = $title->getLinkURL();
01324                         } else {
01325                             $href = 'INVALID-TITLE';
01326                         }
01327                     }
01328 
01329                     $bar[$heading][] = array_merge( array(
01330                         'text' => $text,
01331                         'href' => $href,
01332                         'id' => 'n-' . Sanitizer::escapeId( strtr( $line[1], ' ', '-' ), 'noninitial' ),
01333                         'active' => false
01334                     ), $extraAttribs );
01335                 } else {
01336                     continue;
01337                 }
01338             }
01339         }
01340 
01341         return $bar;
01342     }
01343 
01355     public function commonPrintStylesheet() {
01356         wfDeprecated( __METHOD__, '1.22' );
01357         return false;
01358     }
01359 
01365     function getNewtalks() {
01366 
01367         $newMessagesAlert = '';
01368         $user = $this->getUser();
01369         $newtalks = $user->getNewMessageLinks();
01370         $out = $this->getOutput();
01371 
01372         // Allow extensions to disable or modify the new messages alert
01373         if ( !wfRunHooks( 'GetNewMessagesAlert', array( &$newMessagesAlert, $newtalks, $user, $out ) ) ) {
01374             return '';
01375         }
01376         if ( $newMessagesAlert ) {
01377             return $newMessagesAlert;
01378         }
01379 
01380         if ( count( $newtalks ) == 1 && $newtalks[0]['wiki'] === wfWikiID() ) {
01381             $uTalkTitle = $user->getTalkPage();
01382             $lastSeenRev = isset( $newtalks[0]['rev'] ) ? $newtalks[0]['rev'] : null;
01383             $nofAuthors = 0;
01384             if ( $lastSeenRev !== null ) {
01385                 $plural = true; // Default if we have a last seen revision: if unknown, use plural
01386                 $latestRev = Revision::newFromTitle( $uTalkTitle, false, Revision::READ_NORMAL );
01387                 if ( $latestRev !== null ) {
01388                     // Singular if only 1 unseen revision, plural if several unseen revisions.
01389                     $plural = $latestRev->getParentId() !== $lastSeenRev->getId();
01390                     $nofAuthors = $uTalkTitle->countAuthorsBetween(
01391                         $lastSeenRev, $latestRev, 10, 'include_new' );
01392                 }
01393             } else {
01394                 // Singular if no revision -> diff link will show latest change only in any case
01395                 $plural = false;
01396             }
01397             $plural = $plural ? 999 : 1;
01398             // 999 signifies "more than one revision". We don't know how many, and even if we did,
01399             // the number of revisions or authors is not necessarily the same as the number of
01400             // "messages".
01401             $newMessagesLink = Linker::linkKnown(
01402                 $uTalkTitle,
01403                 $this->msg( 'newmessageslinkplural' )->params( $plural )->escaped(),
01404                 array(),
01405                 array( 'redirect' => 'no' )
01406             );
01407 
01408             $newMessagesDiffLink = Linker::linkKnown(
01409                 $uTalkTitle,
01410                 $this->msg( 'newmessagesdifflinkplural' )->params( $plural )->escaped(),
01411                 array(),
01412                 $lastSeenRev !== null
01413                     ? array( 'oldid' => $lastSeenRev->getId(), 'diff' => 'cur' )
01414                     : array( 'diff' => 'cur' )
01415             );
01416 
01417             if ( $nofAuthors >= 1 && $nofAuthors <= 10 ) {
01418                 $newMessagesAlert = $this->msg(
01419                     'youhavenewmessagesfromusers',
01420                     $newMessagesLink,
01421                     $newMessagesDiffLink
01422                 )->numParams( $nofAuthors, $plural );
01423             } else {
01424                 // $nofAuthors === 11 signifies "11 or more" ("more than 10")
01425                 $newMessagesAlert = $this->msg(
01426                     $nofAuthors > 10 ? 'youhavenewmessagesmanyusers' : 'youhavenewmessages',
01427                     $newMessagesLink,
01428                     $newMessagesDiffLink
01429                 )->numParams( $plural );
01430             }
01431             $newMessagesAlert = $newMessagesAlert->text();
01432             # Disable Squid cache
01433             $out->setSquidMaxage( 0 );
01434         } elseif ( count( $newtalks ) ) {
01435             $sep = $this->msg( 'newtalkseparator' )->escaped();
01436             $msgs = array();
01437 
01438             foreach ( $newtalks as $newtalk ) {
01439                 $msgs[] = Xml::element(
01440                     'a',
01441                     array( 'href' => $newtalk['link'] ), $newtalk['wiki']
01442                 );
01443             }
01444             $parts = implode( $sep, $msgs );
01445             $newMessagesAlert = $this->msg( 'youhavenewmessagesmulti' )->rawParams( $parts )->escaped();
01446             $out->setSquidMaxage( 0 );
01447         }
01448 
01449         return $newMessagesAlert;
01450     }
01451 
01458     private function getCachedNotice( $name ) {
01459         global $wgRenderHashAppend, $parserMemc, $wgContLang;
01460 
01461         wfProfileIn( __METHOD__ );
01462 
01463         $needParse = false;
01464 
01465         if ( $name === 'default' ) {
01466             // special case
01467             global $wgSiteNotice;
01468             $notice = $wgSiteNotice;
01469             if ( empty( $notice ) ) {
01470                 wfProfileOut( __METHOD__ );
01471                 return false;
01472             }
01473         } else {
01474             $msg = $this->msg( $name )->inContentLanguage();
01475             if ( $msg->isDisabled() ) {
01476                 wfProfileOut( __METHOD__ );
01477                 return false;
01478             }
01479             $notice = $msg->plain();
01480         }
01481 
01482         // Use the extra hash appender to let eg SSL variants separately cache.
01483         $key = wfMemcKey( $name . $wgRenderHashAppend );
01484         $cachedNotice = $parserMemc->get( $key );
01485         if ( is_array( $cachedNotice ) ) {
01486             if ( md5( $notice ) == $cachedNotice['hash'] ) {
01487                 $notice = $cachedNotice['html'];
01488             } else {
01489                 $needParse = true;
01490             }
01491         } else {
01492             $needParse = true;
01493         }
01494 
01495         if ( $needParse ) {
01496             $parsed = $this->getOutput()->parse( $notice );
01497             $parserMemc->set( $key, array( 'html' => $parsed, 'hash' => md5( $notice ) ), 600 );
01498             $notice = $parsed;
01499         }
01500 
01501         $notice = Html::rawElement( 'div', array( 'id' => 'localNotice',
01502             'lang' => $wgContLang->getHtmlCode(), 'dir' => $wgContLang->getDir() ), $notice );
01503         wfProfileOut( __METHOD__ );
01504         return $notice;
01505     }
01506 
01512     function getNamespaceNotice() {
01513         wfProfileIn( __METHOD__ );
01514 
01515         $key = 'namespacenotice-' . $this->getTitle()->getNsText();
01516         $namespaceNotice = $this->getCachedNotice( $key );
01517         if ( $namespaceNotice && substr( $namespaceNotice, 0, 7 ) != '<p>&lt;' ) {
01518             $namespaceNotice = '<div id="namespacebanner">' . $namespaceNotice . '</div>';
01519         } else {
01520             $namespaceNotice = '';
01521         }
01522 
01523         wfProfileOut( __METHOD__ );
01524         return $namespaceNotice;
01525     }
01526 
01532     function getSiteNotice() {
01533         wfProfileIn( __METHOD__ );
01534         $siteNotice = '';
01535 
01536         if ( wfRunHooks( 'SiteNoticeBefore', array( &$siteNotice, $this ) ) ) {
01537             if ( is_object( $this->getUser() ) && $this->getUser()->isLoggedIn() ) {
01538                 $siteNotice = $this->getCachedNotice( 'sitenotice' );
01539             } else {
01540                 $anonNotice = $this->getCachedNotice( 'anonnotice' );
01541                 if ( !$anonNotice ) {
01542                     $siteNotice = $this->getCachedNotice( 'sitenotice' );
01543                 } else {
01544                     $siteNotice = $anonNotice;
01545                 }
01546             }
01547             if ( !$siteNotice ) {
01548                 $siteNotice = $this->getCachedNotice( 'default' );
01549             }
01550         }
01551 
01552         wfRunHooks( 'SiteNoticeAfter', array( &$siteNotice, $this ) );
01553         wfProfileOut( __METHOD__ );
01554         return $siteNotice;
01555     }
01556 
01570     public function doEditSectionLink( Title $nt, $section, $tooltip = null, $lang = false ) {
01571         // HTML generated here should probably have userlangattributes
01572         // added to it for LTR text on RTL pages
01573 
01574         $lang = wfGetLangObj( $lang );
01575 
01576         $attribs = array();
01577         if ( !is_null( $tooltip ) ) {
01578             # Bug 25462: undo double-escaping.
01579             $tooltip = Sanitizer::decodeCharReferences( $tooltip );
01580             $attribs['title'] = wfMessage( 'editsectionhint' )->rawParams( $tooltip )
01581                 ->inLanguage( $lang )->text();
01582         }
01583         $link = Linker::link( $nt, wfMessage( 'editsection' )->inLanguage( $lang )->text(),
01584             $attribs,
01585             array( 'action' => 'edit', 'section' => $section ),
01586             array( 'noclasses', 'known' )
01587         );
01588 
01589         # Add the brackets and the span and run the hook.
01590         $result = '<span class="mw-editsection">'
01591             . '<span class="mw-editsection-bracket">[</span>'
01592             . $link
01593             . '<span class="mw-editsection-bracket">]</span>'
01594             . '</span>';
01595 
01596         wfRunHooks( 'DoEditSectionLink', array( $this, $nt, $section, $tooltip, &$result, $lang ) );
01597         return $result;
01598     }
01599 
01609     function __call( $fname, $args ) {
01610         $realFunction = array( 'Linker', $fname );
01611         if ( is_callable( $realFunction ) ) {
01612             wfDeprecated( get_class( $this ) . '::' . $fname, '1.21' );
01613             return call_user_func_array( $realFunction, $args );
01614         } else {
01615             $className = get_class( $this );
01616             throw new MWException( "Call to undefined method $className::$fname" );
01617         }
01618     }
01619 
01620 }