MediaWiki
REL1_22
|
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 if ( $skinDir !== false && $skinDir !== null ) { 00060 # while code from www.php.net 00061 while ( false !== ( $file = $skinDir->read() ) ) { 00062 // Skip non-PHP files, hidden files, and '.dep' includes 00063 $matches = array(); 00064 00065 if ( preg_match( '/^([^.]*)\.php$/', $file, $matches ) ) { 00066 $aSkin = $matches[1]; 00067 $wgValidSkinNames[strtolower( $aSkin )] = $aSkin; 00068 } 00069 } 00070 $skinDir->close(); 00071 } 00072 $skinsInitialised = true; 00073 wfProfileOut( __METHOD__ . '-init' ); 00074 } 00075 return $wgValidSkinNames; 00076 } 00077 00082 static function getSkinNameMessages() { 00083 $messages = array(); 00084 foreach ( self::getSkinNames() as $skinKey => $skinName ) { 00085 // Messages: skinname-cologneblue, skinname-monobook, skinname-modern, skinname-vector 00086 $messages[] = "skinname-$skinKey"; 00087 } 00088 return $messages; 00089 } 00090 00097 public static function getUsableSkins() { 00098 global $wgSkipSkins; 00099 00100 $allowedSkins = self::getSkinNames(); 00101 00102 foreach ( $wgSkipSkins as $skip ) { 00103 unset( $allowedSkins[$skip] ); 00104 } 00105 00106 return $allowedSkins; 00107 } 00108 00116 static function normalizeKey( $key ) { 00117 global $wgDefaultSkin; 00118 00119 $skinNames = Skin::getSkinNames(); 00120 00121 if ( $key == '' || $key == 'default' ) { 00122 // Don't return the default immediately; 00123 // in a misconfiguration we need to fall back. 00124 $key = $wgDefaultSkin; 00125 } 00126 00127 if ( isset( $skinNames[$key] ) ) { 00128 return $key; 00129 } 00130 00131 // Older versions of the software used a numeric setting 00132 // in the user preferences. 00133 $fallback = array( 00134 0 => $wgDefaultSkin, 00135 2 => 'cologneblue' 00136 ); 00137 00138 if ( isset( $fallback[$key] ) ) { 00139 $key = $fallback[$key]; 00140 } 00141 00142 if ( isset( $skinNames[$key] ) ) { 00143 return $key; 00144 } elseif ( isset( $skinNames[$wgDefaultSkin] ) ) { 00145 return $wgDefaultSkin; 00146 } else { 00147 return 'vector'; 00148 } 00149 } 00150 00156 static function &newFromKey( $key ) { 00157 global $wgStyleDirectory; 00158 00159 $key = Skin::normalizeKey( $key ); 00160 00161 $skinNames = Skin::getSkinNames(); 00162 $skinName = $skinNames[$key]; 00163 $className = "Skin{$skinName}"; 00164 00165 # Grab the skin class and initialise it. 00166 if ( !class_exists( $className ) ) { 00167 00168 require_once "{$wgStyleDirectory}/{$skinName}.php"; 00169 00170 # Check if we got if not fallback to default skin 00171 if ( !class_exists( $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 require_once "{$wgStyleDirectory}/Vector.php"; 00179 } 00180 } 00181 $skin = new $className( $key ); 00182 return $skin; 00183 } 00184 00186 public function getSkinName() { 00187 return $this->skinname; 00188 } 00189 00193 function initPage( OutputPage $out ) { 00194 wfProfileIn( __METHOD__ ); 00195 00196 $this->preloadExistence(); 00197 00198 wfProfileOut( __METHOD__ ); 00199 } 00200 00207 public function getDefaultModules() { 00208 global $wgIncludeLegacyJavaScript, $wgPreloadJavaScriptMwUtil, $wgUseAjax, 00209 $wgAjaxWatch, $wgEnableAPI, $wgEnableWriteAPI; 00210 00211 $out = $this->getOutput(); 00212 $user = $out->getUser(); 00213 $modules = array( 00214 // modules that enhance the page content in some way 00215 'content' => array( 00216 'mediawiki.page.ready', 00217 ), 00218 // modules that exist for legacy reasons 00219 'legacy' => array(), 00220 // modules relating to search functionality 00221 'search' => array(), 00222 // modules relating to functionality relating to watching an article 00223 'watch' => array(), 00224 // modules which relate to the current users preferences 00225 'user' => array(), 00226 ); 00227 if ( $wgIncludeLegacyJavaScript ) { 00228 $modules['legacy'][] = 'mediawiki.legacy.wikibits'; 00229 } 00230 00231 if ( $wgPreloadJavaScriptMwUtil ) { 00232 $modules['legacy'][] = 'mediawiki.util'; 00233 } 00234 00235 // Add various resources if required 00236 if ( $wgUseAjax ) { 00237 $modules['legacy'][] = 'mediawiki.legacy.ajax'; 00238 00239 if ( $wgEnableAPI ) { 00240 if ( $wgEnableWriteAPI && $wgAjaxWatch && $user->isLoggedIn() 00241 && $user->isAllowed( 'writeapi' ) 00242 ) { 00243 $modules['watch'][] = 'mediawiki.page.watch.ajax'; 00244 } 00245 00246 if ( !$user->getOption( 'disablesuggest', false ) ) { 00247 $modules['search'][] = 'mediawiki.searchSuggest'; 00248 } 00249 } 00250 } 00251 00252 if ( $user->getBoolOption( 'editsectiononrightclick' ) ) { 00253 $modules['user'][] = 'mediawiki.action.view.rightClickEdit'; 00254 } 00255 00256 // Crazy edit-on-double-click stuff 00257 if ( $out->isArticle() && $user->getOption( 'editondblclick' ) ) { 00258 $modules['user'][] = 'mediawiki.action.view.dblClickEdit'; 00259 } 00260 return $modules; 00261 } 00262 00266 function preloadExistence() { 00267 $user = $this->getUser(); 00268 00269 // User/talk link 00270 $titles = array( $user->getUserPage(), $user->getTalkPage() ); 00271 00272 // Other tab link 00273 if ( $this->getTitle()->isSpecialPage() ) { 00274 // nothing 00275 } elseif ( $this->getTitle()->isTalkPage() ) { 00276 $titles[] = $this->getTitle()->getSubjectPage(); 00277 } else { 00278 $titles[] = $this->getTitle()->getTalkPage(); 00279 } 00280 00281 $lb = new LinkBatch( $titles ); 00282 $lb->setCaller( __METHOD__ ); 00283 $lb->execute(); 00284 } 00285 00291 public function getRevisionId() { 00292 return $this->getOutput()->getRevisionId(); 00293 } 00294 00300 public function isRevisionCurrent() { 00301 $revID = $this->getRevisionId(); 00302 return $revID == 0 || $revID == $this->getTitle()->getLatestRevID(); 00303 } 00304 00310 public function setRelevantTitle( $t ) { 00311 $this->mRelevantTitle = $t; 00312 } 00313 00324 public function getRelevantTitle() { 00325 if ( isset( $this->mRelevantTitle ) ) { 00326 return $this->mRelevantTitle; 00327 } 00328 return $this->getTitle(); 00329 } 00330 00336 public function setRelevantUser( $u ) { 00337 $this->mRelevantUser = $u; 00338 } 00339 00348 public function getRelevantUser() { 00349 if ( isset( $this->mRelevantUser ) ) { 00350 return $this->mRelevantUser; 00351 } 00352 $title = $this->getRelevantTitle(); 00353 if ( $title->hasSubjectNamespace( NS_USER ) ) { 00354 $rootUser = $title->getRootText(); 00355 if ( User::isIP( $rootUser ) ) { 00356 $this->mRelevantUser = User::newFromName( $rootUser, false ); 00357 } else { 00358 $user = User::newFromName( $rootUser, false ); 00359 if ( $user && $user->isLoggedIn() ) { 00360 $this->mRelevantUser = $user; 00361 } 00362 } 00363 return $this->mRelevantUser; 00364 } 00365 return null; 00366 } 00367 00372 abstract function outputPage( OutputPage $out = null ); 00373 00378 static function makeVariablesScript( $data ) { 00379 if ( $data ) { 00380 return Html::inlineScript( 00381 ResourceLoader::makeLoaderConditionalScript( ResourceLoader::makeConfigSetScript( $data ) ) 00382 ); 00383 } else { 00384 return ''; 00385 } 00386 } 00387 00395 public static function makeGlobalVariablesScript( $unused ) { 00396 global $wgOut; 00397 00398 wfDeprecated( __METHOD__, '1.19' ); 00399 00400 return self::makeVariablesScript( $wgOut->getJSVars() ); 00401 } 00402 00408 public static function getDynamicStylesheetQuery() { 00409 global $wgSquidMaxage; 00410 00411 return array( 00412 'action' => 'raw', 00413 'maxage' => $wgSquidMaxage, 00414 'usemsgcache' => 'yes', 00415 'ctype' => 'text/css', 00416 'smaxage' => $wgSquidMaxage, 00417 ); 00418 } 00419 00428 abstract function setupSkinUserCss( OutputPage $out ); 00429 00435 function getPageClasses( $title ) { 00436 $numeric = 'ns-' . $title->getNamespace(); 00437 00438 if ( $title->isSpecialPage() ) { 00439 $type = 'ns-special'; 00440 // bug 23315: provide a class based on the canonical special page name without subpages 00441 list( $canonicalName ) = SpecialPageFactory::resolveAlias( $title->getDBkey() ); 00442 if ( $canonicalName ) { 00443 $type .= ' ' . Sanitizer::escapeClass( "mw-special-$canonicalName" ); 00444 } else { 00445 $type .= ' mw-invalidspecialpage'; 00446 } 00447 } elseif ( $title->isTalkPage() ) { 00448 $type = 'ns-talk'; 00449 } else { 00450 $type = 'ns-subject'; 00451 } 00452 00453 $name = Sanitizer::escapeClass( 'page-' . $title->getPrefixedText() ); 00454 00455 return "$numeric $type $name"; 00456 } 00457 00465 function addToBodyAttributes( $out, &$bodyAttrs ) { 00466 // does nothing by default 00467 } 00468 00473 function getLogo() { 00474 global $wgLogo; 00475 return $wgLogo; 00476 } 00477 00481 function getCategoryLinks() { 00482 global $wgUseCategoryBrowser; 00483 00484 $out = $this->getOutput(); 00485 $allCats = $out->getCategoryLinks(); 00486 00487 if ( !count( $allCats ) ) { 00488 return ''; 00489 } 00490 00491 $embed = "<li>"; 00492 $pop = "</li>"; 00493 00494 $s = ''; 00495 $colon = $this->msg( 'colon-separator' )->escaped(); 00496 00497 if ( !empty( $allCats['normal'] ) ) { 00498 $t = $embed . implode( "{$pop}{$embed}", $allCats['normal'] ) . $pop; 00499 00500 $msg = $this->msg( 'pagecategories' )->numParams( count( $allCats['normal'] ) )->escaped(); 00501 $linkPage = wfMessage( 'pagecategorieslink' )->inContentLanguage()->text(); 00502 $s .= '<div id="mw-normal-catlinks" class="mw-normal-catlinks">' . 00503 Linker::link( Title::newFromText( $linkPage ), $msg ) 00504 . $colon . '<ul>' . $t . '</ul>' . '</div>'; 00505 } 00506 00507 # Hidden categories 00508 if ( isset( $allCats['hidden'] ) ) { 00509 if ( $this->getUser()->getBoolOption( 'showhiddencats' ) ) { 00510 $class = ' mw-hidden-cats-user-shown'; 00511 } elseif ( $this->getTitle()->getNamespace() == NS_CATEGORY ) { 00512 $class = ' mw-hidden-cats-ns-shown'; 00513 } else { 00514 $class = ' mw-hidden-cats-hidden'; 00515 } 00516 00517 $s .= "<div id=\"mw-hidden-catlinks\" class=\"mw-hidden-catlinks$class\">" . 00518 $this->msg( 'hidden-categories' )->numParams( count( $allCats['hidden'] ) )->escaped() . 00519 $colon . '<ul>' . $embed . implode( "{$pop}{$embed}", $allCats['hidden'] ) . $pop . '</ul>' . 00520 '</div>'; 00521 } 00522 00523 # optional 'dmoz-like' category browser. Will be shown under the list 00524 # of categories an article belong to 00525 if ( $wgUseCategoryBrowser ) { 00526 $s .= '<br /><hr />'; 00527 00528 # get a big array of the parents tree 00529 $parenttree = $this->getTitle()->getParentCategoryTree(); 00530 # Skin object passed by reference cause it can not be 00531 # accessed under the method subfunction drawCategoryBrowser 00532 $tempout = explode( "\n", $this->drawCategoryBrowser( $parenttree ) ); 00533 # Clean out bogus first entry and sort them 00534 unset( $tempout[0] ); 00535 asort( $tempout ); 00536 # Output one per line 00537 $s .= implode( "<br />\n", $tempout ); 00538 } 00539 00540 return $s; 00541 } 00542 00548 function drawCategoryBrowser( $tree ) { 00549 $return = ''; 00550 00551 foreach ( $tree as $element => $parent ) { 00552 if ( empty( $parent ) ) { 00553 # element start a new list 00554 $return .= "\n"; 00555 } else { 00556 # grab the others elements 00557 $return .= $this->drawCategoryBrowser( $parent ) . ' > '; 00558 } 00559 00560 # add our current element to the list 00561 $eltitle = Title::newFromText( $element ); 00562 $return .= Linker::link( $eltitle, htmlspecialchars( $eltitle->getText() ) ); 00563 } 00564 00565 return $return; 00566 } 00567 00571 function getCategories() { 00572 $out = $this->getOutput(); 00573 00574 $catlinks = $this->getCategoryLinks(); 00575 00576 $classes = 'catlinks'; 00577 00578 // Check what we're showing 00579 $allCats = $out->getCategoryLinks(); 00580 $showHidden = $this->getUser()->getBoolOption( 'showhiddencats' ) || 00581 $this->getTitle()->getNamespace() == NS_CATEGORY; 00582 00583 if ( empty( $allCats['normal'] ) && !( !empty( $allCats['hidden'] ) && $showHidden ) ) { 00584 $classes .= ' catlinks-allhidden'; 00585 } 00586 00587 return "<div id='catlinks' class='$classes'>{$catlinks}</div>"; 00588 } 00589 00604 protected function afterContentHook() { 00605 $data = ''; 00606 00607 if ( wfRunHooks( 'SkinAfterContent', array( &$data, $this ) ) ) { 00608 // adding just some spaces shouldn't toggle the output 00609 // of the whole <div/>, so we use trim() here 00610 if ( trim( $data ) != '' ) { 00611 // Doing this here instead of in the skins to 00612 // ensure that the div has the same ID in all 00613 // skins 00614 $data = "<div id='mw-data-after-content'>\n" . 00615 "\t$data\n" . 00616 "</div>\n"; 00617 } 00618 } else { 00619 wfDebug( "Hook SkinAfterContent changed output processing.\n" ); 00620 } 00621 00622 return $data; 00623 } 00624 00630 protected function generateDebugHTML() { 00631 return MWDebug::getHTMLDebugLog(); 00632 } 00633 00639 function bottomScripts() { 00640 // TODO and the suckage continues. This function is really just a wrapper around 00641 // OutputPage::getBottomScripts() which takes a Skin param. This should be cleaned 00642 // up at some point 00643 $bottomScriptText = $this->getOutput()->getBottomScripts(); 00644 wfRunHooks( 'SkinAfterBottomScripts', array( $this, &$bottomScriptText ) ); 00645 00646 return $bottomScriptText; 00647 } 00648 00655 function printSource() { 00656 $oldid = $this->getRevisionId(); 00657 if ( $oldid ) { 00658 $url = htmlspecialchars( wfExpandIRI( $this->getTitle()->getCanonicalURL( 'oldid=' . $oldid ) ) ); 00659 } else { 00660 // oldid not available for non existing pages 00661 $url = htmlspecialchars( wfExpandIRI( $this->getTitle()->getCanonicalURL() ) ); 00662 } 00663 return $this->msg( 'retrievedfrom', '<a href="' . $url . '">' . $url . '</a>' )->text(); 00664 } 00665 00669 function getUndeleteLink() { 00670 $action = $this->getRequest()->getVal( 'action', 'view' ); 00671 00672 if ( $this->getUser()->isAllowed( 'deletedhistory' ) && 00673 ( $this->getTitle()->getArticleID() == 0 || $action == 'history' ) ) { 00674 $n = $this->getTitle()->isDeleted(); 00675 00676 if ( $n ) { 00677 if ( $this->getUser()->isAllowed( 'undelete' ) ) { 00678 $msg = 'thisisdeleted'; 00679 } else { 00680 $msg = 'viewdeleted'; 00681 } 00682 00683 return $this->msg( $msg )->rawParams( 00684 Linker::linkKnown( 00685 SpecialPage::getTitleFor( 'Undelete', $this->getTitle()->getPrefixedDBkey() ), 00686 $this->msg( 'restorelink' )->numParams( $n )->escaped() ) 00687 )->text(); 00688 } 00689 } 00690 00691 return ''; 00692 } 00693 00697 function subPageSubtitle() { 00698 global $wgLang; 00699 $out = $this->getOutput(); 00700 $subpages = ''; 00701 00702 if ( !wfRunHooks( 'SkinSubPageSubtitle', array( &$subpages, $this, $out ) ) ) { 00703 return $subpages; 00704 } 00705 00706 if ( $out->isArticle() && MWNamespace::hasSubpages( $out->getTitle()->getNamespace() ) ) { 00707 $ptext = $this->getTitle()->getPrefixedText(); 00708 if ( preg_match( '/\//', $ptext ) ) { 00709 $links = explode( '/', $ptext ); 00710 array_pop( $links ); 00711 $c = 0; 00712 $growinglink = ''; 00713 $display = ''; 00714 00715 foreach ( $links as $link ) { 00716 $growinglink .= $link; 00717 $display .= $link; 00718 $linkObj = Title::newFromText( $growinglink ); 00719 00720 if ( is_object( $linkObj ) && $linkObj->isKnown() ) { 00721 $getlink = Linker::linkKnown( 00722 $linkObj, 00723 htmlspecialchars( $display ) 00724 ); 00725 00726 $c++; 00727 00728 if ( $c > 1 ) { 00729 $subpages .= $wgLang->getDirMarkEntity() . $this->msg( 'pipe-separator' )->escaped(); 00730 } else { 00731 $subpages .= '< '; 00732 } 00733 00734 $subpages .= $getlink; 00735 $display = ''; 00736 } else { 00737 $display .= '/'; 00738 } 00739 $growinglink .= '/'; 00740 } 00741 } 00742 } 00743 00744 return $subpages; 00745 } 00746 00751 function showIPinHeader() { 00752 global $wgShowIPinHeader; 00753 return $wgShowIPinHeader && session_id() != ''; 00754 } 00755 00759 function getSearchLink() { 00760 $searchPage = SpecialPage::getTitleFor( 'Search' ); 00761 return $searchPage->getLocalURL(); 00762 } 00763 00767 function escapeSearchLink() { 00768 return htmlspecialchars( $this->getSearchLink() ); 00769 } 00770 00775 function getCopyright( $type = 'detect' ) { 00776 global $wgRightsPage, $wgRightsUrl, $wgRightsText, $wgContLang; 00777 00778 if ( $type == 'detect' ) { 00779 if ( !$this->isRevisionCurrent() && !$this->msg( 'history_copyright' )->inContentLanguage()->isDisabled() ) { 00780 $type = 'history'; 00781 } else { 00782 $type = 'normal'; 00783 } 00784 } 00785 00786 if ( $type == 'history' ) { 00787 $msg = 'history_copyright'; 00788 } else { 00789 $msg = 'copyright'; 00790 } 00791 00792 if ( $wgRightsPage ) { 00793 $title = Title::newFromText( $wgRightsPage ); 00794 $link = Linker::linkKnown( $title, $wgRightsText ); 00795 } elseif ( $wgRightsUrl ) { 00796 $link = Linker::makeExternalLink( $wgRightsUrl, $wgRightsText ); 00797 } elseif ( $wgRightsText ) { 00798 $link = $wgRightsText; 00799 } else { 00800 # Give up now 00801 return ''; 00802 } 00803 00804 // Allow for site and per-namespace customization of copyright notice. 00805 $forContent = true; 00806 00807 wfRunHooks( 'SkinCopyrightFooter', array( $this->getTitle(), $type, &$msg, &$link, &$forContent ) ); 00808 00809 $msgObj = $this->msg( $msg )->rawParams( $link ); 00810 if ( $forContent ) { 00811 $msg = $msgObj->inContentLanguage()->text(); 00812 if ( $this->getLanguage()->getCode() !== $wgContLang->getCode() ) { 00813 $msg = Html::rawElement( 'span', array( 'lang' => $wgContLang->getHtmlCode(), 'dir' => $wgContLang->getDir() ), $msg ); 00814 } 00815 return $msg; 00816 } else { 00817 return $msgObj->text(); 00818 } 00819 } 00820 00824 function getCopyrightIcon() { 00825 global $wgRightsUrl, $wgRightsText, $wgRightsIcon, $wgCopyrightIcon; 00826 00827 $out = ''; 00828 00829 if ( isset( $wgCopyrightIcon ) && $wgCopyrightIcon ) { 00830 $out = $wgCopyrightIcon; 00831 } elseif ( $wgRightsIcon ) { 00832 $icon = htmlspecialchars( $wgRightsIcon ); 00833 00834 if ( $wgRightsUrl ) { 00835 $url = htmlspecialchars( $wgRightsUrl ); 00836 $out .= '<a href="' . $url . '">'; 00837 } 00838 00839 $text = htmlspecialchars( $wgRightsText ); 00840 $out .= "<img src=\"$icon\" alt=\"$text\" width=\"88\" height=\"31\" />"; 00841 00842 if ( $wgRightsUrl ) { 00843 $out .= '</a>'; 00844 } 00845 } 00846 00847 return $out; 00848 } 00849 00854 function getPoweredBy() { 00855 global $wgStylePath; 00856 00857 $url = htmlspecialchars( "$wgStylePath/common/images/poweredby_mediawiki_88x31.png" ); 00858 $text = '<a href="//www.mediawiki.org/"><img src="' . $url . '" height="31" width="88" alt="Powered by MediaWiki" /></a>'; 00859 wfRunHooks( 'SkinGetPoweredBy', array( &$text, $this ) ); 00860 return $text; 00861 } 00862 00868 protected function lastModified() { 00869 $timestamp = $this->getOutput()->getRevisionTimestamp(); 00870 00871 # No cached timestamp, load it from the database 00872 if ( $timestamp === null ) { 00873 $timestamp = Revision::getTimestampFromId( $this->getTitle(), $this->getRevisionId() ); 00874 } 00875 00876 if ( $timestamp ) { 00877 $d = $this->getLanguage()->userDate( $timestamp, $this->getUser() ); 00878 $t = $this->getLanguage()->userTime( $timestamp, $this->getUser() ); 00879 $s = ' ' . $this->msg( 'lastmodifiedat', $d, $t )->text(); 00880 } else { 00881 $s = ''; 00882 } 00883 00884 if ( wfGetLB()->getLaggedSlaveMode() ) { 00885 $s .= ' <strong>' . $this->msg( 'laggedslavemode' )->text() . '</strong>'; 00886 } 00887 00888 return $s; 00889 } 00890 00895 function logoText( $align = '' ) { 00896 if ( $align != '' ) { 00897 $a = " style='float: {$align};'"; 00898 } else { 00899 $a = ''; 00900 } 00901 00902 $mp = $this->msg( 'mainpage' )->escaped(); 00903 $mptitle = Title::newMainPage(); 00904 $url = ( is_object( $mptitle ) ? htmlspecialchars( $mptitle->getLocalURL() ) : '' ); 00905 00906 $logourl = $this->getLogo(); 00907 $s = "<a href='{$url}'><img{$a} src='{$logourl}' alt='[{$mp}]' /></a>"; 00908 00909 return $s; 00910 } 00911 00918 function makeFooterIcon( $icon, $withImage = 'withImage' ) { 00919 if ( is_string( $icon ) ) { 00920 $html = $icon; 00921 } else { // Assuming array 00922 $url = isset( $icon["url"] ) ? $icon["url"] : null; 00923 unset( $icon["url"] ); 00924 if ( isset( $icon["src"] ) && $withImage === 'withImage' ) { 00925 $html = Html::element( 'img', $icon ); // do this the lazy way, just pass icon data as an attribute array 00926 } else { 00927 $html = htmlspecialchars( $icon["alt"] ); 00928 } 00929 if ( $url ) { 00930 $html = Html::rawElement( 'a', array( "href" => $url ), $html ); 00931 } 00932 } 00933 return $html; 00934 } 00935 00940 function mainPageLink() { 00941 $s = Linker::linkKnown( 00942 Title::newMainPage(), 00943 $this->msg( 'mainpage' )->escaped() 00944 ); 00945 00946 return $s; 00947 } 00948 00954 public function footerLink( $desc, $page ) { 00955 // if the link description has been set to "-" in the default language, 00956 if ( $this->msg( $desc )->inContentLanguage()->isDisabled() ) { 00957 // then it is disabled, for all languages. 00958 return ''; 00959 } else { 00960 // Otherwise, we display the link for the user, described in their 00961 // language (which may or may not be the same as the default language), 00962 // but we make the link target be the one site-wide page. 00963 $title = Title::newFromText( $this->msg( $page )->inContentLanguage()->text() ); 00964 00965 return Linker::linkKnown( 00966 $title, 00967 $this->msg( $desc )->escaped() 00968 ); 00969 } 00970 } 00971 00976 function privacyLink() { 00977 return $this->footerLink( 'privacy', 'privacypage' ); 00978 } 00979 00984 function aboutLink() { 00985 return $this->footerLink( 'aboutsite', 'aboutpage' ); 00986 } 00987 00992 function disclaimerLink() { 00993 return $this->footerLink( 'disclaimers', 'disclaimerpage' ); 00994 } 00995 01003 function editUrlOptions() { 01004 $options = array( 'action' => 'edit' ); 01005 01006 if ( !$this->isRevisionCurrent() ) { 01007 $options['oldid'] = intval( $this->getRevisionId() ); 01008 } 01009 01010 return $options; 01011 } 01012 01017 function showEmailUser( $id ) { 01018 if ( $id instanceof User ) { 01019 $targetUser = $id; 01020 } else { 01021 $targetUser = User::newFromId( $id ); 01022 } 01023 return $this->getUser()->canSendEmail() && # the sending user must have a confirmed email address 01024 $targetUser->canReceiveEmail(); # the target user must have a confirmed email address and allow emails from users 01025 } 01026 01034 function getCommonStylePath( $name ) { 01035 global $wgStylePath, $wgStyleVersion; 01036 return "$wgStylePath/common/$name?$wgStyleVersion"; 01037 } 01038 01046 function getSkinStylePath( $name ) { 01047 global $wgStylePath, $wgStyleVersion; 01048 return "$wgStylePath/{$this->stylename}/$name?$wgStyleVersion"; 01049 } 01050 01051 /* these are used extensively in SkinTemplate, but also some other places */ 01052 01057 static function makeMainPageUrl( $urlaction = '' ) { 01058 $title = Title::newMainPage(); 01059 self::checkTitle( $title, '' ); 01060 01061 return $title->getLocalURL( $urlaction ); 01062 } 01063 01075 static function makeSpecialUrl( $name, $urlaction = '', $proto = null ) { 01076 $title = SpecialPage::getSafeTitleFor( $name ); 01077 if ( is_null( $proto ) ) { 01078 return $title->getLocalURL( $urlaction ); 01079 } else { 01080 return $title->getFullURL( $urlaction, false, $proto ); 01081 } 01082 } 01083 01090 static function makeSpecialUrlSubpage( $name, $subpage, $urlaction = '' ) { 01091 $title = SpecialPage::getSafeTitleFor( $name, $subpage ); 01092 return $title->getLocalURL( $urlaction ); 01093 } 01094 01100 static function makeI18nUrl( $name, $urlaction = '' ) { 01101 $title = Title::newFromText( wfMessage( $name )->inContentLanguage()->text() ); 01102 self::checkTitle( $title, $name ); 01103 return $title->getLocalURL( $urlaction ); 01104 } 01105 01111 static function makeUrl( $name, $urlaction = '' ) { 01112 $title = Title::newFromText( $name ); 01113 self::checkTitle( $title, $name ); 01114 01115 return $title->getLocalURL( $urlaction ); 01116 } 01117 01124 static function makeInternalOrExternalUrl( $name ) { 01125 if ( preg_match( '/^(?i:' . wfUrlProtocols() . ')/', $name ) ) { 01126 return $name; 01127 } else { 01128 return self::makeUrl( $name ); 01129 } 01130 } 01131 01139 static function makeNSUrl( $name, $urlaction = '', $namespace = NS_MAIN ) { 01140 $title = Title::makeTitleSafe( $namespace, $name ); 01141 self::checkTitle( $title, $name ); 01142 01143 return $title->getLocalURL( $urlaction ); 01144 } 01145 01152 static function makeUrlDetails( $name, $urlaction = '' ) { 01153 $title = Title::newFromText( $name ); 01154 self::checkTitle( $title, $name ); 01155 01156 return array( 01157 'href' => $title->getLocalURL( $urlaction ), 01158 'exists' => $title->getArticleID() != 0, 01159 ); 01160 } 01161 01168 static function makeKnownUrlDetails( $name, $urlaction = '' ) { 01169 $title = Title::newFromText( $name ); 01170 self::checkTitle( $title, $name ); 01171 01172 return array( 01173 'href' => $title->getLocalURL( $urlaction ), 01174 'exists' => true 01175 ); 01176 } 01177 01184 static function checkTitle( &$title, $name ) { 01185 if ( !is_object( $title ) ) { 01186 $title = Title::newFromText( $name ); 01187 if ( !is_object( $title ) ) { 01188 $title = Title::newFromText( '--error: link target missing--' ); 01189 } 01190 } 01191 } 01192 01214 function buildSidebar() { 01215 global $wgMemc, $wgEnableSidebarCache, $wgSidebarCacheExpiry; 01216 wfProfileIn( __METHOD__ ); 01217 01218 $key = wfMemcKey( 'sidebar', $this->getLanguage()->getCode() ); 01219 01220 if ( $wgEnableSidebarCache ) { 01221 $cachedsidebar = $wgMemc->get( $key ); 01222 if ( $cachedsidebar ) { 01223 wfProfileOut( __METHOD__ ); 01224 return $cachedsidebar; 01225 } 01226 } 01227 01228 $bar = array(); 01229 $this->addToSidebar( $bar, 'sidebar' ); 01230 01231 wfRunHooks( 'SkinBuildSidebar', array( $this, &$bar ) ); 01232 if ( $wgEnableSidebarCache ) { 01233 $wgMemc->set( $key, $bar, $wgSidebarCacheExpiry ); 01234 } 01235 01236 wfProfileOut( __METHOD__ ); 01237 return $bar; 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 01383 if ( !$uTalkTitle->equals( $out->getTitle() ) ) { 01384 $lastSeenRev = isset( $newtalks[0]['rev'] ) ? $newtalks[0]['rev'] : null; 01385 $nofAuthors = 0; 01386 if ( $lastSeenRev !== null ) { 01387 $plural = true; // Default if we have a last seen revision: if unknown, use plural 01388 $latestRev = Revision::newFromTitle( $uTalkTitle, false, Revision::READ_NORMAL ); 01389 if ( $latestRev !== null ) { 01390 // Singular if only 1 unseen revision, plural if several unseen revisions. 01391 $plural = $latestRev->getParentId() !== $lastSeenRev->getId(); 01392 $nofAuthors = $uTalkTitle->countAuthorsBetween( 01393 $lastSeenRev, $latestRev, 10, 'include_new' ); 01394 } 01395 } else { 01396 // Singular if no revision -> diff link will show latest change only in any case 01397 $plural = false; 01398 } 01399 $plural = $plural ? 2 : 1; 01400 // 2 signifies "more than one revision". We don't know how many, and even if we did, 01401 // the number of revisions or authors is not necessarily the same as the number of 01402 // "messages". 01403 $newMessagesLink = Linker::linkKnown( 01404 $uTalkTitle, 01405 $this->msg( 'newmessageslinkplural' )->params( $plural )->escaped(), 01406 array(), 01407 array( 'redirect' => 'no' ) 01408 ); 01409 01410 $newMessagesDiffLink = Linker::linkKnown( 01411 $uTalkTitle, 01412 $this->msg( 'newmessagesdifflinkplural' )->params( $plural )->escaped(), 01413 array(), 01414 $lastSeenRev !== null 01415 ? array( 'oldid' => $lastSeenRev->getId(), 'diff' => 'cur' ) 01416 : array( 'diff' => 'cur' ) 01417 ); 01418 01419 if ( $nofAuthors >= 1 && $nofAuthors <= 10 ) { 01420 $newMessagesAlert = $this->msg( 01421 'youhavenewmessagesfromusers', 01422 $newMessagesLink, 01423 $newMessagesDiffLink 01424 )->numParams( $nofAuthors ); 01425 } else { 01426 // $nofAuthors === 11 signifies "11 or more" ("more than 10") 01427 $newMessagesAlert = $this->msg( 01428 $nofAuthors > 10 ? 'youhavenewmessagesmanyusers' : 'youhavenewmessages', 01429 $newMessagesLink, 01430 $newMessagesDiffLink 01431 ); 01432 } 01433 $newMessagesAlert = $newMessagesAlert->text(); 01434 # Disable Squid cache 01435 $out->setSquidMaxage( 0 ); 01436 } 01437 } elseif ( count( $newtalks ) ) { 01438 $sep = $this->msg( 'newtalkseparator' )->escaped(); 01439 $msgs = array(); 01440 01441 foreach ( $newtalks as $newtalk ) { 01442 $msgs[] = Xml::element( 01443 'a', 01444 array( 'href' => $newtalk['link'] ), $newtalk['wiki'] 01445 ); 01446 } 01447 $parts = implode( $sep, $msgs ); 01448 $newMessagesAlert = $this->msg( 'youhavenewmessagesmulti' )->rawParams( $parts )->escaped(); 01449 $out->setSquidMaxage( 0 ); 01450 } 01451 01452 return $newMessagesAlert; 01453 } 01454 01461 private function getCachedNotice( $name ) { 01462 global $wgRenderHashAppend, $parserMemc, $wgContLang; 01463 01464 wfProfileIn( __METHOD__ ); 01465 01466 $needParse = false; 01467 01468 if ( $name === 'default' ) { 01469 // special case 01470 global $wgSiteNotice; 01471 $notice = $wgSiteNotice; 01472 if ( empty( $notice ) ) { 01473 wfProfileOut( __METHOD__ ); 01474 return false; 01475 } 01476 } else { 01477 $msg = $this->msg( $name )->inContentLanguage(); 01478 if ( $msg->isDisabled() ) { 01479 wfProfileOut( __METHOD__ ); 01480 return false; 01481 } 01482 $notice = $msg->plain(); 01483 } 01484 01485 // Use the extra hash appender to let eg SSL variants separately cache. 01486 $key = wfMemcKey( $name . $wgRenderHashAppend ); 01487 $cachedNotice = $parserMemc->get( $key ); 01488 if ( is_array( $cachedNotice ) ) { 01489 if ( md5( $notice ) == $cachedNotice['hash'] ) { 01490 $notice = $cachedNotice['html']; 01491 } else { 01492 $needParse = true; 01493 } 01494 } else { 01495 $needParse = true; 01496 } 01497 01498 if ( $needParse ) { 01499 $parsed = $this->getOutput()->parse( $notice ); 01500 $parserMemc->set( $key, array( 'html' => $parsed, 'hash' => md5( $notice ) ), 600 ); 01501 $notice = $parsed; 01502 } 01503 01504 $notice = Html::rawElement( 'div', array( 'id' => 'localNotice', 01505 'lang' => $wgContLang->getHtmlCode(), 'dir' => $wgContLang->getDir() ), $notice ); 01506 wfProfileOut( __METHOD__ ); 01507 return $notice; 01508 } 01509 01515 function getNamespaceNotice() { 01516 wfProfileIn( __METHOD__ ); 01517 01518 $key = 'namespacenotice-' . $this->getTitle()->getNsText(); 01519 $namespaceNotice = $this->getCachedNotice( $key ); 01520 if ( $namespaceNotice && substr( $namespaceNotice, 0, 7 ) != '<p><' ) { 01521 $namespaceNotice = '<div id="namespacebanner">' . $namespaceNotice . '</div>'; 01522 } else { 01523 $namespaceNotice = ''; 01524 } 01525 01526 wfProfileOut( __METHOD__ ); 01527 return $namespaceNotice; 01528 } 01529 01535 function getSiteNotice() { 01536 wfProfileIn( __METHOD__ ); 01537 $siteNotice = ''; 01538 01539 if ( wfRunHooks( 'SiteNoticeBefore', array( &$siteNotice, $this ) ) ) { 01540 if ( is_object( $this->getUser() ) && $this->getUser()->isLoggedIn() ) { 01541 $siteNotice = $this->getCachedNotice( 'sitenotice' ); 01542 } else { 01543 $anonNotice = $this->getCachedNotice( 'anonnotice' ); 01544 if ( !$anonNotice ) { 01545 $siteNotice = $this->getCachedNotice( 'sitenotice' ); 01546 } else { 01547 $siteNotice = $anonNotice; 01548 } 01549 } 01550 if ( !$siteNotice ) { 01551 $siteNotice = $this->getCachedNotice( 'default' ); 01552 } 01553 } 01554 01555 wfRunHooks( 'SiteNoticeAfter', array( &$siteNotice, $this ) ); 01556 wfProfileOut( __METHOD__ ); 01557 return $siteNotice; 01558 } 01559 01573 public function doEditSectionLink( Title $nt, $section, $tooltip = null, $lang = false ) { 01574 // HTML generated here should probably have userlangattributes 01575 // added to it for LTR text on RTL pages 01576 01577 $lang = wfGetLangObj( $lang ); 01578 01579 $attribs = array(); 01580 if ( !is_null( $tooltip ) ) { 01581 # Bug 25462: undo double-escaping. 01582 $tooltip = Sanitizer::decodeCharReferences( $tooltip ); 01583 $attribs['title'] = wfMessage( 'editsectionhint' )->rawParams( $tooltip ) 01584 ->inLanguage( $lang )->text(); 01585 } 01586 $link = Linker::link( $nt, wfMessage( 'editsection' )->inLanguage( $lang )->text(), 01587 $attribs, 01588 array( 'action' => 'edit', 'section' => $section ), 01589 array( 'noclasses', 'known' ) 01590 ); 01591 01592 # Add the brackets and the span and run the hook. 01593 $result = '<span class="mw-editsection">' 01594 . '<span class="mw-editsection-bracket">[</span>' 01595 . $link 01596 . '<span class="mw-editsection-bracket">]</span>' 01597 . '</span>'; 01598 01599 wfRunHooks( 'DoEditSectionLink', array( $this, $nt, $section, $tooltip, &$result, $lang ) ); 01600 return $result; 01601 } 01602 01612 function __call( $fname, $args ) { 01613 $realFunction = array( 'Linker', $fname ); 01614 if ( is_callable( $realFunction ) ) { 01615 wfDeprecated( get_class( $this ) . '::' . $fname, '1.21' ); 01616 return call_user_func_array( $realFunction, $args ); 01617 } else { 01618 $className = get_class( $this ); 01619 throw new MWException( "Call to undefined method $className::$fname" ); 01620 } 01621 } 01622 01623 }