MediaWiki
REL1_19
|
00001 <?php 00006 if ( !defined( 'MEDIAWIKI' ) ) { 00007 die( 1 ); 00008 } 00009 00018 abstract class Skin extends ContextSource { 00019 protected $skinname = 'standard'; 00020 protected $mRelevantTitle = null; 00021 protected $mRelevantUser = null; 00022 00027 static function getSkinNames() { 00028 global $wgValidSkinNames; 00029 static $skinsInitialised = false; 00030 00031 if ( !$skinsInitialised || !count( $wgValidSkinNames ) ) { 00032 # Get a list of available skins 00033 # Build using the regular expression '^(.*).php$' 00034 # Array keys are all lower case, array value keep the case used by filename 00035 # 00036 wfProfileIn( __METHOD__ . '-init' ); 00037 00038 global $wgStyleDirectory; 00039 00040 $skinDir = dir( $wgStyleDirectory ); 00041 00042 # while code from www.php.net 00043 while ( false !== ( $file = $skinDir->read() ) ) { 00044 // Skip non-PHP files, hidden files, and '.dep' includes 00045 $matches = array(); 00046 00047 if ( preg_match( '/^([^.]*)\.php$/', $file, $matches ) ) { 00048 $aSkin = $matches[1]; 00049 $wgValidSkinNames[strtolower( $aSkin )] = $aSkin; 00050 } 00051 } 00052 $skinDir->close(); 00053 $skinsInitialised = true; 00054 wfProfileOut( __METHOD__ . '-init' ); 00055 } 00056 return $wgValidSkinNames; 00057 } 00058 00063 static function getSkinNameMessages() { 00064 $messages = array(); 00065 foreach( self::getSkinNames() as $skinKey => $skinName ) { 00066 $messages[] = "skinname-$skinKey"; 00067 } 00068 return $messages; 00069 } 00070 00077 public static function getUsableSkins() { 00078 global $wgSkipSkins; 00079 00080 $usableSkins = self::getSkinNames(); 00081 00082 foreach ( $wgSkipSkins as $skip ) { 00083 unset( $usableSkins[$skip] ); 00084 } 00085 00086 return $usableSkins; 00087 } 00088 00096 static function normalizeKey( $key ) { 00097 global $wgDefaultSkin; 00098 00099 $skinNames = Skin::getSkinNames(); 00100 00101 if ( $key == '' ) { 00102 // Don't return the default immediately; 00103 // in a misconfiguration we need to fall back. 00104 $key = $wgDefaultSkin; 00105 } 00106 00107 if ( isset( $skinNames[$key] ) ) { 00108 return $key; 00109 } 00110 00111 // Older versions of the software used a numeric setting 00112 // in the user preferences. 00113 $fallback = array( 00114 0 => $wgDefaultSkin, 00115 1 => 'nostalgia', 00116 2 => 'cologneblue' 00117 ); 00118 00119 if ( isset( $fallback[$key] ) ) { 00120 $key = $fallback[$key]; 00121 } 00122 00123 if ( isset( $skinNames[$key] ) ) { 00124 return $key; 00125 } elseif ( isset( $skinNames[$wgDefaultSkin] ) ) { 00126 return $wgDefaultSkin; 00127 } else { 00128 return 'vector'; 00129 } 00130 } 00131 00137 static function &newFromKey( $key ) { 00138 global $wgStyleDirectory; 00139 00140 $key = Skin::normalizeKey( $key ); 00141 00142 $skinNames = Skin::getSkinNames(); 00143 $skinName = $skinNames[$key]; 00144 $className = "Skin{$skinName}"; 00145 00146 # Grab the skin class and initialise it. 00147 if ( !MWInit::classExists( $className ) ) { 00148 00149 if ( !defined( 'MW_COMPILED' ) ) { 00150 // Preload base classes to work around APC/PHP5 bug 00151 $deps = "{$wgStyleDirectory}/{$skinName}.deps.php"; 00152 if ( file_exists( $deps ) ) { 00153 include_once( $deps ); 00154 } 00155 require_once( "{$wgStyleDirectory}/{$skinName}.php" ); 00156 } 00157 00158 # Check if we got if not failback to default skin 00159 if ( !MWInit::classExists( $className ) ) { 00160 # DO NOT die if the class isn't found. This breaks maintenance 00161 # scripts and can cause a user account to be unrecoverable 00162 # except by SQL manipulation if a previously valid skin name 00163 # is no longer valid. 00164 wfDebug( "Skin class does not exist: $className\n" ); 00165 $className = 'SkinVector'; 00166 if ( !defined( 'MW_COMPILED' ) ) { 00167 require_once( "{$wgStyleDirectory}/Vector.php" ); 00168 } 00169 } 00170 } 00171 $skin = new $className( $key ); 00172 return $skin; 00173 } 00174 00176 public function getSkinName() { 00177 return $this->skinname; 00178 } 00179 00183 function initPage( OutputPage $out ) { 00184 wfProfileIn( __METHOD__ ); 00185 00186 $this->preloadExistence(); 00187 00188 wfProfileOut( __METHOD__ ); 00189 } 00190 00194 function preloadExistence() { 00195 $user = $this->getUser(); 00196 00197 // User/talk link 00198 $titles = array( $user->getUserPage(), $user->getTalkPage() ); 00199 00200 // Other tab link 00201 if ( $this->getTitle()->isSpecialPage() ) { 00202 // nothing 00203 } elseif ( $this->getTitle()->isTalkPage() ) { 00204 $titles[] = $this->getTitle()->getSubjectPage(); 00205 } else { 00206 $titles[] = $this->getTitle()->getTalkPage(); 00207 } 00208 00209 $lb = new LinkBatch( $titles ); 00210 $lb->setCaller( __METHOD__ ); 00211 $lb->execute(); 00212 } 00213 00219 public function getRevisionId() { 00220 return $this->getOutput()->getRevisionId(); 00221 } 00222 00228 public function isRevisionCurrent() { 00229 $revID = $this->getRevisionId(); 00230 return $revID == 0 || $revID == $this->getTitle()->getLatestRevID(); 00231 } 00232 00238 public function setRelevantTitle( $t ) { 00239 $this->mRelevantTitle = $t; 00240 } 00241 00252 public function getRelevantTitle() { 00253 if ( isset($this->mRelevantTitle) ) { 00254 return $this->mRelevantTitle; 00255 } 00256 return $this->getTitle(); 00257 } 00258 00264 public function setRelevantUser( $u ) { 00265 $this->mRelevantUser = $u; 00266 } 00267 00276 public function getRelevantUser() { 00277 if ( isset($this->mRelevantUser) ) { 00278 return $this->mRelevantUser; 00279 } 00280 $title = $this->getRelevantTitle(); 00281 if( $title->getNamespace() == NS_USER || $title->getNamespace() == NS_USER_TALK ) { 00282 $rootUser = strtok( $title->getText(), '/' ); 00283 if ( User::isIP( $rootUser ) ) { 00284 $this->mRelevantUser = User::newFromName( $rootUser, false ); 00285 } else { 00286 $user = User::newFromName( $rootUser, false ); 00287 if ( $user && $user->isLoggedIn() ) { 00288 $this->mRelevantUser = $user; 00289 } 00290 } 00291 return $this->mRelevantUser; 00292 } 00293 return null; 00294 } 00295 00300 abstract function outputPage( OutputPage $out = null ); 00301 00306 static function makeVariablesScript( $data ) { 00307 if ( $data ) { 00308 return Html::inlineScript( 00309 ResourceLoader::makeLoaderConditionalScript( ResourceLoader::makeConfigSetScript( $data ) ) 00310 ); 00311 } else { 00312 return ''; 00313 } 00314 } 00315 00323 public static function makeGlobalVariablesScript( $unused ) { 00324 global $wgOut; 00325 00326 wfDeprecated( __METHOD__, '1.19' ); 00327 00328 return self::makeVariablesScript( $wgOut->getJSVars() ); 00329 } 00330 00336 public static function getDynamicStylesheetQuery() { 00337 global $wgSquidMaxage; 00338 00339 return array( 00340 'action' => 'raw', 00341 'maxage' => $wgSquidMaxage, 00342 'usemsgcache' => 'yes', 00343 'ctype' => 'text/css', 00344 'smaxage' => $wgSquidMaxage, 00345 ); 00346 } 00347 00356 abstract function setupSkinUserCss( OutputPage $out ); 00357 00363 function getPageClasses( $title ) { 00364 $numeric = 'ns-' . $title->getNamespace(); 00365 00366 if ( $title->isSpecialPage() ) { 00367 $type = 'ns-special'; 00368 // bug 23315: provide a class based on the canonical special page name without subpages 00369 list( $canonicalName ) = SpecialPageFactory::resolveAlias( $title->getDBkey() ); 00370 if ( $canonicalName ) { 00371 $type .= ' ' . Sanitizer::escapeClass( "mw-special-$canonicalName" ); 00372 } else { 00373 $type .= ' mw-invalidspecialpage'; 00374 } 00375 } elseif ( $title->isTalkPage() ) { 00376 $type = 'ns-talk'; 00377 } else { 00378 $type = 'ns-subject'; 00379 } 00380 00381 $name = Sanitizer::escapeClass( 'page-' . $title->getPrefixedText() ); 00382 00383 return "$numeric $type $name"; 00384 } 00385 00393 function addToBodyAttributes( $out, &$bodyAttrs ) { 00394 // does nothing by default 00395 } 00396 00401 function getLogo() { 00402 global $wgLogo; 00403 return $wgLogo; 00404 } 00405 00409 function getCategoryLinks() { 00410 global $wgUseCategoryBrowser; 00411 00412 $out = $this->getOutput(); 00413 $allCats = $out->getCategoryLinks(); 00414 00415 if ( !count( $allCats ) ) { 00416 return ''; 00417 } 00418 00419 $embed = "<li>"; 00420 $pop = "</li>"; 00421 00422 $s = ''; 00423 $colon = $this->msg( 'colon-separator' )->escaped(); 00424 00425 if ( !empty( $allCats['normal'] ) ) { 00426 $t = $embed . implode( "{$pop}{$embed}" , $allCats['normal'] ) . $pop; 00427 00428 $msg = $this->msg( 'pagecategories', count( $allCats['normal'] ) )->escaped(); 00429 $linkPage = wfMessage( 'pagecategorieslink' )->inContentLanguage()->text(); 00430 $s .= '<div id="mw-normal-catlinks" class="mw-normal-catlinks">' . 00431 Linker::link( Title::newFromText( $linkPage ), $msg ) 00432 . $colon . '<ul>' . $t . '</ul>' . '</div>'; 00433 } 00434 00435 # Hidden categories 00436 if ( isset( $allCats['hidden'] ) ) { 00437 if ( $this->getUser()->getBoolOption( 'showhiddencats' ) ) { 00438 $class = ' mw-hidden-cats-user-shown'; 00439 } elseif ( $this->getTitle()->getNamespace() == NS_CATEGORY ) { 00440 $class = ' mw-hidden-cats-ns-shown'; 00441 } else { 00442 $class = ' mw-hidden-cats-hidden'; 00443 } 00444 00445 $s .= "<div id=\"mw-hidden-catlinks\" class=\"mw-hidden-catlinks$class\">" . 00446 $this->msg( 'hidden-categories', count( $allCats['hidden'] ) )->escaped() . 00447 $colon . '<ul>' . $embed . implode( "{$pop}{$embed}" , $allCats['hidden'] ) . $pop . '</ul>' . 00448 '</div>'; 00449 } 00450 00451 # optional 'dmoz-like' category browser. Will be shown under the list 00452 # of categories an article belong to 00453 if ( $wgUseCategoryBrowser ) { 00454 $s .= '<br /><hr />'; 00455 00456 # get a big array of the parents tree 00457 $parenttree = $this->getTitle()->getParentCategoryTree(); 00458 # Skin object passed by reference cause it can not be 00459 # accessed under the method subfunction drawCategoryBrowser 00460 $tempout = explode( "\n", $this->drawCategoryBrowser( $parenttree ) ); 00461 # Clean out bogus first entry and sort them 00462 unset( $tempout[0] ); 00463 asort( $tempout ); 00464 # Output one per line 00465 $s .= implode( "<br />\n", $tempout ); 00466 } 00467 00468 return $s; 00469 } 00470 00476 function drawCategoryBrowser( $tree ) { 00477 $return = ''; 00478 00479 foreach ( $tree as $element => $parent ) { 00480 if ( empty( $parent ) ) { 00481 # element start a new list 00482 $return .= "\n"; 00483 } else { 00484 # grab the others elements 00485 $return .= $this->drawCategoryBrowser( $parent ) . ' > '; 00486 } 00487 00488 # add our current element to the list 00489 $eltitle = Title::newFromText( $element ); 00490 $return .= Linker::link( $eltitle, htmlspecialchars( $eltitle->getText() ) ); 00491 } 00492 00493 return $return; 00494 } 00495 00499 function getCategories() { 00500 $out = $this->getOutput(); 00501 00502 $catlinks = $this->getCategoryLinks(); 00503 00504 $classes = 'catlinks'; 00505 00506 // Check what we're showing 00507 $allCats = $out->getCategoryLinks(); 00508 $showHidden = $this->getUser()->getBoolOption( 'showhiddencats' ) || 00509 $this->getTitle()->getNamespace() == NS_CATEGORY; 00510 00511 if ( empty( $allCats['normal'] ) && !( !empty( $allCats['hidden'] ) && $showHidden ) ) { 00512 $classes .= ' catlinks-allhidden'; 00513 } 00514 00515 return "<div id='catlinks' class='$classes'>{$catlinks}</div>"; 00516 } 00517 00532 protected function afterContentHook() { 00533 $data = ''; 00534 00535 if ( wfRunHooks( 'SkinAfterContent', array( &$data, $this ) ) ) { 00536 // adding just some spaces shouldn't toggle the output 00537 // of the whole <div/>, so we use trim() here 00538 if ( trim( $data ) != '' ) { 00539 // Doing this here instead of in the skins to 00540 // ensure that the div has the same ID in all 00541 // skins 00542 $data = "<div id='mw-data-after-content'>\n" . 00543 "\t$data\n" . 00544 "</div>\n"; 00545 } 00546 } else { 00547 wfDebug( "Hook SkinAfterContent changed output processing.\n" ); 00548 } 00549 00550 return $data; 00551 } 00552 00558 protected function generateDebugHTML() { 00559 global $wgShowDebug; 00560 00561 $html = MWDebug::getDebugHTML( $this->getContext() ); 00562 00563 if ( $wgShowDebug ) { 00564 $listInternals = $this->formatDebugHTML( $this->getOutput()->mDebugtext ); 00565 $html .= "\n<hr />\n<strong>Debug data:</strong><ul id=\"mw-debug-html\">" . 00566 $listInternals . "</ul>\n"; 00567 } 00568 00569 return $html; 00570 } 00571 00576 private function formatDebugHTML( $debugText ) { 00577 global $wgDebugTimestamps; 00578 00579 $lines = explode( "\n", $debugText ); 00580 $curIdent = 0; 00581 $ret = '<li>'; 00582 00583 foreach ( $lines as $line ) { 00584 $pre = ''; 00585 if ( $wgDebugTimestamps ) { 00586 $matches = array(); 00587 if ( preg_match( '/^(\d+\.\d+ {1,3}\d+.\dM\s{2})/', $line, $matches ) ) { 00588 $pre = $matches[1]; 00589 $line = substr( $line, strlen( $pre ) ); 00590 } 00591 } 00592 $display = ltrim( $line ); 00593 $ident = strlen( $line ) - strlen( $display ); 00594 $diff = $ident - $curIdent; 00595 00596 $display = $pre . $display; 00597 if ( $display == '' ) { 00598 $display = "\xc2\xa0"; 00599 } 00600 00601 if ( !$ident && $diff < 0 && substr( $display, 0, 9 ) != 'Entering ' && substr( $display, 0, 8 ) != 'Exiting ' ) { 00602 $ident = $curIdent; 00603 $diff = 0; 00604 $display = '<span style="background:yellow;">' . htmlspecialchars( $display ) . '</span>'; 00605 } else { 00606 $display = htmlspecialchars( $display ); 00607 } 00608 00609 if ( $diff < 0 ) { 00610 $ret .= str_repeat( "</li></ul>\n", -$diff ) . "</li><li>\n"; 00611 } elseif ( $diff == 0 ) { 00612 $ret .= "</li><li>\n"; 00613 } else { 00614 $ret .= str_repeat( "<ul><li>\n", $diff ); 00615 } 00616 $ret .= "<tt>$display</tt>\n"; 00617 00618 $curIdent = $ident; 00619 } 00620 00621 $ret .= str_repeat( '</li></ul>', $curIdent ) . '</li>'; 00622 00623 return $ret; 00624 } 00625 00631 function bottomScripts() { 00632 // TODO and the suckage continues. This function is really just a wrapper around 00633 // OutputPage::getBottomScripts() which takes a Skin param. This should be cleaned 00634 // up at some point 00635 $bottomScriptText = $this->getOutput()->getBottomScripts(); 00636 wfRunHooks( 'SkinAfterBottomScripts', array( $this, &$bottomScriptText ) ); 00637 00638 return $bottomScriptText; 00639 } 00640 00647 function printSource() { 00648 $oldid = $this->getRevisionId(); 00649 if ( $oldid ) { 00650 $url = htmlspecialchars( $this->getTitle()->getCanonicalURL( 'oldid=' . $oldid ) ); 00651 } else { 00652 // oldid not available for non existing pages 00653 $url = htmlspecialchars( $this->getTitle()->getCanonicalURL() ); 00654 } 00655 return $this->msg( 'retrievedfrom', '<a href="' . $url . '">' . $url . '</a>' )->text(); 00656 } 00657 00661 function getUndeleteLink() { 00662 $action = $this->getRequest()->getVal( 'action', 'view' ); 00663 00664 if ( $this->getUser()->isAllowed( 'deletedhistory' ) && 00665 ( $this->getTitle()->getArticleId() == 0 || $action == 'history' ) ) { 00666 $n = $this->getTitle()->isDeleted(); 00667 00668 00669 if ( $n ) { 00670 if ( $this->getUser()->isAllowed( 'undelete' ) ) { 00671 $msg = 'thisisdeleted'; 00672 } else { 00673 $msg = 'viewdeleted'; 00674 } 00675 00676 return $this->msg( $msg )->rawParams( 00677 Linker::linkKnown( 00678 SpecialPage::getTitleFor( 'Undelete', $this->getTitle()->getPrefixedDBkey() ), 00679 $this->msg( 'restorelink' )->numParams( $n )->escaped() ) 00680 )->text(); 00681 } 00682 } 00683 00684 return ''; 00685 } 00686 00690 function subPageSubtitle() { 00691 $out = $this->getOutput(); 00692 $subpages = ''; 00693 00694 if ( !wfRunHooks( 'SkinSubPageSubtitle', array( &$subpages, $this, $out ) ) ) { 00695 return $subpages; 00696 } 00697 00698 if ( $out->isArticle() && MWNamespace::hasSubpages( $out->getTitle()->getNamespace() ) ) { 00699 $ptext = $this->getTitle()->getPrefixedText(); 00700 if ( preg_match( '/\//', $ptext ) ) { 00701 $links = explode( '/', $ptext ); 00702 array_pop( $links ); 00703 $c = 0; 00704 $growinglink = ''; 00705 $display = ''; 00706 00707 foreach ( $links as $link ) { 00708 $growinglink .= $link; 00709 $display .= $link; 00710 $linkObj = Title::newFromText( $growinglink ); 00711 00712 if ( is_object( $linkObj ) && $linkObj->exists() ) { 00713 $getlink = Linker::linkKnown( 00714 $linkObj, 00715 htmlspecialchars( $display ) 00716 ); 00717 00718 $c++; 00719 00720 if ( $c > 1 ) { 00721 $subpages .= $this->msg( 'pipe-separator' )->escaped(); 00722 } else { 00723 $subpages .= '< '; 00724 } 00725 00726 $subpages .= $getlink; 00727 $display = ''; 00728 } else { 00729 $display .= '/'; 00730 } 00731 $growinglink .= '/'; 00732 } 00733 } 00734 } 00735 00736 return $subpages; 00737 } 00738 00743 function showIPinHeader() { 00744 global $wgShowIPinHeader; 00745 return $wgShowIPinHeader && session_id() != ''; 00746 } 00747 00751 function getSearchLink() { 00752 $searchPage = SpecialPage::getTitleFor( 'Search' ); 00753 return $searchPage->getLocalURL(); 00754 } 00755 00759 function escapeSearchLink() { 00760 return htmlspecialchars( $this->getSearchLink() ); 00761 } 00762 00767 function getCopyright( $type = 'detect' ) { 00768 global $wgRightsPage, $wgRightsUrl, $wgRightsText, $wgContLang; 00769 00770 if ( $type == 'detect' ) { 00771 if ( !$this->isRevisionCurrent() && !$this->msg( 'history_copyright' )->inContentLanguage()->isDisabled() ) { 00772 $type = 'history'; 00773 } else { 00774 $type = 'normal'; 00775 } 00776 } 00777 00778 if ( $type == 'history' ) { 00779 $msg = 'history_copyright'; 00780 } else { 00781 $msg = 'copyright'; 00782 } 00783 00784 if ( $wgRightsPage ) { 00785 $title = Title::newFromText( $wgRightsPage ); 00786 $link = Linker::linkKnown( $title, $wgRightsText ); 00787 } elseif ( $wgRightsUrl ) { 00788 $link = Linker::makeExternalLink( $wgRightsUrl, $wgRightsText ); 00789 } elseif ( $wgRightsText ) { 00790 $link = $wgRightsText; 00791 } else { 00792 # Give up now 00793 return ''; 00794 } 00795 00796 // Allow for site and per-namespace customization of copyright notice. 00797 $forContent = true; 00798 00799 wfRunHooks( 'SkinCopyrightFooter', array( $this->getTitle(), $type, &$msg, &$link, &$forContent ) ); 00800 00801 $msgObj = $this->msg( $msg )->rawParams( $link ); 00802 if ( $forContent ) { 00803 $msg = $msgObj->inContentLanguage()->text(); 00804 if ( $this->getLanguage()->getCode() !== $wgContLang->getCode() ) { 00805 $msg = Html::rawElement( 'span', array( 'lang' => $wgContLang->getHtmlCode(), 'dir' => $wgContLang->getDir() ), $msg ); 00806 } 00807 return $msg; 00808 } else { 00809 return $msgObj->text(); 00810 } 00811 } 00812 00816 function getCopyrightIcon() { 00817 global $wgRightsUrl, $wgRightsText, $wgRightsIcon, $wgCopyrightIcon; 00818 00819 $out = ''; 00820 00821 if ( isset( $wgCopyrightIcon ) && $wgCopyrightIcon ) { 00822 $out = $wgCopyrightIcon; 00823 } elseif ( $wgRightsIcon ) { 00824 $icon = htmlspecialchars( $wgRightsIcon ); 00825 00826 if ( $wgRightsUrl ) { 00827 $url = htmlspecialchars( $wgRightsUrl ); 00828 $out .= '<a href="' . $url . '">'; 00829 } 00830 00831 $text = htmlspecialchars( $wgRightsText ); 00832 $out .= "<img src=\"$icon\" alt=\"$text\" width=\"88\" height=\"31\" />"; 00833 00834 if ( $wgRightsUrl ) { 00835 $out .= '</a>'; 00836 } 00837 } 00838 00839 return $out; 00840 } 00841 00846 function getPoweredBy() { 00847 global $wgStylePath; 00848 00849 $url = htmlspecialchars( "$wgStylePath/common/images/poweredby_mediawiki_88x31.png" ); 00850 $text = '<a href="//www.mediawiki.org/"><img src="' . $url . '" height="31" width="88" alt="Powered by MediaWiki" /></a>'; 00851 wfRunHooks( 'SkinGetPoweredBy', array( &$text, $this ) ); 00852 return $text; 00853 } 00854 00860 protected function lastModified() { 00861 $timestamp = $this->getOutput()->getRevisionTimestamp(); 00862 00863 # No cached timestamp, load it from the database 00864 if ( $timestamp === null ) { 00865 $timestamp = Revision::getTimestampFromId( $this->getTitle(), $this->getRevisionId() ); 00866 } 00867 00868 if ( $timestamp ) { 00869 $d = $this->getLanguage()->userDate( $timestamp, $this->getUser() ); 00870 $t = $this->getLanguage()->userTime( $timestamp, $this->getUser() ); 00871 $s = ' ' . $this->msg( 'lastmodifiedat', $d, $t )->text(); 00872 } else { 00873 $s = ''; 00874 } 00875 00876 if ( wfGetLB()->getLaggedSlaveMode() ) { 00877 $s .= ' <strong>' . $this->msg( 'laggedslavemode' )->text() . '</strong>'; 00878 } 00879 00880 return $s; 00881 } 00882 00887 function logoText( $align = '' ) { 00888 if ( $align != '' ) { 00889 $a = " align='{$align}'"; 00890 } else { 00891 $a = ''; 00892 } 00893 00894 $mp = $this->msg( 'mainpage' )->escaped(); 00895 $mptitle = Title::newMainPage(); 00896 $url = ( is_object( $mptitle ) ? htmlspecialchars( $mptitle->getLocalURL() ) : '' ); 00897 00898 $logourl = $this->getLogo(); 00899 $s = "<a href='{$url}'><img{$a} src='{$logourl}' alt='[{$mp}]' /></a>"; 00900 00901 return $s; 00902 } 00903 00910 function makeFooterIcon( $icon, $withImage = 'withImage' ) { 00911 if ( is_string( $icon ) ) { 00912 $html = $icon; 00913 } else { // Assuming array 00914 $url = isset($icon["url"]) ? $icon["url"] : null; 00915 unset( $icon["url"] ); 00916 if ( isset( $icon["src"] ) && $withImage === 'withImage' ) { 00917 $html = Html::element( 'img', $icon ); // do this the lazy way, just pass icon data as an attribute array 00918 } else { 00919 $html = htmlspecialchars( $icon["alt"] ); 00920 } 00921 if ( $url ) { 00922 $html = Html::rawElement( 'a', array( "href" => $url ), $html ); 00923 } 00924 } 00925 return $html; 00926 } 00927 00932 function mainPageLink() { 00933 $s = Linker::linkKnown( 00934 Title::newMainPage(), 00935 $this->msg( 'mainpage' )->escaped() 00936 ); 00937 00938 return $s; 00939 } 00940 00946 public function footerLink( $desc, $page ) { 00947 // if the link description has been set to "-" in the default language, 00948 if ( $this->msg( $desc )->inContentLanguage()->isDisabled() ) { 00949 // then it is disabled, for all languages. 00950 return ''; 00951 } else { 00952 // Otherwise, we display the link for the user, described in their 00953 // language (which may or may not be the same as the default language), 00954 // but we make the link target be the one site-wide page. 00955 $title = Title::newFromText( $this->msg( $page )->inContentLanguage()->text() ); 00956 00957 return Linker::linkKnown( 00958 $title, 00959 $this->msg( $desc )->escaped() 00960 ); 00961 } 00962 } 00963 00968 function privacyLink() { 00969 return $this->footerLink( 'privacy', 'privacypage' ); 00970 } 00971 00976 function aboutLink() { 00977 return $this->footerLink( 'aboutsite', 'aboutpage' ); 00978 } 00979 00984 function disclaimerLink() { 00985 return $this->footerLink( 'disclaimers', 'disclaimerpage' ); 00986 } 00987 00995 function editUrlOptions() { 00996 $options = array( 'action' => 'edit' ); 00997 00998 if ( !$this->isRevisionCurrent() ) { 00999 $options['oldid'] = intval( $this->getRevisionId() ); 01000 } 01001 01002 return $options; 01003 } 01004 01009 function showEmailUser( $id ) { 01010 if ( $id instanceof User ) { 01011 $targetUser = $id; 01012 } else { 01013 $targetUser = User::newFromId( $id ); 01014 } 01015 return $this->getUser()->canSendEmail() && # the sending user must have a confirmed email address 01016 $targetUser->canReceiveEmail(); # the target user must have a confirmed email address and allow emails from users 01017 } 01018 01026 function getCommonStylePath( $name ) { 01027 global $wgStylePath, $wgStyleVersion; 01028 return "$wgStylePath/common/$name?$wgStyleVersion"; 01029 } 01030 01038 function getSkinStylePath( $name ) { 01039 global $wgStylePath, $wgStyleVersion; 01040 return "$wgStylePath/{$this->stylename}/$name?$wgStyleVersion"; 01041 } 01042 01043 /* these are used extensively in SkinTemplate, but also some other places */ 01044 01049 static function makeMainPageUrl( $urlaction = '' ) { 01050 $title = Title::newMainPage(); 01051 self::checkTitle( $title, '' ); 01052 01053 return $title->getLocalURL( $urlaction ); 01054 } 01055 01061 static function makeSpecialUrl( $name, $urlaction = '' ) { 01062 $title = SpecialPage::getSafeTitleFor( $name ); 01063 return $title->getLocalURL( $urlaction ); 01064 } 01065 01072 static function makeSpecialUrlSubpage( $name, $subpage, $urlaction = '' ) { 01073 $title = SpecialPage::getSafeTitleFor( $name, $subpage ); 01074 return $title->getLocalURL( $urlaction ); 01075 } 01076 01082 static function makeI18nUrl( $name, $urlaction = '' ) { 01083 $title = Title::newFromText( wfMsgForContent( $name ) ); 01084 self::checkTitle( $title, $name ); 01085 return $title->getLocalURL( $urlaction ); 01086 } 01087 01093 static function makeUrl( $name, $urlaction = '' ) { 01094 $title = Title::newFromText( $name ); 01095 self::checkTitle( $title, $name ); 01096 01097 return $title->getLocalURL( $urlaction ); 01098 } 01099 01106 static function makeInternalOrExternalUrl( $name ) { 01107 if ( preg_match( '/^(?:' . wfUrlProtocols() . ')/', $name ) ) { 01108 return $name; 01109 } else { 01110 return self::makeUrl( $name ); 01111 } 01112 } 01113 01121 static function makeNSUrl( $name, $urlaction = '', $namespace = NS_MAIN ) { 01122 $title = Title::makeTitleSafe( $namespace, $name ); 01123 self::checkTitle( $title, $name ); 01124 01125 return $title->getLocalURL( $urlaction ); 01126 } 01127 01134 static function makeUrlDetails( $name, $urlaction = '' ) { 01135 $title = Title::newFromText( $name ); 01136 self::checkTitle( $title, $name ); 01137 01138 return array( 01139 'href' => $title->getLocalURL( $urlaction ), 01140 'exists' => $title->getArticleID() != 0, 01141 ); 01142 } 01143 01150 static function makeKnownUrlDetails( $name, $urlaction = '' ) { 01151 $title = Title::newFromText( $name ); 01152 self::checkTitle( $title, $name ); 01153 01154 return array( 01155 'href' => $title->getLocalURL( $urlaction ), 01156 'exists' => true 01157 ); 01158 } 01159 01166 static function checkTitle( &$title, $name ) { 01167 if ( !is_object( $title ) ) { 01168 $title = Title::newFromText( $name ); 01169 if ( !is_object( $title ) ) { 01170 $title = Title::newFromText( '--error: link target missing--' ); 01171 } 01172 } 01173 } 01174 01180 function buildSidebar() { 01181 global $wgMemc, $wgEnableSidebarCache, $wgSidebarCacheExpiry; 01182 wfProfileIn( __METHOD__ ); 01183 01184 $key = wfMemcKey( 'sidebar', $this->getLanguage()->getCode() ); 01185 01186 if ( $wgEnableSidebarCache ) { 01187 $cachedsidebar = $wgMemc->get( $key ); 01188 if ( $cachedsidebar ) { 01189 wfProfileOut( __METHOD__ ); 01190 return $cachedsidebar; 01191 } 01192 } 01193 01194 $bar = array(); 01195 $this->addToSidebar( $bar, 'sidebar' ); 01196 01197 wfRunHooks( 'SkinBuildSidebar', array( $this, &$bar ) ); 01198 if ( $wgEnableSidebarCache ) { 01199 $wgMemc->set( $key, $bar, $wgSidebarCacheExpiry ); 01200 } 01201 01202 wfProfileOut( __METHOD__ ); 01203 return $bar; 01204 } 01214 function addToSidebar( &$bar, $message ) { 01215 $this->addToSidebarPlain( $bar, wfMsgForContentNoTrans( $message ) ); 01216 } 01217 01225 function addToSidebarPlain( &$bar, $text ) { 01226 $lines = explode( "\n", $text ); 01227 01228 $heading = ''; 01229 01230 foreach ( $lines as $line ) { 01231 if ( strpos( $line, '*' ) !== 0 ) { 01232 continue; 01233 } 01234 $line = rtrim( $line, "\r" ); // for Windows compat 01235 01236 if ( strpos( $line, '**' ) !== 0 ) { 01237 $heading = trim( $line, '* ' ); 01238 if ( !array_key_exists( $heading, $bar ) ) { 01239 $bar[$heading] = array(); 01240 } 01241 } else { 01242 $line = trim( $line, '* ' ); 01243 01244 if ( strpos( $line, '|' ) !== false ) { // sanity check 01245 $line = MessageCache::singleton()->transform( $line, false, null, $this->getTitle() ); 01246 $line = array_map( 'trim', explode( '|', $line, 2 ) ); 01247 if ( count( $line ) !== 2 ) { 01248 // Second sanity check, could be hit by people doing 01249 // funky stuff with parserfuncs... (bug 33321) 01250 continue; 01251 } 01252 01253 $extraAttribs = array(); 01254 01255 $msgLink = $this->msg( $line[0] )->inContentLanguage(); 01256 if ( $msgLink->exists() ) { 01257 $link = $msgLink->text(); 01258 if ( $link == '-' ) { 01259 continue; 01260 } 01261 } else { 01262 $link = $line[0]; 01263 } 01264 $msgText = $this->msg( $line[1] ); 01265 if ( $msgText->exists() ) { 01266 $text = $msgText->text(); 01267 } else { 01268 $text = $line[1]; 01269 } 01270 01271 if ( preg_match( '/^(?:' . wfUrlProtocols() . ')/', $link ) ) { 01272 $href = $link; 01273 01274 // Parser::getExternalLinkAttribs won't work here because of the Namespace things 01275 global $wgNoFollowLinks, $wgNoFollowDomainExceptions; 01276 if ( $wgNoFollowLinks && !wfMatchesDomainList( $href, $wgNoFollowDomainExceptions ) ) { 01277 $extraAttribs['rel'] = 'nofollow'; 01278 } 01279 01280 global $wgExternalLinkTarget; 01281 if ( $wgExternalLinkTarget) { 01282 $extraAttribs['target'] = $wgExternalLinkTarget; 01283 } 01284 } else { 01285 $title = Title::newFromText( $link ); 01286 01287 if ( $title ) { 01288 $title = $title->fixSpecialName(); 01289 $href = $title->getLinkURL(); 01290 } else { 01291 $href = 'INVALID-TITLE'; 01292 } 01293 } 01294 01295 $bar[$heading][] = array_merge( array( 01296 'text' => $text, 01297 'href' => $href, 01298 'id' => 'n-' . Sanitizer::escapeId( strtr( $line[1], ' ', '-' ), 'noninitial' ), 01299 'active' => false 01300 ), $extraAttribs ); 01301 } else { 01302 continue; 01303 } 01304 } 01305 } 01306 01307 return $bar; 01308 } 01309 01317 public function commonPrintStylesheet() { 01318 return true; 01319 } 01320 01325 function getNewtalks() { 01326 $out = $this->getOutput(); 01327 01328 $newtalks = $this->getUser()->getNewMessageLinks(); 01329 $ntl = ''; 01330 01331 if ( count( $newtalks ) == 1 && $newtalks[0]['wiki'] === wfWikiID() ) { 01332 $userTitle = $this->getUser()->getUserPage(); 01333 $userTalkTitle = $userTitle->getTalkPage(); 01334 01335 if ( !$userTalkTitle->equals( $out->getTitle() ) ) { 01336 $newMessagesLink = Linker::linkKnown( 01337 $userTalkTitle, 01338 $this->msg( 'newmessageslink' )->escaped(), 01339 array(), 01340 array( 'redirect' => 'no' ) 01341 ); 01342 01343 $newMessagesDiffLink = Linker::linkKnown( 01344 $userTalkTitle, 01345 $this->msg( 'newmessagesdifflink' )->escaped(), 01346 array(), 01347 array( 'diff' => 'cur' ) 01348 ); 01349 01350 $ntl = $this->msg( 01351 'youhavenewmessages', 01352 $newMessagesLink, 01353 $newMessagesDiffLink 01354 )->text(); 01355 # Disable Squid cache 01356 $out->setSquidMaxage( 0 ); 01357 } 01358 } elseif ( count( $newtalks ) ) { 01359 // _>" " for BC <= 1.16 01360 $sep = str_replace( '_', ' ', $this->msg( 'newtalkseparator' )->escaped() ); 01361 $msgs = array(); 01362 01363 foreach ( $newtalks as $newtalk ) { 01364 $msgs[] = Xml::element( 01365 'a', 01366 array( 'href' => $newtalk['link'] ), $newtalk['wiki'] 01367 ); 01368 } 01369 $parts = implode( $sep, $msgs ); 01370 $ntl = $this->msg( 'youhavenewmessagesmulti' )->rawParams( $parts )->escaped(); 01371 $out->setSquidMaxage( 0 ); 01372 } 01373 01374 return $ntl; 01375 } 01376 01383 private function getCachedNotice( $name ) { 01384 global $wgRenderHashAppend, $parserMemc, $wgContLang; 01385 01386 wfProfileIn( __METHOD__ ); 01387 01388 $needParse = false; 01389 01390 if( $name === 'default' ) { 01391 // special case 01392 global $wgSiteNotice; 01393 $notice = $wgSiteNotice; 01394 if( empty( $notice ) ) { 01395 wfProfileOut( __METHOD__ ); 01396 return false; 01397 } 01398 } else { 01399 $msg = $this->msg( $name )->inContentLanguage(); 01400 if( $msg->isDisabled() ) { 01401 wfProfileOut( __METHOD__ ); 01402 return false; 01403 } 01404 $notice = $msg->plain(); 01405 } 01406 01407 // Use the extra hash appender to let eg SSL variants separately cache. 01408 $key = wfMemcKey( $name . $wgRenderHashAppend ); 01409 $cachedNotice = $parserMemc->get( $key ); 01410 if( is_array( $cachedNotice ) ) { 01411 if( md5( $notice ) == $cachedNotice['hash'] ) { 01412 $notice = $cachedNotice['html']; 01413 } else { 01414 $needParse = true; 01415 } 01416 } else { 01417 $needParse = true; 01418 } 01419 01420 if ( $needParse ) { 01421 $parsed = $this->getOutput()->parse( $notice ); 01422 $parserMemc->set( $key, array( 'html' => $parsed, 'hash' => md5( $notice ) ), 600 ); 01423 $notice = $parsed; 01424 } 01425 01426 $notice = Html::rawElement( 'div', array( 'id' => 'localNotice', 01427 'lang' => $wgContLang->getHtmlCode(), 'dir' => $wgContLang->getDir() ), $notice ); 01428 wfProfileOut( __METHOD__ ); 01429 return $notice; 01430 } 01431 01437 function getNamespaceNotice() { 01438 wfProfileIn( __METHOD__ ); 01439 01440 $key = 'namespacenotice-' . $this->getTitle()->getNsText(); 01441 $namespaceNotice = $this->getCachedNotice( $key ); 01442 if ( $namespaceNotice && substr( $namespaceNotice, 0, 7 ) != '<p><' ) { 01443 $namespaceNotice = '<div id="namespacebanner">' . $namespaceNotice . '</div>'; 01444 } else { 01445 $namespaceNotice = ''; 01446 } 01447 01448 wfProfileOut( __METHOD__ ); 01449 return $namespaceNotice; 01450 } 01451 01457 function getSiteNotice() { 01458 wfProfileIn( __METHOD__ ); 01459 $siteNotice = ''; 01460 01461 if ( wfRunHooks( 'SiteNoticeBefore', array( &$siteNotice, $this ) ) ) { 01462 if ( is_object( $this->getUser() ) && $this->getUser()->isLoggedIn() ) { 01463 $siteNotice = $this->getCachedNotice( 'sitenotice' ); 01464 } else { 01465 $anonNotice = $this->getCachedNotice( 'anonnotice' ); 01466 if ( !$anonNotice ) { 01467 $siteNotice = $this->getCachedNotice( 'sitenotice' ); 01468 } else { 01469 $siteNotice = $anonNotice; 01470 } 01471 } 01472 if ( !$siteNotice ) { 01473 $siteNotice = $this->getCachedNotice( 'default' ); 01474 } 01475 } 01476 01477 wfRunHooks( 'SiteNoticeAfter', array( &$siteNotice, $this ) ); 01478 wfProfileOut( __METHOD__ ); 01479 return $siteNotice; 01480 } 01481 01495 public function doEditSectionLink( Title $nt, $section, $tooltip = null, $lang = false ) { 01496 // HTML generated here should probably have userlangattributes 01497 // added to it for LTR text on RTL pages 01498 $attribs = array(); 01499 if ( !is_null( $tooltip ) ) { 01500 # Bug 25462: undo double-escaping. 01501 $tooltip = Sanitizer::decodeCharReferences( $tooltip ); 01502 $attribs['title'] = wfMsgExt( 'editsectionhint', array( 'language' => $lang, 'parsemag', 'replaceafter' ), $tooltip ); 01503 } 01504 $link = Linker::link( $nt, wfMsgExt( 'editsection', array( 'language' => $lang ) ), 01505 $attribs, 01506 array( 'action' => 'edit', 'section' => $section ), 01507 array( 'noclasses', 'known' ) 01508 ); 01509 01510 # Run the old hook. This takes up half of the function . . . hopefully 01511 # we can rid of it someday. 01512 $attribs = ''; 01513 if ( $tooltip ) { 01514 $attribs = wfMsgExt( 'editsectionhint', array( 'language' => $lang, 'parsemag', 'escape', 'replaceafter' ), $tooltip ); 01515 $attribs = " title=\"$attribs\""; 01516 } 01517 $result = null; 01518 wfRunHooks( 'EditSectionLink', array( &$this, $nt, $section, $attribs, $link, &$result, $lang ) ); 01519 if ( !is_null( $result ) ) { 01520 # For reverse compatibility, add the brackets *after* the hook is 01521 # run, and even add them to hook-provided text. (This is the main 01522 # reason that the EditSectionLink hook is deprecated in favor of 01523 # DoEditSectionLink: it can't change the brackets or the span.) 01524 $result = wfMsgExt( 'editsection-brackets', array( 'escape', 'replaceafter', 'language' => $lang ), $result ); 01525 return "<span class=\"editsection\">$result</span>"; 01526 } 01527 01528 # Add the brackets and the span, and *then* run the nice new hook, with 01529 # clean and non-redundant arguments. 01530 $result = wfMsgExt( 'editsection-brackets', array( 'escape', 'replaceafter', 'language' => $lang ), $link ); 01531 $result = "<span class=\"editsection\">$result</span>"; 01532 01533 wfRunHooks( 'DoEditSectionLink', array( $this, $nt, $section, $tooltip, &$result, $lang ) ); 01534 return $result; 01535 } 01536 01544 function __call( $fname, $args ) { 01545 $realFunction = array( 'Linker', $fname ); 01546 if ( is_callable( $realFunction ) ) { 01547 return call_user_func_array( $realFunction, $args ); 01548 } else { 01549 $className = get_class( $this ); 01550 throw new MWException( "Call to undefined method $className::$fname" ); 01551 } 01552 } 01553 01554 }