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