MediaWiki  REL1_22
Skin.php
Go to the documentation of this file.
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 ) . ' &gt; ';
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 .= '&lt; ';
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>&lt;' ) {
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 }