MediaWiki
REL1_24
|
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 ) . ' > '; 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 .= '< '; 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><' ) { 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 }