[ Index ]

PHP Cross Reference of MediaWiki-1.24.0

title

Body

[close]

/includes/skins/ -> Skin.php (source)

   1  <?php
   2  /**
   3   * Base class for all skins.
   4   *
   5   * This program is free software; you can redistribute it and/or modify
   6   * it under the terms of the GNU General Public License as published by
   7   * the Free Software Foundation; either version 2 of the License, or
   8   * (at your option) any later version.
   9   *
  10   * This program is distributed in the hope that it will be useful,
  11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13   * GNU General Public License for more details.
  14   *
  15   * You should have received a copy of the GNU General Public License along
  16   * with this program; if not, write to the Free Software Foundation, Inc.,
  17   * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18   * http://www.gnu.org/copyleft/gpl.html
  19   *
  20   * @file
  21   */
  22  
  23  /**
  24   * @defgroup Skins Skins
  25   */
  26  
  27  /**
  28   * The main skin class which provides methods and properties for all other skins.
  29   *
  30   * See docs/skin.txt for more information.
  31   *
  32   * @ingroup Skins
  33   */
  34  abstract class Skin extends ContextSource {
  35      protected $skinname = null;
  36      protected $mRelevantTitle = null;
  37      protected $mRelevantUser = null;
  38  
  39      /**
  40       * @var string Stylesheets set to use. Subdirectory in skins/ where various stylesheets are
  41       *   located. Only needs to be set if you intend to use the getSkinStylePath() method.
  42       */
  43      public $stylename = null;
  44  
  45      /**
  46       * Fetch the set of available skins.
  47       * @return array Associative array of strings
  48       */
  49  	static function getSkinNames() {
  50          return SkinFactory::getDefaultInstance()->getSkinNames();
  51      }
  52  
  53      /**
  54       * Fetch the skinname messages for available skins.
  55       * @return string[]
  56       */
  57  	static function getSkinNameMessages() {
  58          $messages = array();
  59          foreach ( self::getSkinNames() as $skinKey => $skinName ) {
  60              $messages[] = "skinname-$skinKey";
  61          }
  62          return $messages;
  63      }
  64  
  65      /**
  66       * Fetch the list of user-selectable skins in regards to $wgSkipSkins.
  67       * Useful for Special:Preferences and other places where you
  68       * only want to show skins users _can_ use.
  69       * @return string[]
  70       * @since 1.23
  71       */
  72  	public static function getAllowedSkins() {
  73          global $wgSkipSkins;
  74  
  75          $allowedSkins = self::getSkinNames();
  76  
  77          foreach ( $wgSkipSkins as $skip ) {
  78              unset( $allowedSkins[$skip] );
  79          }
  80  
  81          return $allowedSkins;
  82      }
  83  
  84      /**
  85       * @deprecated since 1.23, use getAllowedSkins
  86       * @return string[]
  87       */
  88  	public static function getUsableSkins() {
  89          wfDeprecated( __METHOD__, '1.23' );
  90          return self::getAllowedSkins();
  91      }
  92  
  93      /**
  94       * Normalize a skin preference value to a form that can be loaded.
  95       *
  96       * If a skin can't be found, it will fall back to the configured default ($wgDefaultSkin), or the
  97       * hardcoded default ($wgFallbackSkin) if the default skin is unavailable too.
  98       *
  99       * @param string $key 'monobook', 'vector', etc.
 100       * @return string
 101       */
 102  	static function normalizeKey( $key ) {
 103          global $wgDefaultSkin, $wgFallbackSkin;
 104  
 105          $skinNames = Skin::getSkinNames();
 106  
 107          // Make keys lowercase for case-insensitive matching.
 108          $skinNames = array_change_key_case( $skinNames, CASE_LOWER );
 109          $key = strtolower( $key );
 110          $defaultSkin = strtolower( $wgDefaultSkin );
 111          $fallbackSkin = strtolower( $wgFallbackSkin );
 112  
 113          if ( $key == '' || $key == 'default' ) {
 114              // Don't return the default immediately;
 115              // in a misconfiguration we need to fall back.
 116              $key = $defaultSkin;
 117          }
 118  
 119          if ( isset( $skinNames[$key] ) ) {
 120              return $key;
 121          }
 122  
 123          // Older versions of the software used a numeric setting
 124          // in the user preferences.
 125          $fallback = array(
 126              0 => $defaultSkin,
 127              2 => 'cologneblue'
 128          );
 129  
 130          if ( isset( $fallback[$key] ) ) {
 131              $key = $fallback[$key];
 132          }
 133  
 134          if ( isset( $skinNames[$key] ) ) {
 135              return $key;
 136          } elseif ( isset( $skinNames[$defaultSkin] ) ) {
 137              return $defaultSkin;
 138          } else {
 139              return $fallbackSkin;
 140          }
 141      }
 142  
 143      /**
 144       * Factory method for loading a skin of a given type
 145       * @param string $key 'monobook', 'vector', etc.
 146       * @return Skin
 147       * @deprecated since 1.24; Use SkinFactory instead
 148       */
 149      static function &newFromKey( $key ) {
 150          wfDeprecated( __METHOD__, '1.24' );
 151  
 152          $key = Skin::normalizeKey( $key );
 153          $factory = SkinFactory::getDefaultInstance();
 154  
 155          // normalizeKey() guarantees that a skin with this key will exist.
 156          $skin = $factory->makeSkin( $key );
 157          return $skin;
 158      }
 159  
 160      /**
 161       * @return string Skin name
 162       */
 163  	public function getSkinName() {
 164          return $this->skinname;
 165      }
 166  
 167      /**
 168       * @param OutputPage $out
 169       */
 170  	function initPage( OutputPage $out ) {
 171          wfProfileIn( __METHOD__ );
 172  
 173          $this->preloadExistence();
 174  
 175          wfProfileOut( __METHOD__ );
 176      }
 177  
 178      /**
 179       * Defines the ResourceLoader modules that should be added to the skin
 180       * It is recommended that skins wishing to override call parent::getDefaultModules()
 181       * and substitute out any modules they wish to change by using a key to look them up
 182       * @return array Array of modules with helper keys for easy overriding
 183       */
 184  	public function getDefaultModules() {
 185          global $wgIncludeLegacyJavaScript, $wgPreloadJavaScriptMwUtil, $wgUseAjax,
 186              $wgAjaxWatch, $wgEnableAPI, $wgEnableWriteAPI;
 187  
 188          $out = $this->getOutput();
 189          $user = $out->getUser();
 190          $modules = array(
 191              // modules that enhance the page content in some way
 192              'content' => array(
 193                  'mediawiki.page.ready',
 194              ),
 195              // modules that exist for legacy reasons
 196              'legacy' => array(),
 197              // modules relating to search functionality
 198              'search' => array(),
 199              // modules relating to functionality relating to watching an article
 200              'watch' => array(),
 201              // modules which relate to the current users preferences
 202              'user' => array(),
 203          );
 204          if ( $wgIncludeLegacyJavaScript ) {
 205              $modules['legacy'][] = 'mediawiki.legacy.wikibits';
 206          }
 207  
 208          if ( $wgPreloadJavaScriptMwUtil ) {
 209              $modules['legacy'][] = 'mediawiki.util';
 210          }
 211  
 212          // Add various resources if required
 213          if ( $wgUseAjax ) {
 214              $modules['legacy'][] = 'mediawiki.legacy.ajax';
 215  
 216              if ( $wgEnableAPI ) {
 217                  if ( $wgEnableWriteAPI && $wgAjaxWatch && $user->isLoggedIn()
 218                      && $user->isAllowed( 'writeapi' )
 219                  ) {
 220                      $modules['watch'][] = 'mediawiki.page.watch.ajax';
 221                  }
 222  
 223                  $modules['search'][] = 'mediawiki.searchSuggest';
 224              }
 225          }
 226  
 227          if ( $user->getBoolOption( 'editsectiononrightclick' ) ) {
 228              $modules['user'][] = 'mediawiki.action.view.rightClickEdit';
 229          }
 230  
 231          // Crazy edit-on-double-click stuff
 232          if ( $out->isArticle() && $user->getOption( 'editondblclick' ) ) {
 233              $modules['user'][] = 'mediawiki.action.view.dblClickEdit';
 234          }
 235          return $modules;
 236      }
 237  
 238      /**
 239       * Preload the existence of three commonly-requested pages in a single query
 240       */
 241  	function preloadExistence() {
 242          $user = $this->getUser();
 243  
 244          // User/talk link
 245          $titles = array( $user->getUserPage(), $user->getTalkPage() );
 246  
 247          // Other tab link
 248          if ( $this->getTitle()->isSpecialPage() ) {
 249              // nothing
 250          } elseif ( $this->getTitle()->isTalkPage() ) {
 251              $titles[] = $this->getTitle()->getSubjectPage();
 252          } else {
 253              $titles[] = $this->getTitle()->getTalkPage();
 254          }
 255  
 256          wfRunHooks( 'SkinPreloadExistence', array( &$titles, $this ) );
 257  
 258          $lb = new LinkBatch( $titles );
 259          $lb->setCaller( __METHOD__ );
 260          $lb->execute();
 261      }
 262  
 263      /**
 264       * Get the current revision ID
 265       *
 266       * @return int
 267       */
 268  	public function getRevisionId() {
 269          return $this->getOutput()->getRevisionId();
 270      }
 271  
 272      /**
 273       * Whether the revision displayed is the latest revision of the page
 274       *
 275       * @return bool
 276       */
 277  	public function isRevisionCurrent() {
 278          $revID = $this->getRevisionId();
 279          return $revID == 0 || $revID == $this->getTitle()->getLatestRevID();
 280      }
 281  
 282      /**
 283       * Set the "relevant" title
 284       * @see self::getRelevantTitle()
 285       * @param Title $t
 286       */
 287  	public function setRelevantTitle( $t ) {
 288          $this->mRelevantTitle = $t;
 289      }
 290  
 291      /**
 292       * Return the "relevant" title.
 293       * A "relevant" title is not necessarily the actual title of the page.
 294       * Special pages like Special:MovePage use set the page they are acting on
 295       * as their "relevant" title, this allows the skin system to display things
 296       * such as content tabs which belong to to that page instead of displaying
 297       * a basic special page tab which has almost no meaning.
 298       *
 299       * @return Title
 300       */
 301  	public function getRelevantTitle() {
 302          if ( isset( $this->mRelevantTitle ) ) {
 303              return $this->mRelevantTitle;
 304          }
 305          return $this->getTitle();
 306      }
 307  
 308      /**
 309       * Set the "relevant" user
 310       * @see self::getRelevantUser()
 311       * @param User $u
 312       */
 313  	public function setRelevantUser( $u ) {
 314          $this->mRelevantUser = $u;
 315      }
 316  
 317      /**
 318       * Return the "relevant" user.
 319       * A "relevant" user is similar to a relevant title. Special pages like
 320       * Special:Contributions mark the user which they are relevant to so that
 321       * things like the toolbox can display the information they usually are only
 322       * able to display on a user's userpage and talkpage.
 323       * @return User
 324       */
 325  	public function getRelevantUser() {
 326          if ( isset( $this->mRelevantUser ) ) {
 327              return $this->mRelevantUser;
 328          }
 329          $title = $this->getRelevantTitle();
 330          if ( $title->hasSubjectNamespace( NS_USER ) ) {
 331              $rootUser = $title->getRootText();
 332              if ( User::isIP( $rootUser ) ) {
 333                  $this->mRelevantUser = User::newFromName( $rootUser, false );
 334              } else {
 335                  $user = User::newFromName( $rootUser, false );
 336                  if ( $user && $user->isLoggedIn() ) {
 337                      $this->mRelevantUser = $user;
 338                  }
 339              }
 340              return $this->mRelevantUser;
 341          }
 342          return null;
 343      }
 344  
 345      /**
 346       * Outputs the HTML generated by other functions.
 347       * @param OutputPage $out
 348       */
 349      abstract function outputPage( OutputPage $out = null );
 350  
 351      /**
 352       * @param array $data
 353       * @return string
 354       */
 355  	static function makeVariablesScript( $data ) {
 356          if ( $data ) {
 357              return Html::inlineScript(
 358                  ResourceLoader::makeLoaderConditionalScript( ResourceLoader::makeConfigSetScript( $data ) )
 359              );
 360          } else {
 361              return '';
 362          }
 363      }
 364  
 365      /**
 366       * Get the query to generate a dynamic stylesheet
 367       *
 368       * @return array
 369       */
 370  	public static function getDynamicStylesheetQuery() {
 371          global $wgSquidMaxage;
 372  
 373          return array(
 374                  'action' => 'raw',
 375                  'maxage' => $wgSquidMaxage,
 376                  'usemsgcache' => 'yes',
 377                  'ctype' => 'text/css',
 378                  'smaxage' => $wgSquidMaxage,
 379              );
 380      }
 381  
 382      /**
 383       * Add skin specific stylesheets
 384       * Calling this method with an $out of anything but the same OutputPage
 385       * inside ->getOutput() is deprecated. The $out arg is kept
 386       * for compatibility purposes with skins.
 387       * @param OutputPage $out
 388       * @todo delete
 389       */
 390      abstract function setupSkinUserCss( OutputPage $out );
 391  
 392      /**
 393       * TODO: document
 394       * @param Title $title
 395       * @return string
 396       */
 397  	function getPageClasses( $title ) {
 398          $numeric = 'ns-' . $title->getNamespace();
 399  
 400          if ( $title->isSpecialPage() ) {
 401              $type = 'ns-special';
 402              // bug 23315: provide a class based on the canonical special page name without subpages
 403              list( $canonicalName ) = SpecialPageFactory::resolveAlias( $title->getDBkey() );
 404              if ( $canonicalName ) {
 405                  $type .= ' ' . Sanitizer::escapeClass( "mw-special-$canonicalName" );
 406              } else {
 407                  $type .= ' mw-invalidspecialpage';
 408              }
 409          } elseif ( $title->isTalkPage() ) {
 410              $type = 'ns-talk';
 411          } else {
 412              $type = 'ns-subject';
 413          }
 414  
 415          $name = Sanitizer::escapeClass( 'page-' . $title->getPrefixedText() );
 416  
 417          return "$numeric $type $name";
 418      }
 419  
 420      /**
 421       * Return values for <html> element
 422       * @return array Array of associative name-to-value elements for <html> element
 423       */
 424  	public function getHtmlElementAttributes() {
 425          $lang = $this->getLanguage();
 426          return array(
 427              'lang' => $lang->getHtmlCode(),
 428              'dir' => $lang->getDir(),
 429              'class' => 'client-nojs',
 430          );
 431      }
 432  
 433      /**
 434       * This will be called by OutputPage::headElement when it is creating the
 435       * "<body>" tag, skins can override it if they have a need to add in any
 436       * body attributes or classes of their own.
 437       * @param OutputPage $out
 438       * @param array $bodyAttrs
 439       */
 440  	function addToBodyAttributes( $out, &$bodyAttrs ) {
 441          // does nothing by default
 442      }
 443  
 444      /**
 445       * URL to the logo
 446       * @return string
 447       */
 448  	function getLogo() {
 449          global $wgLogo;
 450          return $wgLogo;
 451      }
 452  
 453      /**
 454       * @return string
 455       */
 456  	function getCategoryLinks() {
 457          global $wgUseCategoryBrowser;
 458  
 459          $out = $this->getOutput();
 460          $allCats = $out->getCategoryLinks();
 461  
 462          if ( !count( $allCats ) ) {
 463              return '';
 464          }
 465  
 466          $embed = "<li>";
 467          $pop = "</li>";
 468  
 469          $s = '';
 470          $colon = $this->msg( 'colon-separator' )->escaped();
 471  
 472          if ( !empty( $allCats['normal'] ) ) {
 473              $t = $embed . implode( "{$pop}{$embed}", $allCats['normal'] ) . $pop;
 474  
 475              $msg = $this->msg( 'pagecategories' )->numParams( count( $allCats['normal'] ) )->escaped();
 476              $linkPage = wfMessage( 'pagecategorieslink' )->inContentLanguage()->text();
 477              $s .= '<div id="mw-normal-catlinks" class="mw-normal-catlinks">' .
 478                  Linker::link( Title::newFromText( $linkPage ), $msg )
 479                  . $colon . '<ul>' . $t . '</ul>' . '</div>';
 480          }
 481  
 482          # Hidden categories
 483          if ( isset( $allCats['hidden'] ) ) {
 484              if ( $this->getUser()->getBoolOption( 'showhiddencats' ) ) {
 485                  $class = ' mw-hidden-cats-user-shown';
 486              } elseif ( $this->getTitle()->getNamespace() == NS_CATEGORY ) {
 487                  $class = ' mw-hidden-cats-ns-shown';
 488              } else {
 489                  $class = ' mw-hidden-cats-hidden';
 490              }
 491  
 492              $s .= "<div id=\"mw-hidden-catlinks\" class=\"mw-hidden-catlinks$class\">" .
 493                  $this->msg( 'hidden-categories' )->numParams( count( $allCats['hidden'] ) )->escaped() .
 494                  $colon . '<ul>' . $embed . implode( "{$pop}{$embed}", $allCats['hidden'] ) . $pop . '</ul>' .
 495                  '</div>';
 496          }
 497  
 498          # optional 'dmoz-like' category browser. Will be shown under the list
 499          # of categories an article belong to
 500          if ( $wgUseCategoryBrowser ) {
 501              $s .= '<br /><hr />';
 502  
 503              # get a big array of the parents tree
 504              $parenttree = $this->getTitle()->getParentCategoryTree();
 505              # Skin object passed by reference cause it can not be
 506              # accessed under the method subfunction drawCategoryBrowser
 507              $tempout = explode( "\n", $this->drawCategoryBrowser( $parenttree ) );
 508              # Clean out bogus first entry and sort them
 509              unset( $tempout[0] );
 510              asort( $tempout );
 511              # Output one per line
 512              $s .= implode( "<br />\n", $tempout );
 513          }
 514  
 515          return $s;
 516      }
 517  
 518      /**
 519       * Render the array as a series of links.
 520       * @param array $tree Categories tree returned by Title::getParentCategoryTree
 521       * @return string Separated by &gt;, terminate with "\n"
 522       */
 523  	function drawCategoryBrowser( $tree ) {
 524          $return = '';
 525  
 526          foreach ( $tree as $element => $parent ) {
 527              if ( empty( $parent ) ) {
 528                  # element start a new list
 529                  $return .= "\n";
 530              } else {
 531                  # grab the others elements
 532                  $return .= $this->drawCategoryBrowser( $parent ) . ' &gt; ';
 533              }
 534  
 535              # add our current element to the list
 536              $eltitle = Title::newFromText( $element );
 537              $return .= Linker::link( $eltitle, htmlspecialchars( $eltitle->getText() ) );
 538          }
 539  
 540          return $return;
 541      }
 542  
 543      /**
 544       * @return string
 545       */
 546  	function getCategories() {
 547          $out = $this->getOutput();
 548  
 549          $catlinks = $this->getCategoryLinks();
 550  
 551          $classes = 'catlinks';
 552  
 553          // Check what we're showing
 554          $allCats = $out->getCategoryLinks();
 555          $showHidden = $this->getUser()->getBoolOption( 'showhiddencats' ) ||
 556                          $this->getTitle()->getNamespace() == NS_CATEGORY;
 557  
 558          if ( empty( $allCats['normal'] ) && !( !empty( $allCats['hidden'] ) && $showHidden ) ) {
 559              $classes .= ' catlinks-allhidden';
 560          }
 561  
 562          return "<div id='catlinks' class='$classes'>{$catlinks}</div>";
 563      }
 564  
 565      /**
 566       * This runs a hook to allow extensions placing their stuff after content
 567       * and article metadata (e.g. categories).
 568       * Note: This function has nothing to do with afterContent().
 569       *
 570       * This hook is placed here in order to allow using the same hook for all
 571       * skins, both the SkinTemplate based ones and the older ones, which directly
 572       * use this class to get their data.
 573       *
 574       * The output of this function gets processed in SkinTemplate::outputPage() for
 575       * the SkinTemplate based skins, all other skins should directly echo it.
 576       *
 577       * @return string Empty by default, if not changed by any hook function.
 578       */
 579  	protected function afterContentHook() {
 580          $data = '';
 581  
 582          if ( wfRunHooks( 'SkinAfterContent', array( &$data, $this ) ) ) {
 583              // adding just some spaces shouldn't toggle the output
 584              // of the whole <div/>, so we use trim() here
 585              if ( trim( $data ) != '' ) {
 586                  // Doing this here instead of in the skins to
 587                  // ensure that the div has the same ID in all
 588                  // skins
 589                  $data = "<div id='mw-data-after-content'>\n" .
 590                      "\t$data\n" .
 591                      "</div>\n";
 592              }
 593          } else {
 594              wfDebug( "Hook SkinAfterContent changed output processing.\n" );
 595          }
 596  
 597          return $data;
 598      }
 599  
 600      /**
 601       * Generate debug data HTML for displaying at the bottom of the main content
 602       * area.
 603       * @return string HTML containing debug data, if enabled (otherwise empty).
 604       */
 605  	protected function generateDebugHTML() {
 606          return MWDebug::getHTMLDebugLog();
 607      }
 608  
 609      /**
 610       * This gets called shortly before the "</body>" tag.
 611       *
 612       * @return string HTML-wrapped JS code to be put before "</body>"
 613       */
 614  	function bottomScripts() {
 615          // TODO and the suckage continues. This function is really just a wrapper around
 616          // OutputPage::getBottomScripts() which takes a Skin param. This should be cleaned
 617          // up at some point
 618          $bottomScriptText = $this->getOutput()->getBottomScripts();
 619          wfRunHooks( 'SkinAfterBottomScripts', array( $this, &$bottomScriptText ) );
 620  
 621          return $bottomScriptText;
 622      }
 623  
 624      /**
 625       * Text with the permalink to the source page,
 626       * usually shown on the footer of a printed page
 627       *
 628       * @return string HTML text with an URL
 629       */
 630  	function printSource() {
 631          $oldid = $this->getRevisionId();
 632          if ( $oldid ) {
 633              $canonicalUrl = $this->getTitle()->getCanonicalURL( 'oldid=' . $oldid );
 634              $url = htmlspecialchars( wfExpandIRI( $canonicalUrl ) );
 635          } else {
 636              // oldid not available for non existing pages
 637              $url = htmlspecialchars( wfExpandIRI( $this->getTitle()->getCanonicalURL() ) );
 638          }
 639  
 640          return $this->msg( 'retrievedfrom', '<a dir="ltr" href="' . $url
 641              . '">' . $url . '</a>' )->text();
 642      }
 643  
 644      /**
 645       * @return string
 646       */
 647  	function getUndeleteLink() {
 648          $action = $this->getRequest()->getVal( 'action', 'view' );
 649  
 650          if ( $this->getUser()->isAllowed( 'deletedhistory' ) &&
 651              ( $this->getTitle()->getArticleID() == 0 || $action == 'history' ) ) {
 652              $n = $this->getTitle()->isDeleted();
 653  
 654              if ( $n ) {
 655                  if ( $this->getUser()->isAllowed( 'undelete' ) ) {
 656                      $msg = 'thisisdeleted';
 657                  } else {
 658                      $msg = 'viewdeleted';
 659                  }
 660  
 661                  return $this->msg( $msg )->rawParams(
 662                      Linker::linkKnown(
 663                          SpecialPage::getTitleFor( 'Undelete', $this->getTitle()->getPrefixedDBkey() ),
 664                          $this->msg( 'restorelink' )->numParams( $n )->escaped() )
 665                      )->text();
 666              }
 667          }
 668  
 669          return '';
 670      }
 671  
 672      /**
 673       * @return string
 674       */
 675  	function subPageSubtitle() {
 676          $out = $this->getOutput();
 677          $subpages = '';
 678  
 679          if ( !wfRunHooks( 'SkinSubPageSubtitle', array( &$subpages, $this, $out ) ) ) {
 680              return $subpages;
 681          }
 682  
 683          if ( $out->isArticle() && MWNamespace::hasSubpages( $out->getTitle()->getNamespace() ) ) {
 684              $ptext = $this->getTitle()->getPrefixedText();
 685              if ( preg_match( '/\//', $ptext ) ) {
 686                  $links = explode( '/', $ptext );
 687                  array_pop( $links );
 688                  $c = 0;
 689                  $growinglink = '';
 690                  $display = '';
 691                  $lang = $this->getLanguage();
 692  
 693                  foreach ( $links as $link ) {
 694                      $growinglink .= $link;
 695                      $display .= $link;
 696                      $linkObj = Title::newFromText( $growinglink );
 697  
 698                      if ( is_object( $linkObj ) && $linkObj->isKnown() ) {
 699                          $getlink = Linker::linkKnown(
 700                              $linkObj,
 701                              htmlspecialchars( $display )
 702                          );
 703  
 704                          $c++;
 705  
 706                          if ( $c > 1 ) {
 707                              $subpages .= $lang->getDirMarkEntity() . $this->msg( 'pipe-separator' )->escaped();
 708                          } else {
 709                              $subpages .= '&lt; ';
 710                          }
 711  
 712                          $subpages .= $getlink;
 713                          $display = '';
 714                      } else {
 715                          $display .= '/';
 716                      }
 717                      $growinglink .= '/';
 718                  }
 719              }
 720          }
 721  
 722          return $subpages;
 723      }
 724  
 725      /**
 726       * Returns true if the IP should be shown in the header
 727       * @return bool
 728       */
 729  	function showIPinHeader() {
 730          global $wgShowIPinHeader;
 731          return $wgShowIPinHeader && session_id() != '';
 732      }
 733  
 734      /**
 735       * @return string
 736       */
 737  	function getSearchLink() {
 738          $searchPage = SpecialPage::getTitleFor( 'Search' );
 739          return $searchPage->getLocalURL();
 740      }
 741  
 742      /**
 743       * @return string
 744       */
 745  	function escapeSearchLink() {
 746          return htmlspecialchars( $this->getSearchLink() );
 747      }
 748  
 749      /**
 750       * @param string $type
 751       * @return string
 752       */
 753  	function getCopyright( $type = 'detect' ) {
 754          global $wgRightsPage, $wgRightsUrl, $wgRightsText;
 755  
 756          if ( $type == 'detect' ) {
 757              if ( !$this->isRevisionCurrent()
 758                  && !$this->msg( 'history_copyright' )->inContentLanguage()->isDisabled()
 759              ) {
 760                  $type = 'history';
 761              } else {
 762                  $type = 'normal';
 763              }
 764          }
 765  
 766          if ( $type == 'history' ) {
 767              $msg = 'history_copyright';
 768          } else {
 769              $msg = 'copyright';
 770          }
 771  
 772          if ( $wgRightsPage ) {
 773              $title = Title::newFromText( $wgRightsPage );
 774              $link = Linker::linkKnown( $title, $wgRightsText );
 775          } elseif ( $wgRightsUrl ) {
 776              $link = Linker::makeExternalLink( $wgRightsUrl, $wgRightsText );
 777          } elseif ( $wgRightsText ) {
 778              $link = $wgRightsText;
 779          } else {
 780              # Give up now
 781              return '';
 782          }
 783  
 784          // Allow for site and per-namespace customization of copyright notice.
 785          // @todo Remove deprecated $forContent param from hook handlers and then remove here.
 786          $forContent = true;
 787  
 788          wfRunHooks(
 789              'SkinCopyrightFooter',
 790              array( $this->getTitle(), $type, &$msg, &$link, &$forContent )
 791          );
 792  
 793          return $this->msg( $msg )->rawParams( $link )->text();
 794      }
 795  
 796      /**
 797       * @return null|string
 798       */
 799  	function getCopyrightIcon() {
 800          global $wgRightsUrl, $wgRightsText, $wgRightsIcon, $wgCopyrightIcon;
 801  
 802          $out = '';
 803  
 804          if ( $wgCopyrightIcon ) {
 805              $out = $wgCopyrightIcon;
 806          } elseif ( $wgRightsIcon ) {
 807              $icon = htmlspecialchars( $wgRightsIcon );
 808  
 809              if ( $wgRightsUrl ) {
 810                  $url = htmlspecialchars( $wgRightsUrl );
 811                  $out .= '<a href="' . $url . '">';
 812              }
 813  
 814              $text = htmlspecialchars( $wgRightsText );
 815              $out .= "<img src=\"$icon\" alt=\"$text\" width=\"88\" height=\"31\" />";
 816  
 817              if ( $wgRightsUrl ) {
 818                  $out .= '</a>';
 819              }
 820          }
 821  
 822          return $out;
 823      }
 824  
 825      /**
 826       * Gets the powered by MediaWiki icon.
 827       * @return string
 828       */
 829  	function getPoweredBy() {
 830          global $wgResourceBasePath;
 831  
 832          $url = htmlspecialchars( "$wgResourceBasePath/resources/assets/poweredby_mediawiki_88x31.png" );
 833          $text = '<a href="//www.mediawiki.org/"><img src="' . $url
 834              . '" height="31" width="88" alt="Powered by MediaWiki" /></a>';
 835          wfRunHooks( 'SkinGetPoweredBy', array( &$text, $this ) );
 836          return $text;
 837      }
 838  
 839      /**
 840       * Get the timestamp of the latest revision, formatted in user language
 841       *
 842       * @return string
 843       */
 844  	protected function lastModified() {
 845          $timestamp = $this->getOutput()->getRevisionTimestamp();
 846  
 847          # No cached timestamp, load it from the database
 848          if ( $timestamp === null ) {
 849              $timestamp = Revision::getTimestampFromId( $this->getTitle(), $this->getRevisionId() );
 850          }
 851  
 852          if ( $timestamp ) {
 853              $d = $this->getLanguage()->userDate( $timestamp, $this->getUser() );
 854              $t = $this->getLanguage()->userTime( $timestamp, $this->getUser() );
 855              $s = ' ' . $this->msg( 'lastmodifiedat', $d, $t )->text();
 856          } else {
 857              $s = '';
 858          }
 859  
 860          if ( wfGetLB()->getLaggedSlaveMode() ) {
 861              $s .= ' <strong>' . $this->msg( 'laggedslavemode' )->text() . '</strong>';
 862          }
 863  
 864          return $s;
 865      }
 866  
 867      /**
 868       * @param string $align
 869       * @return string
 870       */
 871  	function logoText( $align = '' ) {
 872          if ( $align != '' ) {
 873              $a = " style='float: {$align};'";
 874          } else {
 875              $a = '';
 876          }
 877  
 878          $mp = $this->msg( 'mainpage' )->escaped();
 879          $mptitle = Title::newMainPage();
 880          $url = ( is_object( $mptitle ) ? htmlspecialchars( $mptitle->getLocalURL() ) : '' );
 881  
 882          $logourl = $this->getLogo();
 883          $s = "<a href='{$url}'><img{$a} src='{$logourl}' alt='[{$mp}]' /></a>";
 884  
 885          return $s;
 886      }
 887  
 888      /**
 889       * Renders a $wgFooterIcons icon according to the method's arguments
 890       * @param array $icon The icon to build the html for, see $wgFooterIcons
 891       *   for the format of this array.
 892       * @param bool|string $withImage Whether to use the icon's image or output
 893       *   a text-only footericon.
 894       * @return string HTML
 895       */
 896  	function makeFooterIcon( $icon, $withImage = 'withImage' ) {
 897          if ( is_string( $icon ) ) {
 898              $html = $icon;
 899          } else { // Assuming array
 900              $url = isset( $icon["url"] ) ? $icon["url"] : null;
 901              unset( $icon["url"] );
 902              if ( isset( $icon["src"] ) && $withImage === 'withImage' ) {
 903                  // do this the lazy way, just pass icon data as an attribute array
 904                  $html = Html::element( 'img', $icon );
 905              } else {
 906                  $html = htmlspecialchars( $icon["alt"] );
 907              }
 908              if ( $url ) {
 909                  $html = Html::rawElement( 'a', array( "href" => $url ), $html );
 910              }
 911          }
 912          return $html;
 913      }
 914  
 915      /**
 916       * Gets the link to the wiki's main page.
 917       * @return string
 918       */
 919  	function mainPageLink() {
 920          $s = Linker::linkKnown(
 921              Title::newMainPage(),
 922              $this->msg( 'mainpage' )->escaped()
 923          );
 924  
 925          return $s;
 926      }
 927  
 928      /**
 929       * Returns an HTML link for use in the footer
 930       * @param string $desc The i18n message key for the link text
 931       * @param string $page The i18n message key for the page to link to
 932       * @return string HTML anchor
 933       */
 934  	public function footerLink( $desc, $page ) {
 935          // if the link description has been set to "-" in the default language,
 936          if ( $this->msg( $desc )->inContentLanguage()->isDisabled() ) {
 937              // then it is disabled, for all languages.
 938              return '';
 939          } else {
 940              // Otherwise, we display the link for the user, described in their
 941              // language (which may or may not be the same as the default language),
 942              // but we make the link target be the one site-wide page.
 943              $title = Title::newFromText( $this->msg( $page )->inContentLanguage()->text() );
 944  
 945              return Linker::linkKnown(
 946                  $title,
 947                  $this->msg( $desc )->escaped()
 948              );
 949          }
 950      }
 951  
 952      /**
 953       * Gets the link to the wiki's privacy policy page.
 954       * @return string HTML
 955       */
 956  	function privacyLink() {
 957          return $this->footerLink( 'privacy', 'privacypage' );
 958      }
 959  
 960      /**
 961       * Gets the link to the wiki's about page.
 962       * @return string HTML
 963       */
 964  	function aboutLink() {
 965          return $this->footerLink( 'aboutsite', 'aboutpage' );
 966      }
 967  
 968      /**
 969       * Gets the link to the wiki's general disclaimers page.
 970       * @return string HTML
 971       */
 972  	function disclaimerLink() {
 973          return $this->footerLink( 'disclaimers', 'disclaimerpage' );
 974      }
 975  
 976      /**
 977       * Return URL options for the 'edit page' link.
 978       * This may include an 'oldid' specifier, if the current page view is such.
 979       *
 980       * @return array
 981       * @private
 982       */
 983  	function editUrlOptions() {
 984          $options = array( 'action' => 'edit' );
 985  
 986          if ( !$this->isRevisionCurrent() ) {
 987              $options['oldid'] = intval( $this->getRevisionId() );
 988          }
 989  
 990          return $options;
 991      }
 992  
 993      /**
 994       * @param User|int $id
 995       * @return bool
 996       */
 997  	function showEmailUser( $id ) {
 998          if ( $id instanceof User ) {
 999              $targetUser = $id;
1000          } else {
1001              $targetUser = User::newFromId( $id );
1002          }
1003  
1004          # The sending user must have a confirmed email address and the target
1005          # user must have a confirmed email address and allow emails from users.
1006          return $this->getUser()->canSendEmail() &&
1007              $targetUser->canReceiveEmail();
1008      }
1009  
1010      /**
1011       * This function previously returned a fully resolved style path URL to images or styles stored in
1012       * the legacy skins/common/ directory.
1013       *
1014       * That directory has been removed in 1.24 and the function always returns an empty string.
1015       *
1016       * @deprecated since 1.24
1017       * @param string $name The name or path of a skin resource file
1018       * @return string Empty string
1019       */
1020  	function getCommonStylePath( $name ) {
1021          wfDeprecated( __METHOD__, '1.24' );
1022          return '';
1023      }
1024  
1025      /**
1026       * Return a fully resolved style path url to images or styles stored in the current skins's folder.
1027       * This method returns a url resolved using the configured skin style path
1028       * and includes the style version inside of the url.
1029       *
1030       * Requires $stylename to be set, otherwise throws MWException.
1031       *
1032       * @param string $name The name or path of a skin resource file
1033       * @return string The fully resolved style path url including styleversion
1034       */
1035  	function getSkinStylePath( $name ) {
1036          global $wgStylePath, $wgStyleVersion;
1037  
1038          if ( $this->stylename === null ) {
1039              $class = get_class( $this );
1040              throw new MWException( "$class::\$stylename must be set to use getSkinStylePath()" );
1041          }
1042  
1043          return "$wgStylePath/{$this->stylename}/$name?$wgStyleVersion";
1044      }
1045  
1046      /* these are used extensively in SkinTemplate, but also some other places */
1047  
1048      /**
1049       * @param string $urlaction
1050       * @return string
1051       */
1052  	static function makeMainPageUrl( $urlaction = '' ) {
1053          $title = Title::newMainPage();
1054          self::checkTitle( $title, '' );
1055  
1056          return $title->getLocalURL( $urlaction );
1057      }
1058  
1059      /**
1060       * Make a URL for a Special Page using the given query and protocol.
1061       *
1062       * If $proto is set to null, make a local URL. Otherwise, make a full
1063       * URL with the protocol specified.
1064       *
1065       * @param string $name Name of the Special page
1066       * @param string $urlaction Query to append
1067       * @param string|null $proto Protocol to use or null for a local URL
1068       * @return string
1069       */
1070  	static function makeSpecialUrl( $name, $urlaction = '', $proto = null ) {
1071          $title = SpecialPage::getSafeTitleFor( $name );
1072          if ( is_null( $proto ) ) {
1073              return $title->getLocalURL( $urlaction );
1074          } else {
1075              return $title->getFullURL( $urlaction, false, $proto );
1076          }
1077      }
1078  
1079      /**
1080       * @param string $name
1081       * @param string $subpage
1082       * @param string $urlaction
1083       * @return string
1084       */
1085  	static function makeSpecialUrlSubpage( $name, $subpage, $urlaction = '' ) {
1086          $title = SpecialPage::getSafeTitleFor( $name, $subpage );
1087          return $title->getLocalURL( $urlaction );
1088      }
1089  
1090      /**
1091       * @param string $name
1092       * @param string $urlaction
1093       * @return string
1094       */
1095  	static function makeI18nUrl( $name, $urlaction = '' ) {
1096          $title = Title::newFromText( wfMessage( $name )->inContentLanguage()->text() );
1097          self::checkTitle( $title, $name );
1098          return $title->getLocalURL( $urlaction );
1099      }
1100  
1101      /**
1102       * @param string $name
1103       * @param string $urlaction
1104       * @return string
1105       */
1106  	static function makeUrl( $name, $urlaction = '' ) {
1107          $title = Title::newFromText( $name );
1108          self::checkTitle( $title, $name );
1109  
1110          return $title->getLocalURL( $urlaction );
1111      }
1112  
1113      /**
1114       * If url string starts with http, consider as external URL, else
1115       * internal
1116       * @param string $name
1117       * @return string URL
1118       */
1119  	static function makeInternalOrExternalUrl( $name ) {
1120          if ( preg_match( '/^(?i:' . wfUrlProtocols() . ')/', $name ) ) {
1121              return $name;
1122          } else {
1123              return self::makeUrl( $name );
1124          }
1125      }
1126  
1127      /**
1128       * this can be passed the NS number as defined in Language.php
1129       * @param string $name
1130       * @param string $urlaction
1131       * @param int $namespace
1132       * @return string
1133       */
1134  	static function makeNSUrl( $name, $urlaction = '', $namespace = NS_MAIN ) {
1135          $title = Title::makeTitleSafe( $namespace, $name );
1136          self::checkTitle( $title, $name );
1137  
1138          return $title->getLocalURL( $urlaction );
1139      }
1140  
1141      /**
1142       * these return an array with the 'href' and boolean 'exists'
1143       * @param string $name
1144       * @param string $urlaction
1145       * @return array
1146       */
1147  	static function makeUrlDetails( $name, $urlaction = '' ) {
1148          $title = Title::newFromText( $name );
1149          self::checkTitle( $title, $name );
1150  
1151          return array(
1152              'href' => $title->getLocalURL( $urlaction ),
1153              'exists' => $title->getArticleID() != 0,
1154          );
1155      }
1156  
1157      /**
1158       * Make URL details where the article exists (or at least it's convenient to think so)
1159       * @param string $name Article name
1160       * @param string $urlaction
1161       * @return array
1162       */
1163  	static function makeKnownUrlDetails( $name, $urlaction = '' ) {
1164          $title = Title::newFromText( $name );
1165          self::checkTitle( $title, $name );
1166  
1167          return array(
1168              'href' => $title->getLocalURL( $urlaction ),
1169              'exists' => true
1170          );
1171      }
1172  
1173      /**
1174       * make sure we have some title to operate on
1175       *
1176       * @param Title $title
1177       * @param string $name
1178       */
1179  	static function checkTitle( &$title, $name ) {
1180          if ( !is_object( $title ) ) {
1181              $title = Title::newFromText( $name );
1182              if ( !is_object( $title ) ) {
1183                  $title = Title::newFromText( '--error: link target missing--' );
1184              }
1185          }
1186      }
1187  
1188      /**
1189       * Build an array that represents the sidebar(s), the navigation bar among them.
1190       *
1191       * BaseTemplate::getSidebar can be used to simplify the format and id generation in new skins.
1192       *
1193       * The format of the returned array is array( heading => content, ... ), where:
1194       * - heading is the heading of a navigation portlet. It is either:
1195       *   - magic string to be handled by the skins ('SEARCH' / 'LANGUAGES' / 'TOOLBOX' / ...)
1196       *   - a message name (e.g. 'navigation'), the message should be HTML-escaped by the skin
1197       *   - plain text, which should be HTML-escaped by the skin
1198       * - content is the contents of the portlet. It is either:
1199       *   - HTML text (<ul><li>...</li>...</ul>)
1200       *   - array of link data in a format accepted by BaseTemplate::makeListItem()
1201       *   - (for a magic string as a key, any value)
1202       *
1203       * Note that extensions can control the sidebar contents using the SkinBuildSidebar hook
1204       * and can technically insert anything in here; skin creators are expected to handle
1205       * values described above.
1206       *
1207       * @return array
1208       */
1209  	function buildSidebar() {
1210          global $wgMemc, $wgEnableSidebarCache, $wgSidebarCacheExpiry;
1211          wfProfileIn( __METHOD__ );
1212  
1213          $key = wfMemcKey( 'sidebar', $this->getLanguage()->getCode() );
1214  
1215          if ( $wgEnableSidebarCache ) {
1216              $cachedsidebar = $wgMemc->get( $key );
1217              if ( $cachedsidebar ) {
1218                  wfRunHooks( 'SidebarBeforeOutput', array( $this, &$cachedsidebar ) );
1219  
1220                  wfProfileOut( __METHOD__ );
1221                  return $cachedsidebar;
1222              }
1223          }
1224  
1225          $bar = array();
1226          $this->addToSidebar( $bar, 'sidebar' );
1227  
1228          wfRunHooks( 'SkinBuildSidebar', array( $this, &$bar ) );
1229          if ( $wgEnableSidebarCache ) {
1230              $wgMemc->set( $key, $bar, $wgSidebarCacheExpiry );
1231          }
1232  
1233          wfRunHooks( 'SidebarBeforeOutput', array( $this, &$bar ) );
1234  
1235          wfProfileOut( __METHOD__ );
1236          return $bar;
1237      }
1238  
1239      /**
1240       * Add content from a sidebar system message
1241       * Currently only used for MediaWiki:Sidebar (but may be used by Extensions)
1242       *
1243       * This is just a wrapper around addToSidebarPlain() for backwards compatibility
1244       *
1245       * @param array $bar
1246       * @param string $message
1247       */
1248  	function addToSidebar( &$bar, $message ) {
1249          $this->addToSidebarPlain( $bar, wfMessage( $message )->inContentLanguage()->plain() );
1250      }
1251  
1252      /**
1253       * Add content from plain text
1254       * @since 1.17
1255       * @param array $bar
1256       * @param string $text
1257       * @return array
1258       */
1259  	function addToSidebarPlain( &$bar, $text ) {
1260          $lines = explode( "\n", $text );
1261  
1262          $heading = '';
1263  
1264          foreach ( $lines as $line ) {
1265              if ( strpos( $line, '*' ) !== 0 ) {
1266                  continue;
1267              }
1268              $line = rtrim( $line, "\r" ); // for Windows compat
1269  
1270              if ( strpos( $line, '**' ) !== 0 ) {
1271                  $heading = trim( $line, '* ' );
1272                  if ( !array_key_exists( $heading, $bar ) ) {
1273                      $bar[$heading] = array();
1274                  }
1275              } else {
1276                  $line = trim( $line, '* ' );
1277  
1278                  if ( strpos( $line, '|' ) !== false ) { // sanity check
1279                      $line = MessageCache::singleton()->transform( $line, false, null, $this->getTitle() );
1280                      $line = array_map( 'trim', explode( '|', $line, 2 ) );
1281                      if ( count( $line ) !== 2 ) {
1282                          // Second sanity check, could be hit by people doing
1283                          // funky stuff with parserfuncs... (bug 33321)
1284                          continue;
1285                      }
1286  
1287                      $extraAttribs = array();
1288  
1289                      $msgLink = $this->msg( $line[0] )->inContentLanguage();
1290                      if ( $msgLink->exists() ) {
1291                          $link = $msgLink->text();
1292                          if ( $link == '-' ) {
1293                              continue;
1294                          }
1295                      } else {
1296                          $link = $line[0];
1297                      }
1298                      $msgText = $this->msg( $line[1] );
1299                      if ( $msgText->exists() ) {
1300                          $text = $msgText->text();
1301                      } else {
1302                          $text = $line[1];
1303                      }
1304  
1305                      if ( preg_match( '/^(?i:' . wfUrlProtocols() . ')/', $link ) ) {
1306                          $href = $link;
1307  
1308                          // Parser::getExternalLinkAttribs won't work here because of the Namespace things
1309                          global $wgNoFollowLinks, $wgNoFollowDomainExceptions;
1310                          if ( $wgNoFollowLinks && !wfMatchesDomainList( $href, $wgNoFollowDomainExceptions ) ) {
1311                              $extraAttribs['rel'] = 'nofollow';
1312                          }
1313  
1314                          global $wgExternalLinkTarget;
1315                          if ( $wgExternalLinkTarget ) {
1316                              $extraAttribs['target'] = $wgExternalLinkTarget;
1317                          }
1318                      } else {
1319                          $title = Title::newFromText( $link );
1320  
1321                          if ( $title ) {
1322                              $title = $title->fixSpecialName();
1323                              $href = $title->getLinkURL();
1324                          } else {
1325                              $href = 'INVALID-TITLE';
1326                          }
1327                      }
1328  
1329                      $bar[$heading][] = array_merge( array(
1330                          'text' => $text,
1331                          'href' => $href,
1332                          'id' => 'n-' . Sanitizer::escapeId( strtr( $line[1], ' ', '-' ), 'noninitial' ),
1333                          'active' => false
1334                      ), $extraAttribs );
1335                  } else {
1336                      continue;
1337                  }
1338              }
1339          }
1340  
1341          return $bar;
1342      }
1343  
1344      /**
1345       * This function previously controlled whether the 'mediawiki.legacy.wikiprintable' module
1346       * should be loaded by OutputPage. That module no longer exists and the return value of this
1347       * method is ignored.
1348       *
1349       * If your skin doesn't provide its own print styles, the 'mediawiki.legacy.commonPrint' module
1350       * can be used instead (SkinTemplate-based skins do it automatically).
1351       *
1352       * @deprecated since 1.22
1353       * @return bool
1354       */
1355  	public function commonPrintStylesheet() {
1356          wfDeprecated( __METHOD__, '1.22' );
1357          return false;
1358      }
1359  
1360      /**
1361       * Gets new talk page messages for the current user and returns an
1362       * appropriate alert message (or an empty string if there are no messages)
1363       * @return string
1364       */
1365  	function getNewtalks() {
1366  
1367          $newMessagesAlert = '';
1368          $user = $this->getUser();
1369          $newtalks = $user->getNewMessageLinks();
1370          $out = $this->getOutput();
1371  
1372          // Allow extensions to disable or modify the new messages alert
1373          if ( !wfRunHooks( 'GetNewMessagesAlert', array( &$newMessagesAlert, $newtalks, $user, $out ) ) ) {
1374              return '';
1375          }
1376          if ( $newMessagesAlert ) {
1377              return $newMessagesAlert;
1378          }
1379  
1380          if ( count( $newtalks ) == 1 && $newtalks[0]['wiki'] === wfWikiID() ) {
1381              $uTalkTitle = $user->getTalkPage();
1382              $lastSeenRev = isset( $newtalks[0]['rev'] ) ? $newtalks[0]['rev'] : null;
1383              $nofAuthors = 0;
1384              if ( $lastSeenRev !== null ) {
1385                  $plural = true; // Default if we have a last seen revision: if unknown, use plural
1386                  $latestRev = Revision::newFromTitle( $uTalkTitle, false, Revision::READ_NORMAL );
1387                  if ( $latestRev !== null ) {
1388                      // Singular if only 1 unseen revision, plural if several unseen revisions.
1389                      $plural = $latestRev->getParentId() !== $lastSeenRev->getId();
1390                      $nofAuthors = $uTalkTitle->countAuthorsBetween(
1391                          $lastSeenRev, $latestRev, 10, 'include_new' );
1392                  }
1393              } else {
1394                  // Singular if no revision -> diff link will show latest change only in any case
1395                  $plural = false;
1396              }
1397              $plural = $plural ? 999 : 1;
1398              // 999 signifies "more than one revision". We don't know how many, and even if we did,
1399              // the number of revisions or authors is not necessarily the same as the number of
1400              // "messages".
1401              $newMessagesLink = Linker::linkKnown(
1402                  $uTalkTitle,
1403                  $this->msg( 'newmessageslinkplural' )->params( $plural )->escaped(),
1404                  array(),
1405                  array( 'redirect' => 'no' )
1406              );
1407  
1408              $newMessagesDiffLink = Linker::linkKnown(
1409                  $uTalkTitle,
1410                  $this->msg( 'newmessagesdifflinkplural' )->params( $plural )->escaped(),
1411                  array(),
1412                  $lastSeenRev !== null
1413                      ? array( 'oldid' => $lastSeenRev->getId(), 'diff' => 'cur' )
1414                      : array( 'diff' => 'cur' )
1415              );
1416  
1417              if ( $nofAuthors >= 1 && $nofAuthors <= 10 ) {
1418                  $newMessagesAlert = $this->msg(
1419                      'youhavenewmessagesfromusers',
1420                      $newMessagesLink,
1421                      $newMessagesDiffLink
1422                  )->numParams( $nofAuthors, $plural );
1423              } else {
1424                  // $nofAuthors === 11 signifies "11 or more" ("more than 10")
1425                  $newMessagesAlert = $this->msg(
1426                      $nofAuthors > 10 ? 'youhavenewmessagesmanyusers' : 'youhavenewmessages',
1427                      $newMessagesLink,
1428                      $newMessagesDiffLink
1429                  )->numParams( $plural );
1430              }
1431              $newMessagesAlert = $newMessagesAlert->text();
1432              # Disable Squid cache
1433              $out->setSquidMaxage( 0 );
1434          } elseif ( count( $newtalks ) ) {
1435              $sep = $this->msg( 'newtalkseparator' )->escaped();
1436              $msgs = array();
1437  
1438              foreach ( $newtalks as $newtalk ) {
1439                  $msgs[] = Xml::element(
1440                      'a',
1441                      array( 'href' => $newtalk['link'] ), $newtalk['wiki']
1442                  );
1443              }
1444              $parts = implode( $sep, $msgs );
1445              $newMessagesAlert = $this->msg( 'youhavenewmessagesmulti' )->rawParams( $parts )->escaped();
1446              $out->setSquidMaxage( 0 );
1447          }
1448  
1449          return $newMessagesAlert;
1450      }
1451  
1452      /**
1453       * Get a cached notice
1454       *
1455       * @param string $name Message name, or 'default' for $wgSiteNotice
1456       * @return string HTML fragment
1457       */
1458  	private function getCachedNotice( $name ) {
1459          global $wgRenderHashAppend, $parserMemc, $wgContLang;
1460  
1461          wfProfileIn( __METHOD__ );
1462  
1463          $needParse = false;
1464  
1465          if ( $name === 'default' ) {
1466              // special case
1467              global $wgSiteNotice;
1468              $notice = $wgSiteNotice;
1469              if ( empty( $notice ) ) {
1470                  wfProfileOut( __METHOD__ );
1471                  return false;
1472              }
1473          } else {
1474              $msg = $this->msg( $name )->inContentLanguage();
1475              if ( $msg->isDisabled() ) {
1476                  wfProfileOut( __METHOD__ );
1477                  return false;
1478              }
1479              $notice = $msg->plain();
1480          }
1481  
1482          // Use the extra hash appender to let eg SSL variants separately cache.
1483          $key = wfMemcKey( $name . $wgRenderHashAppend );
1484          $cachedNotice = $parserMemc->get( $key );
1485          if ( is_array( $cachedNotice ) ) {
1486              if ( md5( $notice ) == $cachedNotice['hash'] ) {
1487                  $notice = $cachedNotice['html'];
1488              } else {
1489                  $needParse = true;
1490              }
1491          } else {
1492              $needParse = true;
1493          }
1494  
1495          if ( $needParse ) {
1496              $parsed = $this->getOutput()->parse( $notice );
1497              $parserMemc->set( $key, array( 'html' => $parsed, 'hash' => md5( $notice ) ), 600 );
1498              $notice = $parsed;
1499          }
1500  
1501          $notice = Html::rawElement( 'div', array( 'id' => 'localNotice',
1502              'lang' => $wgContLang->getHtmlCode(), 'dir' => $wgContLang->getDir() ), $notice );
1503          wfProfileOut( __METHOD__ );
1504          return $notice;
1505      }
1506  
1507      /**
1508       * Get a notice based on page's namespace
1509       *
1510       * @return string HTML fragment
1511       */
1512  	function getNamespaceNotice() {
1513          wfProfileIn( __METHOD__ );
1514  
1515          $key = 'namespacenotice-' . $this->getTitle()->getNsText();
1516          $namespaceNotice = $this->getCachedNotice( $key );
1517          if ( $namespaceNotice && substr( $namespaceNotice, 0, 7 ) != '<p>&lt;' ) {
1518              $namespaceNotice = '<div id="namespacebanner">' . $namespaceNotice . '</div>';
1519          } else {
1520              $namespaceNotice = '';
1521          }
1522  
1523          wfProfileOut( __METHOD__ );
1524          return $namespaceNotice;
1525      }
1526  
1527      /**
1528       * Get the site notice
1529       *
1530       * @return string HTML fragment
1531       */
1532  	function getSiteNotice() {
1533          wfProfileIn( __METHOD__ );
1534          $siteNotice = '';
1535  
1536          if ( wfRunHooks( 'SiteNoticeBefore', array( &$siteNotice, $this ) ) ) {
1537              if ( is_object( $this->getUser() ) && $this->getUser()->isLoggedIn() ) {
1538                  $siteNotice = $this->getCachedNotice( 'sitenotice' );
1539              } else {
1540                  $anonNotice = $this->getCachedNotice( 'anonnotice' );
1541                  if ( !$anonNotice ) {
1542                      $siteNotice = $this->getCachedNotice( 'sitenotice' );
1543                  } else {
1544                      $siteNotice = $anonNotice;
1545                  }
1546              }
1547              if ( !$siteNotice ) {
1548                  $siteNotice = $this->getCachedNotice( 'default' );
1549              }
1550          }
1551  
1552          wfRunHooks( 'SiteNoticeAfter', array( &$siteNotice, $this ) );
1553          wfProfileOut( __METHOD__ );
1554          return $siteNotice;
1555      }
1556  
1557      /**
1558       * Create a section edit link.  This supersedes editSectionLink() and
1559       * editSectionLinkForOther().
1560       *
1561       * @param Title $nt The title being linked to (may not be the same as
1562       *   the current page, if the section is included from a template)
1563       * @param string $section The designation of the section being pointed to,
1564       *   to be included in the link, like "&section=$section"
1565       * @param string $tooltip The tooltip to use for the link: will be escaped
1566       *   and wrapped in the 'editsectionhint' message
1567       * @param string $lang Language code
1568       * @return string HTML to use for edit link
1569       */
1570  	public function doEditSectionLink( Title $nt, $section, $tooltip = null, $lang = false ) {
1571          // HTML generated here should probably have userlangattributes
1572          // added to it for LTR text on RTL pages
1573  
1574          $lang = wfGetLangObj( $lang );
1575  
1576          $attribs = array();
1577          if ( !is_null( $tooltip ) ) {
1578              # Bug 25462: undo double-escaping.
1579              $tooltip = Sanitizer::decodeCharReferences( $tooltip );
1580              $attribs['title'] = wfMessage( 'editsectionhint' )->rawParams( $tooltip )
1581                  ->inLanguage( $lang )->text();
1582          }
1583          $link = Linker::link( $nt, wfMessage( 'editsection' )->inLanguage( $lang )->text(),
1584              $attribs,
1585              array( 'action' => 'edit', 'section' => $section ),
1586              array( 'noclasses', 'known' )
1587          );
1588  
1589          # Add the brackets and the span and run the hook.
1590          $result = '<span class="mw-editsection">'
1591              . '<span class="mw-editsection-bracket">[</span>'
1592              . $link
1593              . '<span class="mw-editsection-bracket">]</span>'
1594              . '</span>';
1595  
1596          wfRunHooks( 'DoEditSectionLink', array( $this, $nt, $section, $tooltip, &$result, $lang ) );
1597          return $result;
1598      }
1599  
1600      /**
1601       * Use PHP's magic __call handler to intercept legacy calls to the linker
1602       * for backwards compatibility.
1603       *
1604       * @param string $fname Name of called method
1605       * @param array $args Arguments to the method
1606       * @throws MWException
1607       * @return mixed
1608       */
1609  	function __call( $fname, $args ) {
1610          $realFunction = array( 'Linker', $fname );
1611          if ( is_callable( $realFunction ) ) {
1612              wfDeprecated( get_class( $this ) . '::' . $fname, '1.21' );
1613              return call_user_func_array( $realFunction, $args );
1614          } else {
1615              $className = get_class( $this );
1616              throw new MWException( "Call to undefined method $className::$fname" );
1617          }
1618      }
1619  
1620  }


Generated: Fri Nov 28 14:03:12 2014 Cross-referenced by PHPXref 0.7.1