diff options
author | Pierre Schmitz <pierre@archlinux.de> | 2014-12-27 15:41:37 +0100 |
---|---|---|
committer | Pierre Schmitz <pierre@archlinux.de> | 2014-12-31 11:43:28 +0100 |
commit | c1f9b1f7b1b77776192048005dcc66dcf3df2bfb (patch) | |
tree | 2b38796e738dd74cb42ecd9bfd151803108386bc /includes/OutputPage.php | |
parent | b88ab0086858470dd1f644e64cb4e4f62bb2be9b (diff) |
Update to MediaWiki 1.24.1
Diffstat (limited to 'includes/OutputPage.php')
-rw-r--r-- | includes/OutputPage.php | 1761 |
1 files changed, 975 insertions, 786 deletions
diff --git a/includes/OutputPage.php b/includes/OutputPage.php index e6d4339f..2f8094ab 100644 --- a/includes/OutputPage.php +++ b/includes/OutputPage.php @@ -36,20 +36,31 @@ * @todo document */ class OutputPage extends ContextSource { - /// Should be private. Used with addMeta() which adds "<meta>" - var $mMetatags = array(); + /** @var array Should be private. Used with addMeta() which adds "<meta>" */ + protected $mMetatags = array(); - var $mLinktags = array(); - var $mCanonicalUrl = false; + /** @var array */ + protected $mLinktags = array(); - /// Additional stylesheets. Looks like this is for extensions. Might be replaced by resource loader. - var $mExtStyles = array(); + /** @var bool */ + protected $mCanonicalUrl = false; - /// Should be private - has getter and setter. Contains the HTML title - var $mPagetitle = ''; + /** + * @var array Additional stylesheets. Looks like this is for extensions. + * Might be replaced by resource loader. + */ + protected $mExtStyles = array(); - /// Contains all of the "<body>" content. Should be private we got set/get accessors and the append() method. - var $mBodytext = ''; + /** + * @var string Should be private - has getter and setter. Contains + * the HTML title */ + public $mPagetitle = ''; + + /** + * @var string Contains all of the "<body>" content. Should be private we + * got set/get accessors and the append() method. + */ + public $mBodytext = ''; /** * Holds the debug lines that will be output as comments in page source if @@ -58,43 +69,43 @@ class OutputPage extends ContextSource { */ public $mDebugtext = ''; - /// Should be private. Stores contents of "<title>" tag - var $mHTMLtitle = ''; - - /// Should be private. Is the displayed content related to the source of the corresponding wiki article. - var $mIsarticle = false; + /** @var string Stores contents of "<title>" tag */ + private $mHTMLtitle = ''; /** - * Should be private. Has get/set methods properly documented. - * Stores "article flag" toggle. + * @var bool Is the displayed content related to the source of the + * corresponding wiki article. */ - var $mIsArticleRelated = true; + private $mIsarticle = false; + + /** @var bool Stores "article flag" toggle. */ + private $mIsArticleRelated = true; /** - * Should be private. We have to set isPrintable(). Some pages should + * @var bool We have to set isPrintable(). Some pages should * never be printed (ex: redirections). */ - var $mPrintable = false; + private $mPrintable = false; /** - * Should be private. We have set/get/append methods. - * - * Contains the page subtitle. Special pages usually have some links here. - * Don't confuse with site subtitle added by skins. + * @var array Contains the page subtitle. Special pages usually have some + * links here. Don't confuse with site subtitle added by skins. */ private $mSubtitle = array(); - var $mRedirect = ''; - var $mStatusCode; + /** @var string */ + public $mRedirect = ''; + + /** @var int */ + protected $mStatusCode; /** - * mLastModified and mEtag are used for sending cache control. - * The whole caching system should probably be moved into its own class. + * @var string Variable mLastModified and mEtag are used for sending cache control. + * The whole caching system should probably be moved into its own class. */ - var $mLastModified = ''; + protected $mLastModified = ''; /** - * Should be private. No getter but used in sendCacheControl(); * Contains an HTTP Entity Tags (see RFC 2616 section 3.13) which is used * as a unique identifier for the content. It is later used by the client * to compare its cached version with the server version. Client sends @@ -103,53 +114,70 @@ class OutputPage extends ContextSource { * To get more information, you will have to look at HTTP/1.1 protocol which * is properly described in RFC 2616 : http://tools.ietf.org/html/rfc2616 */ - var $mETag = false; + private $mETag = false; - var $mCategoryLinks = array(); - var $mCategories = array(); + /** @var array */ + protected $mCategoryLinks = array(); - /// Should be private. Array of Interwiki Prefixed (non DB key) Titles (e.g. 'fr:Test page') - var $mLanguageLinks = array(); + /** @var array */ + protected $mCategories = array(); + + /** @var array Array of Interwiki Prefixed (non DB key) Titles (e.g. 'fr:Test page') */ + private $mLanguageLinks = array(); /** - * Should be private. Used for JavaScript (pre resource loader) - * We should split js / css. + * Used for JavaScript (pre resource loader) + * @todo We should split JS / CSS. * mScripts content is inserted as is in "<head>" by Skin. This might - * contains either a link to a stylesheet or inline css. + * contain either a link to a stylesheet or inline CSS. */ - var $mScripts = ''; + private $mScripts = ''; - /** - * Inline CSS styles. Use addInlineStyle() sparingly - */ - var $mInlineStyles = ''; + /** @var string Inline CSS styles. Use addInlineStyle() sparingly */ + protected $mInlineStyles = ''; - // - var $mLinkColours; + /** @todo Unused? */ + private $mLinkColours; /** - * Used by skin template. + * @var string Used by skin template. * Example: $tpl->set( 'displaytitle', $out->mPageLinkTitle ); */ - var $mPageLinkTitle = ''; + public $mPageLinkTitle = ''; + + /** @var array Array of elements in "<head>". Parser might add its own headers! */ + protected $mHeadItems = array(); + + // @todo FIXME: Next 5 variables probably come from the resource loader + + /** @var array */ + protected $mModules = array(); + + /** @var array */ + protected $mModuleScripts = array(); + + /** @var array */ + protected $mModuleStyles = array(); + + /** @var array */ + protected $mModuleMessages = array(); - /// Array of elements in "<head>". Parser might add its own headers! - var $mHeadItems = array(); + /** @var ResourceLoader */ + protected $mResourceLoader; - // @todo FIXME: Next variables probably comes from the resource loader - var $mModules = array(), $mModuleScripts = array(), $mModuleStyles = array(), $mModuleMessages = array(); - var $mResourceLoader; - var $mJsConfigVars = array(); + /** @var array */ + protected $mJsConfigVars = array(); - /** @todo FIXME: Is this still used ?*/ - var $mInlineMsg = array(); + /** @var array */ + protected $mTemplateIds = array(); - var $mTemplateIds = array(); - var $mImageTimeKeys = array(); + /** @var array */ + protected $mImageTimeKeys = array(); - var $mRedirectCode = ''; + /** @var string */ + public $mRedirectCode = ''; - var $mFeedLinksAppendQuery = null; + protected $mFeedLinksAppendQuery = null; /** @var array * What level of 'untrustworthiness' is allowed in CSS/JS modules loaded on this page? @@ -160,14 +188,19 @@ class OutputPage extends ContextSource { ResourceLoaderModule::TYPE_COMBINED => ResourceLoaderModule::ORIGIN_ALL, ); + /** @var bool Whether output is disabled. If this is true, the 'output' method will do nothing. */ + protected $mDoNothing = false; + + // Parser related. + /** - * @EasterEgg I just love the name for this self documenting variable. - * @todo document + * @var int + * @todo Unused? */ - var $mDoNothing = false; + private $mContainsOldMagic = 0; - // Parser related. - var $mContainsOldMagic = 0, $mContainsNewMagic = 0; + /** @var int */ + protected $mContainsNewMagic = 0; /** * lazy initialised, use parserOptions() @@ -176,57 +209,64 @@ class OutputPage extends ContextSource { protected $mParserOptions = null; /** - * Handles the atom / rss links. - * We probably only support atom in 2011. - * Looks like a private variable. + * Handles the Atom / RSS links. + * We probably only support Atom in 2011. * @see $wgAdvertisedFeedTypes */ - var $mFeedLinks = array(); + private $mFeedLinks = array(); // Gwicke work on squid caching? Roughly from 2003. - var $mEnableClientCache = true; + protected $mEnableClientCache = true; - /** - * Flag if output should only contain the body of the article. - * Should be private. - */ - var $mArticleBodyOnly = false; + /** @var bool Flag if output should only contain the body of the article. */ + private $mArticleBodyOnly = false; + + /** @var bool */ + protected $mNewSectionLink = false; - var $mNewSectionLink = false; - var $mHideNewSectionLink = false; + /** @var bool */ + protected $mHideNewSectionLink = false; /** - * Comes from the parser. This was probably made to load CSS/JS only - * if we had "<gallery>". Used directly in CategoryPage.php + * @var bool Comes from the parser. This was probably made to load CSS/JS + * only if we had "<gallery>". Used directly in CategoryPage.php. * Looks like resource loader can replace this. */ - var $mNoGallery = false; + public $mNoGallery = false; + + /** @var string */ + private $mPageTitleActionText = ''; + + /** @var array */ + private $mParseWarnings = array(); - // should be private. - var $mPageTitleActionText = ''; - var $mParseWarnings = array(); + /** @var int Cache stuff. Looks like mEnableClientCache */ + protected $mSquidMaxage = 0; - // Cache stuff. Looks like mEnableClientCache - var $mSquidMaxage = 0; + /** + * @var bool + * @todo Document + */ + protected $mPreventClickjacking = true; - // @todo document - var $mPreventClickjacking = true; + /** @var int To include the variable {{REVISIONID}} */ + private $mRevisionId = null; - /// should be private. To include the variable {{REVISIONID}} - var $mRevisionId = null; + /** @var string */ private $mRevisionTimestamp = null; - var $mFileVersion = null; + /** @var array */ + protected $mFileVersion = null; /** - * An array of stylesheet filenames (relative from skins path), with options - * for CSS media, IE conditions, and RTL/LTR direction. + * @var array An array of stylesheet filenames (relative from skins path), + * with options for CSS media, IE conditions, and RTL/LTR direction. * For internal use; add settings in the skin via $this->addStyle() * * Style again! This seems like a code duplication since we already have - * mStyles. This is what makes OpenSource amazing. + * mStyles. This is what makes Open Source amazing. */ - var $styles = array(); + protected $styles = array(); /** * Whether jQuery is already handled. @@ -253,19 +293,25 @@ class OutputPage extends ContextSource { private $mProperties = array(); /** - * @var string|null: ResourceLoader target for load.php links. If null, will be omitted + * @var string|null ResourceLoader target for load.php links. If null, will be omitted */ private $mTarget = null; /** - * @var bool: Whether output should contain table of contents + * @var bool Whether parser output should contain table of contents */ private $mEnableTOC = true; /** + * @var bool Whether parser output should contain section edit links + */ + private $mEnableSectionEditLinks = true; + + /** * Constructor for OutputPage. This should not be called directly. * Instead a new RequestContext should be created and it will implicitly create * a OutputPage tied to that context. + * @param IContextSource|null $context */ function __construct( IContextSource $context = null ) { if ( $context === null ) { @@ -291,7 +337,7 @@ class OutputPage extends ContextSource { /** * Get the URL to redirect to, or an empty string if not redirect URL set * - * @return String + * @return string */ public function getRedirect() { return $this->mRedirect; @@ -300,7 +346,7 @@ class OutputPage extends ContextSource { /** * Set the HTTP status code to send with the output. * - * @param $statusCode Integer + * @param int $statusCode */ public function setStatusCode( $statusCode ) { $this->mStatusCode = $statusCode; @@ -310,32 +356,52 @@ class OutputPage extends ContextSource { * Add a new "<meta>" tag * To add an http-equiv meta tag, precede the name with "http:" * - * @param string $name tag name - * @param string $val tag value + * @param string $name Tag name + * @param string $val Tag value */ function addMeta( $name, $val ) { array_push( $this->mMetatags, array( $name, $val ) ); } /** + * Returns the current <meta> tags + * + * @since 1.25 + * @return array + */ + public function getMetaTags() { + return $this->mMetatags; + } + + /** * Add a new \<link\> tag to the page header. * * Note: use setCanonicalUrl() for rel=canonical. * - * @param array $linkarr associative array of attributes. + * @param array $linkarr Associative array of attributes. */ - function addLink( $linkarr ) { + function addLink( array $linkarr ) { array_push( $this->mLinktags, $linkarr ); } /** + * Returns the current <link> tags + * + * @since 1.25 + * @return array + */ + public function getLinkTags() { + return $this->mLinktags; + } + + /** * Add a new \<link\> with "rel" attribute set to "meta" * - * @param array $linkarr associative array mapping attribute names to their + * @param array $linkarr Associative array mapping attribute names to their * values, both keys and values will be escaped, and the * "rel" attribute will be automatically added */ - function addMetadataLink( $linkarr ) { + function addMetadataLink( array $linkarr ) { $linkarr['rel'] = $this->getMetadataAttribute(); $this->addLink( $linkarr ); } @@ -343,15 +409,27 @@ class OutputPage extends ContextSource { /** * Set the URL to be used for the <link rel=canonical>. This should be used * in preference to addLink(), to avoid duplicate link tags. + * @param string $url */ function setCanonicalUrl( $url ) { $this->mCanonicalUrl = $url; } /** + * Returns the URL to be used for the <link rel=canonical> if + * one is set. + * + * @since 1.25 + * @return bool|string + */ + public function getCanonicalUrl() { + return $this->mCanonicalUrl; + } + + /** * Get the value of the "rel" attribute for metadata links * - * @return String + * @return string */ public function getMetadataAttribute() { # note: buggy CC software only reads first "meta" link @@ -367,7 +445,7 @@ class OutputPage extends ContextSource { /** * Add raw HTML to the list of scripts (including \<script\> tag, etc.) * - * @param string $script raw HTML + * @param string $script Raw HTML */ function addScript( $script ) { $this->mScripts .= $script . "\n"; @@ -376,7 +454,7 @@ class OutputPage extends ContextSource { /** * Register and add a stylesheet from an extension directory. * - * @param string $url path to sheet. Provide either a full url (beginning + * @param string $url Path to sheet. Provide either a full url (beginning * with 'http', etc) or a relative path from the document root * (beginning with '/'). Otherwise it behaves identically to * addStyle() and draws from the /skins folder. @@ -388,7 +466,7 @@ class OutputPage extends ContextSource { /** * Get all styles added by extensions * - * @return Array + * @return array */ function getExtStyle() { return $this->mExtStyles; @@ -397,20 +475,19 @@ class OutputPage extends ContextSource { /** * Add a JavaScript file out of skins/common, or a given relative path. * - * @param string $file filename in skins/common or complete on-server path + * @param string $file Filename in skins/common or complete on-server path * (/foo/bar.js) - * @param string $version style version of the file. Defaults to $wgStyleVersion + * @param string $version Style version of the file. Defaults to $wgStyleVersion */ public function addScriptFile( $file, $version = null ) { - global $wgStylePath, $wgStyleVersion; // See if $file parameter is an absolute URL or begins with a slash if ( substr( $file, 0, 1 ) == '/' || preg_match( '#^[a-z]*://#i', $file ) ) { $path = $file; } else { - $path = "{$wgStylePath}/common/{$file}"; + $path = $this->getConfig()->get( 'StylePath' ) . "/common/{$file}"; } if ( is_null( $version ) ) { - $version = $wgStyleVersion; + $version = $this->getConfig()->get( 'StyleVersion' ); } $this->addScript( Html::linkedScript( wfAppendQuery( $path, $version ) ) ); } @@ -427,21 +504,25 @@ class OutputPage extends ContextSource { /** * Get all registered JS and CSS tags for the header. * - * @return String + * @return string + * @deprecated since 1.24 Use OutputPage::headElement to build the full header. */ function getScript() { + wfDeprecated( __METHOD__, '1.24' ); return $this->mScripts . $this->getHeadItems(); } /** * Filter an array of modules to remove insufficiently trustworthy members, and modules * which are no longer registered (eg a page is cached before an extension is disabled) - * @param $modules Array - * @param string $position if not null, only return modules with this position - * @param $type string - * @return Array + * @param array $modules + * @param string|null $position If not null, only return modules with this position + * @param string $type + * @return array */ - protected function filterModules( $modules, $position = null, $type = ResourceLoaderModule::TYPE_COMBINED ) { + protected function filterModules( array $modules, $position = null, + $type = ResourceLoaderModule::TYPE_COMBINED + ) { $resourceLoader = $this->getResourceLoader(); $filteredModules = array(); foreach ( $modules as $val ) { @@ -449,8 +530,8 @@ class OutputPage extends ContextSource { if ( $module instanceof ResourceLoaderModule && $module->getOrigin() <= $this->getAllowedModules( $type ) && ( is_null( $position ) || $module->getPosition() == $position ) - && ( !$this->mTarget || in_array( $this->mTarget, $module->getTargets() ) ) ) - { + && ( !$this->mTarget || in_array( $this->mTarget, $module->getTargets() ) ) + ) { $filteredModules[] = $val; } } @@ -460,10 +541,10 @@ class OutputPage extends ContextSource { /** * Get the list of modules to include on this page * - * @param bool $filter whether to filter out insufficiently trustworthy modules - * @param string $position if not null, only return modules with this position - * @param $param string - * @return Array of module names + * @param bool $filter Whether to filter out insufficiently trustworthy modules + * @param string|null $position If not null, only return modules with this position + * @param string $param + * @return array Array of module names */ public function getModules( $filter = false, $position = null, $param = 'mModules' ) { $modules = array_values( array_unique( $this->$param ) ); @@ -477,7 +558,7 @@ class OutputPage extends ContextSource { * through this function will be loaded by the resource loader when the * page loads. * - * @param $modules Mixed: module name (string) or array of module names + * @param string|array $modules Module name (string) or array of module names */ public function addModules( $modules ) { $this->mModules = array_merge( $this->mModules, (array)$modules ); @@ -486,10 +567,10 @@ class OutputPage extends ContextSource { /** * Get the list of module JS to include on this page * - * @param $filter - * @param $position + * @param bool $filter + * @param string|null $position * - * @return array of module names + * @return array Array of module names */ public function getModuleScripts( $filter = false, $position = null ) { return $this->getModules( $filter, $position, 'mModuleScripts' ); @@ -500,7 +581,7 @@ class OutputPage extends ContextSource { * scripts added through this function will be loaded by the resource loader when * the page loads. * - * @param $modules Mixed: module name (string) or array of module names + * @param string|array $modules Module name (string) or array of module names */ public function addModuleScripts( $modules ) { $this->mModuleScripts = array_merge( $this->mModuleScripts, (array)$modules ); @@ -509,10 +590,10 @@ class OutputPage extends ContextSource { /** * Get the list of module CSS to include on this page * - * @param $filter - * @param $position + * @param bool $filter + * @param string|null $position * - * @return Array of module names + * @return array Array of module names */ public function getModuleStyles( $filter = false, $position = null ) { return $this->getModules( $filter, $position, 'mModuleStyles' ); @@ -525,7 +606,7 @@ class OutputPage extends ContextSource { * tags, rather than as a combined Javascript and CSS package. Thus, they will * load when JavaScript is disabled (unless CSS also happens to be disabled). * - * @param $modules Mixed: module name (string) or array of module names + * @param string|array $modules Module name (string) or array of module names */ public function addModuleStyles( $modules ) { $this->mModuleStyles = array_merge( $this->mModuleStyles, (array)$modules ); @@ -534,10 +615,10 @@ class OutputPage extends ContextSource { /** * Get the list of module messages to include on this page * - * @param $filter - * @param $position + * @param bool $filter + * @param string|null $position * - * @return Array of module names + * @return array Array of module names */ public function getModuleMessages( $filter = false, $position = null ) { return $this->getModules( $filter, $position, 'mModuleMessages' ); @@ -548,14 +629,14 @@ class OutputPage extends ContextSource { * Module messages added through this function will be loaded by the resource * loader when the page loads. * - * @param $modules Mixed: module name (string) or array of module names + * @param string|array $modules Module name (string) or array of module names */ public function addModuleMessages( $modules ) { $this->mModuleMessages = array_merge( $this->mModuleMessages, (array)$modules ); } /** - * @return null|string: ResourceLoader target + * @return null|string ResourceLoader target */ public function getTarget() { return $this->mTarget; @@ -564,7 +645,7 @@ class OutputPage extends ContextSource { /** * Sets ResourceLoader target for load.php links. If null, will be omitted * - * @param $target string|null + * @param string|null $target */ public function setTarget( $target ) { $this->mTarget = $target; @@ -573,7 +654,7 @@ class OutputPage extends ContextSource { /** * Get an array of head items * - * @return Array + * @return array */ function getHeadItemsArray() { return $this->mHeadItems; @@ -582,9 +663,12 @@ class OutputPage extends ContextSource { /** * Get all header items in a string * - * @return String + * @return string + * @deprecated since 1.24 Use OutputPage::headElement or + * if absolutely necessary use OutputPage::getHeadItemsArray */ function getHeadItems() { + wfDeprecated( __METHOD__, '1.24' ); $s = ''; foreach ( $this->mHeadItems as $item ) { $s .= $item; @@ -595,8 +679,8 @@ class OutputPage extends ContextSource { /** * Add or replace an header item to the output * - * @param string $name item name - * @param string $value raw HTML + * @param string $name Item name + * @param string $value Raw HTML */ public function addHeadItem( $name, $value ) { $this->mHeadItems[$name] = $value; @@ -605,8 +689,8 @@ class OutputPage extends ContextSource { /** * Check if the header item $name is already set * - * @param string $name item name - * @return Boolean + * @param string $name Item name + * @return bool */ public function hasHeadItem( $name ) { return isset( $this->mHeadItems[$name] ); @@ -615,7 +699,7 @@ class OutputPage extends ContextSource { /** * Set the value of the ETag HTTP header, only used if $wgUseETag is true * - * @param string $tag value of "ETag" header + * @param string $tag Value of "ETag" header */ function setETag( $tag ) { $this->mETag = $tag; @@ -626,7 +710,7 @@ class OutputPage extends ContextSource { * without any skin, sidebar, etc. * Used e.g. when calling with "action=render". * - * @param $only Boolean: whether to output only the body of the article + * @param bool $only Whether to output only the body of the article */ public function setArticleBodyOnly( $only ) { $this->mArticleBodyOnly = $only; @@ -635,7 +719,7 @@ class OutputPage extends ContextSource { /** * Return whether the output will contain only the body of the article * - * @return Boolean + * @return bool */ public function getArticleBodyOnly() { return $this->mArticleBodyOnly; @@ -656,8 +740,8 @@ class OutputPage extends ContextSource { * Get an additional output property * @since 1.21 * - * @param $name - * @return mixed: Property value or null if not found + * @param string $name + * @return mixed Property value or null if not found */ public function getProperty( $name ) { if ( isset( $this->mProperties[$name] ) ) { @@ -674,23 +758,18 @@ class OutputPage extends ContextSource { * * Side effect: sets mLastModified for Last-Modified header * - * @param $timestamp string + * @param string $timestamp * - * @return Boolean: true if cache-ok headers was sent. + * @return bool True if cache-ok headers was sent. */ public function checkLastModified( $timestamp ) { - global $wgCachePages, $wgCacheEpoch, $wgUseSquid, $wgSquidMaxage; - if ( !$timestamp || $timestamp == '19700101000000' ) { wfDebug( __METHOD__ . ": CACHE DISABLED, NO TIMESTAMP\n" ); return false; } - if ( !$wgCachePages ) { - wfDebug( __METHOD__ . ": CACHE DISABLED\n", false ); - return false; - } - if ( $this->getUser()->getOption( 'nocache' ) ) { - wfDebug( __METHOD__ . ": USER DISABLED CACHE\n", false ); + $config = $this->getConfig(); + if ( !$config->get( 'CachePages' ) ) { + wfDebug( __METHOD__ . ": CACHE DISABLED\n" ); return false; } @@ -698,11 +777,11 @@ class OutputPage extends ContextSource { $modifiedTimes = array( 'page' => $timestamp, 'user' => $this->getUser()->getTouched(), - 'epoch' => $wgCacheEpoch + 'epoch' => $config->get( 'CacheEpoch' ) ); - if ( $wgUseSquid ) { + if ( $config->get( 'UseSquid' ) ) { // bug 44570: the core page itself may not change, but resources might - $modifiedTimes['sepoch'] = wfTimestamp( TS_MW, time() - $wgSquidMaxage ); + $modifiedTimes['sepoch'] = wfTimestamp( TS_MW, time() - $config->get( 'SquidMaxage' ) ); } wfRunHooks( 'OutputPageCheckLastModified', array( &$modifiedTimes ) ); @@ -711,7 +790,7 @@ class OutputPage extends ContextSource { $clientHeader = $this->getRequest()->getHeader( 'If-Modified-Since' ); if ( $clientHeader === false ) { - wfDebug( __METHOD__ . ": client did not send If-Modified-Since header\n", false ); + wfDebug( __METHOD__ . ": client did not send If-Modified-Since header\n", 'log' ); return false; } @@ -724,7 +803,8 @@ class OutputPage extends ContextSource { $clientHeaderTime = strtotime( $clientHeader ); wfRestoreWarnings(); if ( !$clientHeaderTime ) { - wfDebug( __METHOD__ . ": unable to parse the client's If-Modified-Since header: $clientHeader\n" ); + wfDebug( __METHOD__ + . ": unable to parse the client's If-Modified-Since header: $clientHeader\n" ); return false; } $clientHeaderTime = wfTimestamp( TS_MW, $clientHeaderTime ); @@ -739,17 +819,17 @@ class OutputPage extends ContextSource { } wfDebug( __METHOD__ . ": client sent If-Modified-Since: " . - wfTimestamp( TS_ISO_8601, $clientHeaderTime ) . "\n", false ); + wfTimestamp( TS_ISO_8601, $clientHeaderTime ) . "\n", 'log' ); wfDebug( __METHOD__ . ": effective Last-Modified: " . - wfTimestamp( TS_ISO_8601, $maxModified ) . "\n", false ); + wfTimestamp( TS_ISO_8601, $maxModified ) . "\n", 'log' ); if ( $clientHeaderTime < $maxModified ) { - wfDebug( __METHOD__ . ": STALE, $info\n", false ); + wfDebug( __METHOD__ . ": STALE, $info\n", 'log' ); return false; } # Not modified # Give a 304 response code and disable body output - wfDebug( __METHOD__ . ": NOT MODIFIED, $info\n", false ); + wfDebug( __METHOD__ . ": NOT MODIFIED, $info\n", 'log' ); ini_set( 'zlib.output_compression', 0 ); $this->getRequest()->response()->header( "HTTP/1.1 304 Not Modified" ); $this->sendCacheControl(); @@ -766,7 +846,7 @@ class OutputPage extends ContextSource { /** * Override the last modified timestamp * - * @param string $timestamp new timestamp, in a format readable by + * @param string $timestamp New timestamp, in a format readable by * wfTimestamp() */ public function setLastModified( $timestamp ) { @@ -776,7 +856,7 @@ class OutputPage extends ContextSource { /** * Set the robot policy for the page: <http://www.robotstxt.org/meta.html> * - * @param string $policy the literal string to output as the contents of + * @param string $policy The literal string to output as the contents of * the meta tag. Will be parsed according to the spec and output in * standardized form. * @return null @@ -810,7 +890,7 @@ class OutputPage extends ContextSource { * Set the follow policy for the page, but leave the index policy un- * touched. * - * @param string $policy either 'follow' or 'nofollow'. + * @param string $policy Either 'follow' or 'nofollow'. * @return null */ public function setFollowPolicy( $policy ) { @@ -824,7 +904,7 @@ class OutputPage extends ContextSource { * Set the new value of the "action text", this will be added to the * "HTML title", separated from it with " - ". * - * @param string $text new value of the "action text" + * @param string $text New value of the "action text" */ public function setPageTitleActionText( $text ) { $this->mPageTitleActionText = $text; @@ -833,20 +913,17 @@ class OutputPage extends ContextSource { /** * Get the value of the "action text" * - * @return String + * @return string */ public function getPageTitleActionText() { - if ( isset( $this->mPageTitleActionText ) ) { - return $this->mPageTitleActionText; - } - return ''; + return $this->mPageTitleActionText; } /** * "HTML title" means the contents of "<title>". * It is stored as plain, unescaped text and will be run through htmlspecialchars in the skin file. * - * @param $name string + * @param string|Message $name */ public function setHTMLTitle( $name ) { if ( $name instanceof Message ) { @@ -859,7 +936,7 @@ class OutputPage extends ContextSource { /** * Return the "HTML title", i.e. the content of the "<title>" tag. * - * @return String + * @return string */ public function getHTMLTitle() { return $this->mHTMLtitle; @@ -868,19 +945,21 @@ class OutputPage extends ContextSource { /** * Set $mRedirectedFrom, the Title of the page which redirected us to the current page. * - * @param $t Title + * @param Title $t */ public function setRedirectedFrom( $t ) { $this->mRedirectedFrom = $t; } /** - * "Page title" means the contents of \<h1\>. It is stored as a valid HTML fragment. - * This function allows good tags like \<sup\> in the \<h1\> tag, but not bad tags like \<script\>. - * This function automatically sets \<title\> to the same content as \<h1\> but with all tags removed. - * Bad tags that were escaped in \<h1\> will still be escaped in \<title\>, and good tags like \<i\> will be dropped entirely. + * "Page title" means the contents of \<h1\>. It is stored as a valid HTML + * fragment. This function allows good tags like \<sup\> in the \<h1\> tag, + * but not bad tags like \<script\>. This function automatically sets + * \<title\> to the same content as \<h1\> but with all tags removed. Bad + * tags that were escaped in \<h1\> will still be escaped in \<title\>, and + * good tags like \<i\> will be dropped entirely. * - * @param $name string|Message + * @param string|Message $name */ public function setPageTitle( $name ) { if ( $name instanceof Message ) { @@ -893,13 +972,16 @@ class OutputPage extends ContextSource { $this->mPagetitle = $nameWithTags; # change "<i>foo&bar</i>" to "foo&bar" - $this->setHTMLTitle( $this->msg( 'pagetitle' )->rawParams( Sanitizer::stripAllTags( $nameWithTags ) ) ); + $this->setHTMLTitle( + $this->msg( 'pagetitle' )->rawParams( Sanitizer::stripAllTags( $nameWithTags ) ) + ->inContentLanguage() + ); } /** * Return the "page title", i.e. the content of the \<h1\> tag. * - * @return String + * @return string */ public function getPageTitle() { return $this->mPagetitle; @@ -908,7 +990,7 @@ class OutputPage extends ContextSource { /** * Set the Title object to use * - * @param $t Title object + * @param Title $t */ public function setTitle( Title $t ) { $this->getContext()->setTitle( $t ); @@ -917,7 +999,7 @@ class OutputPage extends ContextSource { /** * Replace the subtitle with $str * - * @param string|Message $str new value of the subtitle. String should be safe HTML. + * @param string|Message $str New value of the subtitle. String should be safe HTML. */ public function setSubtitle( $str ) { $this->clearSubtitle(); @@ -927,8 +1009,8 @@ class OutputPage extends ContextSource { /** * Add $str to the subtitle * - * @deprecated in 1.19; use addSubtitle() instead - * @param string|Message $str to add to the subtitle + * @deprecated since 1.19; use addSubtitle() instead + * @param string|Message $str String or Message to add to the subtitle */ public function appendSubtitle( $str ) { $this->addSubtitle( $str ); @@ -937,7 +1019,7 @@ class OutputPage extends ContextSource { /** * Add $str to the subtitle * - * @param string|Message $str to add to the subtitle. String should be safe HTML. + * @param string|Message $str String or Message to add to the subtitle. String should be safe HTML. */ public function addSubtitle( $str ) { if ( $str instanceof Message ) { @@ -950,14 +1032,15 @@ class OutputPage extends ContextSource { /** * Add a subtitle containing a backlink to a page * - * @param $title Title to link to + * @param Title $title Title to link to + * @param array $query Array of additional parameters to include in the link */ - public function addBacklinkSubtitle( Title $title ) { - $query = array(); + public function addBacklinkSubtitle( Title $title, $query = array() ) { if ( $title->isRedirect() ) { $query['redirect'] = 'no'; } - $this->addSubtitle( $this->msg( 'backlinksubtitle' )->rawParams( Linker::link( $title, null, array(), $query ) ) ); + $this->addSubtitle( $this->msg( 'backlinksubtitle' ) + ->rawParams( Linker::link( $title, null, array(), $query ) ) ); } /** @@ -970,7 +1053,7 @@ class OutputPage extends ContextSource { /** * Get the subtitle * - * @return String + * @return string */ public function getSubtitle() { return implode( "<br />\n\t\t\t\t", $this->mSubtitle ); @@ -987,7 +1070,7 @@ class OutputPage extends ContextSource { /** * Return whether the page is "printable" * - * @return Boolean + * @return bool */ public function isPrintable() { return $this->mPrintable; @@ -1003,7 +1086,7 @@ class OutputPage extends ContextSource { /** * Return whether the output will be completely disabled * - * @return Boolean + * @return bool */ public function isDisabled() { return $this->mDoNothing; @@ -1012,7 +1095,7 @@ class OutputPage extends ContextSource { /** * Show an "add new section" link? * - * @return Boolean + * @return bool */ public function showNewSectionLink() { return $this->mNewSectionLink; @@ -1021,7 +1104,7 @@ class OutputPage extends ContextSource { /** * Forcibly hide the new section link? * - * @return Boolean + * @return bool */ public function forceHideNewSectionLink() { return $this->mHideNewSectionLink; @@ -1033,7 +1116,7 @@ class OutputPage extends ContextSource { * for the new version * @see addFeedLink() * - * @param $show Boolean: true: add default feeds, false: remove all feeds + * @param bool $show True: add default feeds, false: remove all feeds */ public function setSyndicated( $show = true ) { if ( $show ) { @@ -1049,15 +1132,13 @@ class OutputPage extends ContextSource { * for the new version * @see addFeedLink() * - * @param string $val query to append to feed links or false to output + * @param string $val Query to append to feed links or false to output * default links */ public function setFeedAppendQuery( $val ) { - global $wgAdvertisedFeedTypes; - $this->mFeedLinks = array(); - foreach ( $wgAdvertisedFeedTypes as $type ) { + foreach ( $this->getConfig()->get( 'AdvertisedFeedTypes' ) as $type ) { $query = "feed=$type"; if ( is_string( $val ) ) { $query .= '&' . $val; @@ -1069,20 +1150,18 @@ class OutputPage extends ContextSource { /** * Add a feed link to the page header * - * @param string $format feed type, should be a key of $wgFeedClasses + * @param string $format Feed type, should be a key of $wgFeedClasses * @param string $href URL */ public function addFeedLink( $format, $href ) { - global $wgAdvertisedFeedTypes; - - if ( in_array( $format, $wgAdvertisedFeedTypes ) ) { + if ( in_array( $format, $this->getConfig()->get( 'AdvertisedFeedTypes' ) ) ) { $this->mFeedLinks[$format] = $href; } } /** * Should we output feed links for this page? - * @return Boolean + * @return bool */ public function isSyndicated() { return count( $this->mFeedLinks ) > 0; @@ -1090,7 +1169,7 @@ class OutputPage extends ContextSource { /** * Return URLs for each supported syndication format for this page. - * @return array associating format keys with URLs + * @return array Associating format keys with URLs */ public function getSyndicationLinks() { return $this->mFeedLinks; @@ -1110,7 +1189,7 @@ class OutputPage extends ContextSource { * corresponding article on the wiki * Setting true will cause the change "article related" toggle to true * - * @param $v Boolean + * @param bool $v */ public function setArticleFlag( $v ) { $this->mIsarticle = $v; @@ -1123,7 +1202,7 @@ class OutputPage extends ContextSource { * Return whether the content displayed page is related to the source of * the corresponding article on the wiki * - * @return Boolean + * @return bool */ public function isArticle() { return $this->mIsarticle; @@ -1133,7 +1212,7 @@ class OutputPage extends ContextSource { * Set whether this page is related an article on the wiki * Setting false will cause the change of "article flag" toggle to false * - * @param $v Boolean + * @param bool $v */ public function setArticleRelated( $v ) { $this->mIsArticleRelated = $v; @@ -1145,7 +1224,7 @@ class OutputPage extends ContextSource { /** * Return whether this page is related an article on the wiki * - * @return Boolean + * @return bool */ public function isArticleRelated() { return $this->mIsArticleRelated; @@ -1157,7 +1236,7 @@ class OutputPage extends ContextSource { * @param array $newLinkArray Associative array mapping language code to the page * name */ - public function addLanguageLinks( $newLinkArray ) { + public function addLanguageLinks( array $newLinkArray ) { $this->mLanguageLinks += $newLinkArray; } @@ -1167,14 +1246,14 @@ class OutputPage extends ContextSource { * @param array $newLinkArray Associative array mapping language code to the page * name */ - public function setLanguageLinks( $newLinkArray ) { + public function setLanguageLinks( array $newLinkArray ) { $this->mLanguageLinks = $newLinkArray; } /** * Get the list of language links * - * @return Array of Interwiki Prefixed (non DB key) Titles (e.g. 'fr:Test page') + * @return array Array of Interwiki Prefixed (non DB key) Titles (e.g. 'fr:Test page') */ public function getLanguageLinks() { return $this->mLanguageLinks; @@ -1183,9 +1262,9 @@ class OutputPage extends ContextSource { /** * Add an array of categories, with names in the keys * - * @param array $categories mapping category name => sort key + * @param array $categories Mapping category name => sort key */ - public function addCategoryLinks( $categories ) { + public function addCategoryLinks( array $categories ) { global $wgContLang; if ( !is_array( $categories ) || count( $categories ) == 0 ) { @@ -1199,22 +1278,29 @@ class OutputPage extends ContextSource { # Fetch existence plus the hiddencat property $dbr = wfGetDB( DB_SLAVE ); + $fields = array( 'page_id', 'page_namespace', 'page_title', 'page_len', + 'page_is_redirect', 'page_latest', 'pp_value' ); + + if ( $this->getConfig()->get( 'ContentHandlerUseDB' ) ) { + $fields[] = 'page_content_model'; + } + $res = $dbr->select( array( 'page', 'page_props' ), - array( 'page_id', 'page_namespace', 'page_title', 'page_len', 'page_is_redirect', 'page_latest', 'pp_value' ), + $fields, $lb->constructSet( 'page', $dbr ), __METHOD__, array(), - array( 'page_props' => array( 'LEFT JOIN', array( 'pp_propname' => 'hiddencat', 'pp_page = page_id' ) ) ) + array( 'page_props' => array( 'LEFT JOIN', array( + 'pp_propname' => 'hiddencat', + 'pp_page = page_id' + ) ) ) ); # Add the results to the link cache $lb->addResultToCache( LinkCache::singleton(), $res ); - # Set all the values to 'normal'. This can be done with array_fill_keys in PHP 5.2.0+ - $categories = array_combine( - array_keys( $categories ), - array_fill( 0, count( $categories ), 'normal' ) - ); + # Set all the values to 'normal'. + $categories = array_fill_keys( array_keys( $categories ), 'normal' ); # Mark hidden categories foreach ( $res as $row ) { @@ -1224,15 +1310,19 @@ class OutputPage extends ContextSource { } # Add the remaining categories to the skin - if ( wfRunHooks( 'OutputPageMakeCategoryLinks', array( &$this, $categories, &$this->mCategoryLinks ) ) ) { + if ( wfRunHooks( + 'OutputPageMakeCategoryLinks', + array( &$this, $categories, &$this->mCategoryLinks ) ) + ) { foreach ( $categories as $category => $type ) { $origcategory = $category; $title = Title::makeTitleSafe( NS_CATEGORY, $category ); + if ( !$title ) { + continue; + } $wgContLang->findVariantLink( $category, $title, true ); - if ( $category != $origcategory ) { - if ( array_key_exists( $category, $categories ) ) { - continue; - } + if ( $category != $origcategory && array_key_exists( $category, $categories ) ) { + continue; } $text = $wgContLang->convertHtml( $title->getText() ); $this->mCategories[] = $title->getText(); @@ -1244,9 +1334,9 @@ class OutputPage extends ContextSource { /** * Reset the category links (but not the category list) and add $categories * - * @param array $categories mapping category name => sort key + * @param array $categories Mapping category name => sort key */ - public function setCategoryLinks( $categories ) { + public function setCategoryLinks( array $categories ) { $this->mCategoryLinks = array(); $this->addCategoryLinks( $categories ); } @@ -1257,7 +1347,7 @@ class OutputPage extends ContextSource { * hidden categories) and $link a HTML fragment with a link to the category * page * - * @return Array + * @return array */ public function getCategoryLinks() { return $this->mCategoryLinks; @@ -1266,7 +1356,7 @@ class OutputPage extends ContextSource { /** * Get the list of category names this page belongs to * - * @return Array of strings + * @return array Array of strings */ public function getCategories() { return $this->mCategories; @@ -1281,7 +1371,6 @@ class OutputPage extends ContextSource { * @todo this should be given a more accurate name */ public function disallowUserJs() { - global $wgAllowSiteCSSOnRestrictedPages; $this->reduceAllowedModules( ResourceLoaderModule::TYPE_SCRIPTS, ResourceLoaderModule::ORIGIN_CORE_INDIVIDUAL @@ -1289,7 +1378,7 @@ class OutputPage extends ContextSource { // Site-wide styles are controlled by a config setting, see bug 71621 // for background on why. User styles are never allowed. - if ( $wgAllowSiteCSSOnRestrictedPages ) { + if ( $this->getConfig()->get( 'AllowSiteCSSOnRestrictedPages' ) ) { $styleOrigin = ResourceLoaderModule::ORIGIN_USER_SITEWIDE; } else { $styleOrigin = ResourceLoaderModule::ORIGIN_CORE_INDIVIDUAL; @@ -1301,19 +1390,7 @@ class OutputPage extends ContextSource { } /** - * Return whether user JavaScript is allowed for this page - * @deprecated since 1.18 Load modules with ResourceLoader, and origin and - * trustworthiness is identified and enforced automagically. - * @return Boolean - */ - public function isUserJsAllowed() { - wfDeprecated( __METHOD__, '1.18' ); - return $this->getAllowedModules( ResourceLoaderModule::TYPE_SCRIPTS ) >= ResourceLoaderModule::ORIGIN_USER_INDIVIDUAL; - } - - /** - * Get the level of JavaScript / CSS untrustworthiness allowed on this page. - * + * Show what level of JavaScript / CSS untrustworthiness is allowed on this page * @see ResourceLoaderModule::$origin * @param string $type ResourceLoaderModule TYPE_ constant * @return int ResourceLoaderModule ORIGIN_ class constant @@ -1333,7 +1410,6 @@ class OutputPage extends ContextSource { * * @deprecated since 1.24 Raising level of allowed untrusted content is no longer supported. * Use reduceAllowedModules() instead - * * @param string $type ResourceLoaderModule TYPE_ constant * @param int $level ResourceLoaderModule class constant */ @@ -1378,11 +1454,11 @@ class OutputPage extends ContextSource { * * @since 1.19 * - * @param $element string - * @param $attribs array - * @param $contents string + * @param string $element + * @param array $attribs + * @param string $contents */ - public function addElement( $element, $attribs = array(), $contents = '' ) { + public function addElement( $element, array $attribs = array(), $contents = '' ) { $this->addHTML( Html::element( $element, $attribs, $contents ) ); } @@ -1396,7 +1472,7 @@ class OutputPage extends ContextSource { /** * Get the body HTML * - * @return String: HTML + * @return string HTML */ public function getHTML() { return $this->mBodytext; @@ -1405,9 +1481,9 @@ class OutputPage extends ContextSource { /** * Get/set the ParserOptions object to use for wikitext parsing * - * @param $options ParserOptions|null either the ParserOption to use or null to only get the - * current ParserOption object - * @return ParserOptions object + * @param ParserOptions|null $options Either the ParserOption to use or null to only get the + * current ParserOption object + * @return ParserOptions */ public function parserOptions( $options = null ) { if ( !$this->mParserOptions ) { @@ -1421,8 +1497,8 @@ class OutputPage extends ContextSource { * Set the revision ID which will be seen by the wiki text parser * for things such as embedded {{REVISIONID}} variable use. * - * @param $revid Mixed: an positive integer, or null - * @return Mixed: previous value + * @param int|null $revid An positive integer, or null + * @return mixed Previous value */ public function setRevisionId( $revid ) { $val = is_null( $revid ) ? null : intval( $revid ); @@ -1432,7 +1508,7 @@ class OutputPage extends ContextSource { /** * Get the displayed revision ID * - * @return Integer + * @return int */ public function getRevisionId() { return $this->mRevisionId; @@ -1442,8 +1518,8 @@ class OutputPage extends ContextSource { * Set the timestamp of the revision which will be displayed. This is used * to avoid a extra DB call in Skin::lastModified(). * - * @param $timestamp Mixed: string, or null - * @return Mixed: previous value + * @param string|null $timestamp + * @return mixed Previous value */ public function setRevisionTimestamp( $timestamp ) { return wfSetVar( $this->mRevisionTimestamp, $timestamp ); @@ -1453,7 +1529,7 @@ class OutputPage extends ContextSource { * Get the timestamp of displayed revision. * This will be null if not filled by setRevisionTimestamp(). * - * @return String or null + * @return string|null */ public function getRevisionTimestamp() { return $this->mRevisionTimestamp; @@ -1462,8 +1538,8 @@ class OutputPage extends ContextSource { /** * Set the displayed file version * - * @param $file File|bool - * @return Mixed: previous value + * @param File|bool $file + * @return mixed Previous value */ public function setFileVersion( $file ) { $val = null; @@ -1476,7 +1552,7 @@ class OutputPage extends ContextSource { /** * Get the displayed file version * - * @return Array|null ('time' => MW timestamp, 'sha1' => sha1) + * @return array|null ('time' => MW timestamp, 'sha1' => sha1) */ public function getFileVersion() { return $this->mFileVersion; @@ -1485,7 +1561,7 @@ class OutputPage extends ContextSource { /** * Get the templates used on this page * - * @return Array (namespace => dbKey => revId) + * @return array (namespace => dbKey => revId) * @since 1.18 */ public function getTemplateIds() { @@ -1495,7 +1571,7 @@ class OutputPage extends ContextSource { /** * Get the files used on this page * - * @return Array (dbKey => array('time' => MW timestamp or null, 'sha1' => sha1 or '')) + * @return array (dbKey => array('time' => MW timestamp or null, 'sha1' => sha1 or '')) * @since 1.18 */ public function getFileSearchOptions() { @@ -1506,9 +1582,9 @@ class OutputPage extends ContextSource { * Convert wikitext to HTML and add it to the buffer * Default assumes that the current page title will be used. * - * @param $text String - * @param $linestart Boolean: is this the start of a line? - * @param $interface Boolean: is this text in the user interface language? + * @param string $text + * @param bool $linestart Is this the start of a line? + * @param bool $interface Is this text in the user interface language? */ public function addWikiText( $text, $linestart = true, $interface = true ) { $title = $this->getTitle(); // Work around E_STRICT @@ -1521,9 +1597,9 @@ class OutputPage extends ContextSource { /** * Add wikitext with a custom Title object * - * @param string $text wikitext - * @param $title Title object - * @param $linestart Boolean: is this the start of a line? + * @param string $text Wikitext + * @param Title $title + * @param bool $linestart Is this the start of a line? */ public function addWikiTextWithTitle( $text, &$title, $linestart = true ) { $this->addWikiTextTitle( $text, $title, $linestart ); @@ -1532,9 +1608,9 @@ class OutputPage extends ContextSource { /** * Add wikitext with a custom Title object and tidy enabled. * - * @param string $text wikitext - * @param $title Title object - * @param $linestart Boolean: is this the start of a line? + * @param string $text Wikitext + * @param Title $title + * @param bool $linestart Is this the start of a line? */ function addWikiTextTitleTidy( $text, &$title, $linestart = true ) { $this->addWikiTextTitle( $text, $title, $linestart, true ); @@ -1543,8 +1619,8 @@ class OutputPage extends ContextSource { /** * Add wikitext with tidy enabled * - * @param string $text wikitext - * @param $linestart Boolean: is this the start of a line? + * @param string $text Wikitext + * @param bool $linestart Is this the start of a line? */ public function addWikiTextTidy( $text, $linestart = true ) { $title = $this->getTitle(); @@ -1554,14 +1630,16 @@ class OutputPage extends ContextSource { /** * Add wikitext with a custom Title object * - * @param string $text wikitext - * @param $title Title object - * @param $linestart Boolean: is this the start of a line? - * @param $tidy Boolean: whether to use tidy - * @param $interface Boolean: whether it is an interface message - * (for example disables conversion) + * @param string $text Wikitext + * @param Title $title + * @param bool $linestart Is this the start of a line? + * @param bool $tidy Whether to use tidy + * @param bool $interface Whether it is an interface message + * (for example disables conversion) */ - public function addWikiTextTitle( $text, Title $title, $linestart, $tidy = false, $interface = false ) { + public function addWikiTextTitle( $text, Title $title, $linestart, + $tidy = false, $interface = false + ) { global $wgParser; wfProfileIn( __METHOD__ ); @@ -1570,7 +1648,7 @@ class OutputPage extends ContextSource { $oldTidy = $popts->setTidy( $tidy ); $popts->setInterfaceMessage( (bool)$interface ); - $parserOutput = $wgParser->parse( + $parserOutput = $wgParser->getFreshParser()->parse( $text, $title, $popts, $linestart, true, $this->mRevisionId ); @@ -1583,11 +1661,24 @@ class OutputPage extends ContextSource { } /** - * Add a ParserOutput object, but without Html + * Add a ParserOutput object, but without Html. + * + * @deprecated since 1.24, use addParserOutputMetadata() instead. + * @param ParserOutput $parserOutput + */ + public function addParserOutputNoText( $parserOutput ) { + $this->addParserOutputMetadata( $parserOutput ); + } + + /** + * Add all metadata associated with a ParserOutput object, but without the actual HTML. This + * includes categories, language links, ResourceLoader modules, effects of certain magic words, + * and so on. * - * @param $parserOutput ParserOutput object + * @since 1.24 + * @param ParserOutput $parserOutput */ - public function addParserOutputNoText( &$parserOutput ) { + public function addParserOutputMetadata( $parserOutput ) { $this->mLanguageLinks += $parserOutput->getLanguageLinks(); $this->addCategoryLinks( $parserOutput->getCategories() ); $this->mNewSectionLink = $parserOutput->getNewSection(); @@ -1603,6 +1694,7 @@ class OutputPage extends ContextSource { $this->addModuleScripts( $parserOutput->getModuleScripts() ); $this->addModuleStyles( $parserOutput->getModuleStyles() ); $this->addModuleMessages( $parserOutput->getModuleMessages() ); + $this->addJsConfigVars( $parserOutput->getJsConfigVars() ); $this->mPreventClickjacking = $this->mPreventClickjacking || $parserOutput->preventClickjacking(); @@ -1620,11 +1712,11 @@ class OutputPage extends ContextSource { } // Hooks registered in the object - global $wgParserOutputHooks; + $parserOutputHooks = $this->getConfig()->get( 'ParserOutputHooks' ); foreach ( $parserOutput->getOutputHooks() as $hookInfo ) { list( $hookName, $data ) = $hookInfo; - if ( isset( $wgParserOutputHooks[$hookName] ) ) { - call_user_func( $wgParserOutputHooks[$hookName], $this, $parserOutput, $data ); + if ( isset( $parserOutputHooks[$hookName] ) ) { + call_user_func( $parserOutputHooks[$hookName], $this, $parserOutput, $data ); } } @@ -1636,43 +1728,72 @@ class OutputPage extends ContextSource { } /** - * Add a ParserOutput object + * Add the HTML and enhancements for it (like ResourceLoader modules) associated with a + * ParserOutput object, without any other metadata. * - * @param $parserOutput ParserOutput + * @since 1.24 + * @param ParserOutput $parserOutput */ - function addParserOutput( &$parserOutput ) { - $this->addParserOutputNoText( $parserOutput ); - $parserOutput->setTOCEnabled( $this->mEnableTOC ); + public function addParserOutputContent( $parserOutput ) { + $this->addParserOutputText( $parserOutput ); + + $this->addModules( $parserOutput->getModules() ); + $this->addModuleScripts( $parserOutput->getModuleScripts() ); + $this->addModuleStyles( $parserOutput->getModuleStyles() ); + $this->addModuleMessages( $parserOutput->getModuleMessages() ); + + $this->addJsConfigVars( $parserOutput->getJsConfigVars() ); + } + + /** + * Add the HTML associated with a ParserOutput object, without any metadata. + * + * @since 1.24 + * @param ParserOutput $parserOutput + */ + public function addParserOutputText( $parserOutput ) { $text = $parserOutput->getText(); wfRunHooks( 'OutputPageBeforeHTML', array( &$this, &$text ) ); $this->addHTML( $text ); } /** + * Add everything from a ParserOutput object. + * + * @param ParserOutput $parserOutput + */ + function addParserOutput( $parserOutput ) { + $this->addParserOutputMetadata( $parserOutput ); + $parserOutput->setTOCEnabled( $this->mEnableTOC ); + + // Touch section edit links only if not previously disabled + if ( $parserOutput->getEditSectionTokens() ) { + $parserOutput->setEditSectionTokens( $this->mEnableSectionEditLinks ); + } + + $this->addParserOutputText( $parserOutput ); + } + + /** * Add the output of a QuickTemplate to the output buffer * - * @param $template QuickTemplate + * @param QuickTemplate $template */ public function addTemplate( &$template ) { - ob_start(); - $template->execute(); - $this->addHTML( ob_get_contents() ); - ob_end_clean(); + $this->addHTML( $template->getHTML() ); } /** * Parse wikitext and return the HTML. * - * @param $text String - * @param $linestart Boolean: is this the start of a line? - * @param $interface Boolean: use interface language ($wgLang instead of - * $wgContLang) while parsing language sensitive magic - * words like GRAMMAR and PLURAL. This also disables - * LanguageConverter. - * @param $language Language object: target language object, will override - * $interface + * @param string $text + * @param bool $linestart Is this the start of a line? + * @param bool $interface Use interface language ($wgLang instead of + * $wgContLang) while parsing language sensitive magic words like GRAMMAR and PLURAL. + * This also disables LanguageConverter. + * @param Language $language Target language object, will override $interface * @throws MWException - * @return String: HTML + * @return string HTML */ public function parse( $text, $linestart = true, $interface = false, $language = null ) { global $wgParser; @@ -1689,7 +1810,7 @@ class OutputPage extends ContextSource { $oldLang = $popts->setTargetLanguage( $language ); } - $parserOutput = $wgParser->parse( + $parserOutput = $wgParser->getFreshParser()->parse( $text, $this->getTitle(), $popts, $linestart, true, $this->mRevisionId ); @@ -1707,28 +1828,22 @@ class OutputPage extends ContextSource { /** * Parse wikitext, strip paragraphs, and return the HTML. * - * @param $text String - * @param $linestart Boolean: is this the start of a line? - * @param $interface Boolean: use interface language ($wgLang instead of - * $wgContLang) while parsing language sensitive magic - * words like GRAMMAR and PLURAL - * @return String: HTML + * @param string $text + * @param bool $linestart Is this the start of a line? + * @param bool $interface Use interface language ($wgLang instead of + * $wgContLang) while parsing language sensitive magic + * words like GRAMMAR and PLURAL + * @return string HTML */ public function parseInline( $text, $linestart = true, $interface = false ) { $parsed = $this->parse( $text, $linestart, $interface ); - - $m = array(); - if ( preg_match( '/^<p>(.*)\n?<\/p>\n?/sU', $parsed, $m ) ) { - $parsed = $m[1]; - } - - return $parsed; + return Parser::stripOuterParagraph( $parsed ); } /** * Set the value of the "s-maxage" part of the "Cache-control" HTTP header * - * @param $maxage Integer: maximum cache time on the Squid, in seconds. + * @param int $maxage Maximum cache time on the Squid, in seconds. */ public function setSquidMaxage( $maxage ) { $this->mSquidMaxage = $maxage; @@ -1737,7 +1852,7 @@ class OutputPage extends ContextSource { /** * Use enableClientCache(false) to force it to send nocache headers * - * @param $state bool + * @param bool $state * * @return bool */ @@ -1748,20 +1863,20 @@ class OutputPage extends ContextSource { /** * Get the list of cookies that will influence on the cache * - * @return Array + * @return array */ function getCacheVaryCookies() { - global $wgCookiePrefix, $wgCacheVaryCookies; static $cookies; if ( $cookies === null ) { + $config = $this->getConfig(); $cookies = array_merge( array( - "{$wgCookiePrefix}Token", - "{$wgCookiePrefix}LoggedOut", + $config->get( 'CookiePrefix' ) . 'Token', + $config->get( 'CookiePrefix' ) . 'LoggedOut', "forceHTTPS", session_name() ), - $wgCacheVaryCookies + $config->get( 'CacheVaryCookies' ) ); wfRunHooks( 'GetCacheVaryCookies', array( $this, &$cookies ) ); } @@ -1772,7 +1887,7 @@ class OutputPage extends ContextSource { * Check if the request has a cache-varying cookie header * If it does, it's very important that we don't allow public caching * - * @return Boolean + * @return bool */ function haveCacheVaryCookies() { $cookieHeader = $this->getRequest()->getHeader( 'cookie' ); @@ -1794,8 +1909,8 @@ class OutputPage extends ContextSource { /** * Add an HTTP header that will influence on the cache * - * @param string $header header name - * @param $option Array|null + * @param string $header Header name + * @param array|null $option * @todo FIXME: Document the $option parameter; it appears to be for * X-Vary-Options but what format is acceptable? */ @@ -1816,7 +1931,7 @@ class OutputPage extends ContextSource { * Return a Vary: header on which to vary caches. Based on the keys of $mVaryHeader, * such as Accept-Encoding or Cookie * - * @return String + * @return string */ public function getVaryHeader() { return 'Vary: ' . join( ', ', array_keys( $this->mVaryHeader ) ); @@ -1825,7 +1940,7 @@ class OutputPage extends ContextSource { /** * Get a complete X-Vary-Options header * - * @return String + * @return string */ public function getXVO() { $cvCookies = $this->getCacheVaryCookies(); @@ -1858,7 +1973,12 @@ class OutputPage extends ContextSource { * /w/index.php?title=Main_page&variant=zh-cn should never be served. */ function addAcceptLanguage() { - $lang = $this->getTitle()->getPageLanguage(); + $title = $this->getTitle(); + if ( !$title instanceof Title ) { + return; + } + + $lang = $title->getPageLanguage(); if ( !$this->getRequest()->getCheck( 'variant' ) && $lang->hasVariants() ) { $variants = $lang->getVariants(); $aloption = array(); @@ -1889,7 +2009,7 @@ class OutputPage extends ContextSource { * This is the default for special pages. If you display a CSRF-protected * form on an ordinary view page, then you need to call this function. * - * @param $enable bool + * @param bool $enable */ public function preventClickjacking( $enable = true ) { $this->mPreventClickjacking = $enable; @@ -1908,7 +2028,7 @@ class OutputPage extends ContextSource { * Get the prevent-clickjacking flag * * @since 1.24 - * @return boolean + * @return bool */ public function getPreventClickjacking() { return $this->mPreventClickjacking; @@ -1922,11 +2042,11 @@ class OutputPage extends ContextSource { * @return string */ public function getFrameOptions() { - global $wgBreakFrames, $wgEditPageFrameOptions; - if ( $wgBreakFrames ) { + $config = $this->getConfig(); + if ( $config->get( 'BreakFrames' ) ) { return 'DENY'; - } elseif ( $this->mPreventClickjacking && $wgEditPageFrameOptions ) { - return $wgEditPageFrameOptions; + } elseif ( $this->mPreventClickjacking && $config->get( 'EditPageFrameOptions' ) ) { + return $config->get( 'EditPageFrameOptions' ); } return false; } @@ -1935,10 +2055,9 @@ class OutputPage extends ContextSource { * Send cache control HTTP headers */ public function sendCacheControl() { - global $wgUseSquid, $wgUseESI, $wgUseETag, $wgSquidMaxage, $wgUseXVO; - $response = $this->getRequest()->response(); - if ( $wgUseETag && $this->mETag ) { + $config = $this->getConfig(); + if ( $config->get( 'UseETag' ) && $this->mETag ) { $response->header( "ETag: $this->mETag" ); } @@ -1949,39 +2068,41 @@ class OutputPage extends ContextSource { # maintain different caches for logged-in users and non-logged in ones $response->header( $this->getVaryHeader() ); - if ( $wgUseXVO ) { + if ( $config->get( 'UseXVO' ) ) { # Add an X-Vary-Options header for Squid with Wikimedia patches $response->header( $this->getXVO() ); } if ( $this->mEnableClientCache ) { if ( - $wgUseSquid && session_id() == '' && !$this->isPrintable() && + $config->get( 'UseSquid' ) && session_id() == '' && !$this->isPrintable() && $this->mSquidMaxage != 0 && !$this->haveCacheVaryCookies() ) { - if ( $wgUseESI ) { + if ( $config->get( 'UseESI' ) ) { # We'll purge the proxy cache explicitly, but require end user agents # to revalidate against the proxy on each visit. # Surrogate-Control controls our Squid, Cache-Control downstream caches - wfDebug( __METHOD__ . ": proxy caching with ESI; {$this->mLastModified} **\n", false ); + wfDebug( __METHOD__ . ": proxy caching with ESI; {$this->mLastModified} **\n", 'log' ); # start with a shorter timeout for initial testing # header( 'Surrogate-Control: max-age=2678400+2678400, content="ESI/1.0"'); - $response->header( 'Surrogate-Control: max-age=' . $wgSquidMaxage . '+' . $this->mSquidMaxage . ', content="ESI/1.0"' ); + $response->header( 'Surrogate-Control: max-age=' . $config->get( 'SquidMaxage' ) + . '+' . $this->mSquidMaxage . ', content="ESI/1.0"' ); $response->header( 'Cache-Control: s-maxage=0, must-revalidate, max-age=0' ); } else { # We'll purge the proxy cache for anons explicitly, but require end user agents # to revalidate against the proxy on each visit. # IMPORTANT! The Squid needs to replace the Cache-Control header with # Cache-Control: s-maxage=0, must-revalidate, max-age=0 - wfDebug( __METHOD__ . ": local proxy caching; {$this->mLastModified} **\n", false ); + wfDebug( __METHOD__ . ": local proxy caching; {$this->mLastModified} **\n", 'log' ); # start with a shorter timeout for initial testing # header( "Cache-Control: s-maxage=2678400, must-revalidate, max-age=0" ); - $response->header( 'Cache-Control: s-maxage=' . $this->mSquidMaxage . ', must-revalidate, max-age=0' ); + $response->header( 'Cache-Control: s-maxage=' . $this->mSquidMaxage + . ', must-revalidate, max-age=0' ); } } else { # We do want clients to cache if they can, but they *must* check for updates # on revisiting the page. - wfDebug( __METHOD__ . ": private caching; {$this->mLastModified} **\n", false ); + wfDebug( __METHOD__ . ": private caching; {$this->mLastModified} **\n", 'log' ); $response->header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', 0 ) . ' GMT' ); $response->header( "Cache-Control: private, must-revalidate, max-age=0" ); } @@ -1989,7 +2110,7 @@ class OutputPage extends ContextSource { $response->header( "Last-Modified: {$this->mLastModified}" ); } } else { - wfDebug( __METHOD__ . ": no caching **\n", false ); + wfDebug( __METHOD__ . ": no caching **\n", 'log' ); # In general, the absence of a last modified header should be enough to prevent # the client from using its cache. We send a few other things just to make sure. @@ -2000,26 +2121,11 @@ class OutputPage extends ContextSource { } /** - * Get the message associated with the HTTP response code $code - * - * @param $code Integer: status code - * @return String or null: message or null if $code is not in the list of - * messages - * - * @deprecated since 1.18 Use HttpStatus::getMessage() instead. - */ - public static function getStatusMessage( $code ) { - wfDeprecated( __METHOD__, '1.18' ); - return HttpStatus::getMessage( $code ); - } - - /** * Finally, all the text has been munged and accumulated into * the object, let's actually output it: */ public function output() { - global $wgLanguageCode, $wgDebugRedirects, $wgMimeType, $wgVaryOnXFP, - $wgUseAjax, $wgResponsiveImages; + global $wgLanguageCode; if ( $this->mDoNothing ) { return; @@ -2028,6 +2134,7 @@ class OutputPage extends ContextSource { wfProfileIn( __METHOD__ ); $response = $this->getRequest()->response(); + $config = $this->getConfig(); if ( $this->mRedirect != '' ) { # Standards require redirect URLs to be absolute @@ -2038,19 +2145,19 @@ class OutputPage extends ContextSource { if ( wfRunHooks( "BeforePageRedirect", array( $this, &$redirect, &$code ) ) ) { if ( $code == '301' || $code == '303' ) { - if ( !$wgDebugRedirects ) { + if ( !$config->get( 'DebugRedirects' ) ) { $message = HttpStatus::getMessage( $code ); $response->header( "HTTP/1.1 $code $message" ); } $this->mLastModified = wfTimestamp( TS_RFC2822 ); } - if ( $wgVaryOnXFP ) { + if ( $config->get( 'VaryOnXFP' ) ) { $this->addVaryHeader( 'X-Forwarded-Proto' ); } $this->sendCacheControl(); $response->header( "Content-Type: text/html; charset=utf-8" ); - if ( $wgDebugRedirects ) { + if ( $config->get( 'DebugRedirects' ) ) { $url = htmlspecialchars( $redirect ); print "<html>\n<head>\n<title>Redirect</title>\n</head>\n<body>\n"; print "<p>Location: <a href=\"$url\">$url</a></p>\n"; @@ -2072,9 +2179,13 @@ class OutputPage extends ContextSource { # Buffer output; final headers may depend on later processing ob_start(); - $response->header( "Content-type: $wgMimeType; charset=UTF-8" ); + $response->header( 'Content-type: ' . $config->get( 'MimeType' ) . '; charset=UTF-8' ); $response->header( 'Content-language: ' . $wgLanguageCode ); + // Avoid Internet Explorer "compatibility view" in IE 8-10, so that + // jQuery etc. can work correctly. + $response->header( 'X-UA-Compatible: IE=Edge' ); + // Prevent framing, if requested $frameOptions = $this->getFrameOptions(); if ( $frameOptions ) { @@ -2097,7 +2208,7 @@ class OutputPage extends ContextSource { ); // Support for high-density display images if enabled - if ( $wgResponsiveImages ) { + if ( $config->get( 'ResponsiveImages' ) ) { $coreModules[] = 'mediawiki.hidpi'; } @@ -2106,10 +2217,6 @@ class OutputPage extends ContextSource { $this->addModules( $group ); } MWDebug::addModules( $this ); - if ( $wgUseAjax ) { - // FIXME: deprecate? - not clear why this is useful - wfRunHooks( 'AjaxAddScript', array( &$this ) ); - } // Hook that allows last minute changes to the output page, e.g. // adding of CSS or Javascript by extensions. @@ -2133,7 +2240,7 @@ class OutputPage extends ContextSource { /** * Actually output something with print. * - * @param string $ins the string to output + * @param string $ins The string to output * @deprecated since 1.22 Use echo yourself. */ public function out( $ins ) { @@ -2154,8 +2261,8 @@ class OutputPage extends ContextSource { * indexing, clear the current text and redirect, set the page's title * and optionally an custom HTML title (content of the "<title>" tag). * - * @param string|Message $pageTitle will be passed directly to setPageTitle() - * @param string|Message $htmlTitle will be passed directly to setHTMLTitle(); + * @param string|Message $pageTitle Will be passed directly to setPageTitle() + * @param string|Message $htmlTitle Will be passed directly to setHTMLTitle(); * optional, if not passed the "<title>" attribute will be * based on $pageTitle */ @@ -2175,13 +2282,14 @@ class OutputPage extends ContextSource { /** * Output a standard error page * + * showErrorPage( 'titlemsg', 'pagetextmsg' ); * showErrorPage( 'titlemsg', 'pagetextmsg', array( 'param1', 'param2' ) ); * showErrorPage( 'titlemsg', $messageObject ); - * showErrorPage( $titleMessageObj, $messageObject ); + * showErrorPage( $titleMessageObject, $messageObject ); * - * @param $title Mixed: message key (string) for page title, or a Message object - * @param $msg Mixed: message key (string) for page text, or a Message object - * @param array $params message parameters; ignored if $msg is a Message object + * @param string|Message $title Message key (string) for page title, or a Message object + * @param string|Message $msg Message key (string) for page text, or a Message object + * @param array $params Message parameters; ignored if $msg is a Message object */ public function showErrorPage( $title, $msg, $params = array() ) { if ( !$title instanceof Message ) { @@ -2191,6 +2299,11 @@ class OutputPage extends ContextSource { $this->prepareErrorPage( $title ); if ( $msg instanceof Message ) { + if ( $params !== array() ) { + trigger_error( 'Argument ignored: $params. The message parameters argument ' + . 'is discarded when the $msg argument is a Message object instead of ' + . 'a string.', E_USER_NOTICE ); + } $this->addHTML( $msg->parseAsBlock() ); } else { $this->addWikiMsgArray( $msg, $params ); @@ -2202,10 +2315,10 @@ class OutputPage extends ContextSource { /** * Output a standard permission error page * - * @param array $errors error message keys - * @param string $action action that was denied or null if unknown + * @param array $errors Error message keys + * @param string $action Action that was denied or null if unknown */ - public function showPermissionsErrorPage( $errors, $action = null ) { + public function showPermissionsErrorPage( array $errors, $action = null ) { // For some action (read, edit, create and upload), display a "login to do this action" // error if all of the following conditions are met: // 1. the user is not logged in @@ -2275,7 +2388,7 @@ class OutputPage extends ContextSource { * Display an error page indicating that a given version of MediaWiki is * required to use it * - * @param $version Mixed: the version of MediaWiki needed to use the page + * @param mixed $version The version of MediaWiki needed to use the page */ public function versionRequired( $version ) { $this->prepareErrorPage( $this->msg( 'versionrequired', $version ) ); @@ -2287,7 +2400,7 @@ class OutputPage extends ContextSource { /** * Display an error page noting that a given permission bit is required. * @deprecated since 1.18, just throw the exception directly - * @param string $permission key required + * @param string $permission Key required * @throws PermissionsError */ public function permissionRequired( $permission ) { @@ -2297,7 +2410,7 @@ class OutputPage extends ContextSource { /** * Produce the stock "please login to use the wiki" page * - * @deprecated in 1.19; throw the exception directly + * @deprecated since 1.19; throw the exception directly */ public function loginToUse() { throw new PermissionsError( 'read' ); @@ -2306,11 +2419,11 @@ class OutputPage extends ContextSource { /** * Format a list of error messages * - * @param array $errors of arrays returned by Title::getUserPermissionsErrors - * @param string $action action that was denied or null if unknown - * @return String: the wikitext error-messages, formatted into a list. + * @param array $errors Array of arrays returned by Title::getUserPermissionsErrors + * @param string $action Action that was denied or null if unknown + * @return string The wikitext error-messages, formatted into a list. */ - public function formatPermissionsErrorMessage( $errors, $action = null ) { + public function formatPermissionsErrorMessage( array $errors, $action = null ) { if ( $action == null ) { $text = $this->msg( 'permissionserrorstext', count( $errors ) )->plain() . "\n\n"; } else { @@ -2355,13 +2468,16 @@ class OutputPage extends ContextSource { * * @todo Needs to be split into multiple functions. * - * @param $source String: source code to show (or null). - * @param $protected Boolean: is this a permissions error? - * @param $reasons Array: list of reasons for this error, as returned by Title::getUserPermissionsErrors(). - * @param $action String: action that was denied or null if unknown + * @param string $source Source code to show (or null). + * @param bool $protected Is this a permissions error? + * @param array $reasons List of reasons for this error, as returned by + * Title::getUserPermissionsErrors(). + * @param string $action Action that was denied or null if unknown * @throws ReadOnlyError */ - public function readOnlyPage( $source = null, $protected = false, $reasons = array(), $action = null ) { + public function readOnlyPage( $source = null, $protected = false, + array $reasons = array(), $action = null + ) { $this->setRobotPolicy( 'noindex,nofollow' ); $this->setArticleRelated( false ); @@ -2432,12 +2548,12 @@ $templates * then the warning is a bit more obvious. If the lag is * lower than $wgSlaveLagWarning, then no warning is shown. * - * @param $lag Integer: slave lag + * @param int $lag Slave lag */ public function showLagWarning( $lag ) { - global $wgSlaveLagWarning, $wgSlaveLagCritical; - if ( $lag >= $wgSlaveLagWarning ) { - $message = $lag < $wgSlaveLagCritical + $config = $this->getConfig(); + if ( $lag >= $config->get( 'SlaveLagWarning' ) ) { + $message = $lag < $config->get( 'SlaveLagCritical' ) ? 'lag-warn-normal' : 'lag-warn-high'; $wrap = Html::rawElement( 'div', array( 'class' => "mw-{$message}" ), "\n$1\n" ); @@ -2474,12 +2590,12 @@ $templates /** * Add a "return to" link pointing to a specified title * - * @param $title Title to link - * @param array $query query string parameters - * @param string $text text of the link (input is not escaped) - * @param $options Options array to pass to Linker + * @param Title $title Title to link + * @param array $query Query string parameters + * @param string $text Text of the link (input is not escaped) + * @param array $options Options array to pass to Linker */ - public function addReturnTo( $title, $query = array(), $text = null, $options = array() ) { + public function addReturnTo( $title, array $query = array(), $text = null, $options = array() ) { $link = $this->msg( 'returnto' )->rawParams( Linker::link( $title, $text, array(), $query, $options ) )->escaped(); $this->addHTML( "<p id=\"mw-returnto\">{$link}</p>\n" ); @@ -2489,9 +2605,9 @@ $templates * Add a "return to" link pointing to a specified title, * or the title indicated in the request, or else the main page * - * @param $unused - * @param $returnto Title or String to return to - * @param string $returntoquery query string for the return to link + * @param mixed $unused + * @param Title|string $returnto Title or String to return to + * @param string $returntoquery Query string for the return to link */ public function returnToMain( $unused = null, $returnto = null, $returntoquery = null ) { if ( $returnto == null ) { @@ -2519,20 +2635,20 @@ $templates } /** - * @param $sk Skin The given Skin - * @param $includeStyle Boolean: unused - * @return String: The doctype, opening "<html>", and head element. + * @param Skin $sk The given Skin + * @param bool $includeStyle Unused + * @return string The doctype, opening "<html>", and head element. */ public function headElement( Skin $sk, $includeStyle = true ) { - global $wgContLang, $wgMimeType; + global $wgContLang; $userdir = $this->getLanguage()->getDir(); $sitedir = $wgContLang->getDir(); - $ret = Html::htmlHeader( array( 'lang' => $this->getLanguage()->getHtmlCode(), 'dir' => $userdir, 'class' => 'client-nojs' ) ); + $ret = Html::htmlHeader( $sk->getHtmlElementAttributes() ); if ( $this->getHTMLTitle() == '' ) { - $this->setHTMLTitle( $this->msg( 'pagetitle', $this->getPageTitle() ) ); + $this->setHTMLTitle( $this->msg( 'pagetitle', $this->getPageTitle() )->inContentLanguage() ); } $openHead = Html::openElement( 'head' ); @@ -2541,7 +2657,7 @@ $templates $ret .= "$openHead\n"; } - if ( !Html::isXmlMimeType( $wgMimeType ) ) { + if ( !Html::isXmlMimeType( $this->getConfig()->get( 'MimeType' ) ) ) { // Add <meta charset="UTF-8"> // This should be before <title> since it defines the charset used by // text including the text inside <title>. @@ -2550,17 +2666,23 @@ $templates // Our XML declaration is output by Html::htmlHeader. // http://www.whatwg.org/html/semantics.html#attr-meta-http-equiv-content-type // http://www.whatwg.org/html/semantics.html#charset - $ret .= Html::element( 'meta', array( 'charset' => 'UTF-8' ) ); + $ret .= Html::element( 'meta', array( 'charset' => 'UTF-8' ) ) . "\n"; } $ret .= Html::element( 'title', null, $this->getHTMLTitle() ) . "\n"; - $ret .= implode( "\n", array( - $this->getHeadLinks(), - $this->buildCssLinks(), - $this->getHeadScripts(), - $this->getHeadItems() - ) ); + foreach ( $this->getHeadLinksArray() as $item ) { + $ret .= $item . "\n"; + } + + // No newline after buildCssLinks since makeResourceLoaderLink did that already + $ret .= $this->buildCssLinks(); + + $ret .= $this->getHeadScripts() . "\n"; + + foreach ( $this->mHeadItems as $item ) { + $ret .= $item . "\n"; + } $closeHead = Html::closeElement( 'head' ); if ( $closeHead ) { @@ -2581,7 +2703,8 @@ $templates $bodyClasses[] = $sk->getPageClasses( $this->getTitle() ); $bodyClasses[] = 'skin-' . Sanitizer::escapeClass( $sk->getSkinName() ); - $bodyClasses[] = 'action-' . Sanitizer::escapeClass( Action::getActionName( $this->getContext() ) ); + $bodyClasses[] = + 'action-' . Sanitizer::escapeClass( Action::getActionName( $this->getContext() ) ); $bodyAttrs = array(); // While the implode() is not strictly needed, it's used for backwards compatibility @@ -2604,27 +2727,34 @@ $templates */ public function getResourceLoader() { if ( is_null( $this->mResourceLoader ) ) { - $this->mResourceLoader = new ResourceLoader(); + $this->mResourceLoader = new ResourceLoader( $this->getConfig() ); } return $this->mResourceLoader; } /** - * TODO: Document - * @param $modules Array/string with the module name(s) + * @todo Document + * @param array|string $modules One or more module names * @param string $only ResourceLoaderModule TYPE_ class constant - * @param $useESI boolean - * @param array $extraQuery with extra query parameters to add to each request. array( param => value ) - * @param $loadCall boolean If true, output an (asynchronous) mw.loader.load() call rather than a "<script src='...'>" tag - * @return string html "<script>" and "<style>" tags - */ - protected function makeResourceLoaderLink( $modules, $only, $useESI = false, array $extraQuery = array(), $loadCall = false ) { - global $wgResourceLoaderUseESI; - + * @param bool $useESI + * @param array $extraQuery Array with extra query parameters to add to each + * request. array( param => value ). + * @param bool $loadCall If true, output an (asynchronous) mw.loader.load() + * call rather than a "<script src='...'>" tag. + * @return string The html "<script>", "<link>" and "<style>" tags + */ + protected function makeResourceLoaderLink( $modules, $only, $useESI = false, + array $extraQuery = array(), $loadCall = false + ) { $modules = (array)$modules; + $links = array( + 'html' => '', + 'states' => array(), + ); + if ( !count( $modules ) ) { - return ''; + return $links; } if ( count( $modules ) > 1 ) { @@ -2635,20 +2765,23 @@ $templates if ( ResourceLoader::inDebugMode() ) { // Recursively call us for every item - $links = ''; foreach ( $modules as $name ) { - $links .= $this->makeResourceLoaderLink( $name, $only, $useESI ); + $link = $this->makeResourceLoaderLink( $name, $only, $useESI ); + $links['html'] .= $link['html']; + $links['states'] += $link['states']; } return $links; } } + if ( !is_null( $this->mTarget ) ) { $extraQuery['target'] = $this->mTarget; } - // Create keyed-by-group list of module objects from modules list - $groups = array(); + // Create keyed-by-source and then keyed-by-group list of module objects from modules list + $sortedModules = array(); $resourceLoader = $this->getResourceLoader(); + $resourceLoaderUseESI = $this->getConfig()->get( 'ResourceLoaderUseESI' ); foreach ( $modules as $name ) { $module = $resourceLoader->getModule( $name ); # Check that we're allowed to include this module on this page @@ -2657,156 +2790,196 @@ $templates && $only == ResourceLoaderModule::TYPE_SCRIPTS ) || ( $module->getOrigin() > $this->getAllowedModules( ResourceLoaderModule::TYPE_STYLES ) && $only == ResourceLoaderModule::TYPE_STYLES ) + || ( $module->getOrigin() > $this->getAllowedModules( ResourceLoaderModule::TYPE_COMBINED ) + && $only == ResourceLoaderModule::TYPE_COMBINED ) || ( $this->mTarget && !in_array( $this->mTarget, $module->getTargets() ) ) ) { continue; } - $group = $module->getGroup(); - if ( !isset( $groups[$group] ) ) { - $groups[$group] = array(); - } - $groups[$group][$name] = $module; + $sortedModules[$module->getSource()][$module->getGroup()][$name] = $module; } - $links = ''; - foreach ( $groups as $group => $grpModules ) { - // Special handling for user-specific groups - $user = null; - if ( ( $group === 'user' || $group === 'private' ) && $this->getUser()->isLoggedIn() ) { - $user = $this->getUser()->getName(); - } + foreach ( $sortedModules as $source => $groups ) { + foreach ( $groups as $group => $grpModules ) { + // Special handling for user-specific groups + $user = null; + if ( ( $group === 'user' || $group === 'private' ) && $this->getUser()->isLoggedIn() ) { + $user = $this->getUser()->getName(); + } - // Create a fake request based on the one we are about to make so modules return - // correct timestamp and emptiness data - $query = ResourceLoader::makeLoaderQuery( - array(), // modules; not determined yet - $this->getLanguage()->getCode(), - $this->getSkin()->getSkinName(), - $user, - null, // version; not determined yet - ResourceLoader::inDebugMode(), - $only === ResourceLoaderModule::TYPE_COMBINED ? null : $only, - $this->isPrintable(), - $this->getRequest()->getBool( 'handheld' ), - $extraQuery - ); - $context = new ResourceLoaderContext( $resourceLoader, new FauxRequest( $query ) ); - // Extract modules that know they're empty - $emptyModules = array(); - foreach ( $grpModules as $key => $module ) { - if ( $module->isKnownEmpty( $context ) ) { - $emptyModules[$key] = 'ready'; - unset( $grpModules[$key] ); + // Create a fake request based on the one we are about to make so modules return + // correct timestamp and emptiness data + $query = ResourceLoader::makeLoaderQuery( + array(), // modules; not determined yet + $this->getLanguage()->getCode(), + $this->getSkin()->getSkinName(), + $user, + null, // version; not determined yet + ResourceLoader::inDebugMode(), + $only === ResourceLoaderModule::TYPE_COMBINED ? null : $only, + $this->isPrintable(), + $this->getRequest()->getBool( 'handheld' ), + $extraQuery + ); + $context = new ResourceLoaderContext( $resourceLoader, new FauxRequest( $query ) ); + + + // Extract modules that know they're empty and see if we have one or more + // raw modules + $isRaw = false; + foreach ( $grpModules as $key => $module ) { + // Inline empty modules: since they're empty, just mark them as 'ready' (bug 46857) + // If we're only getting the styles, we don't need to do anything for empty modules. + if ( $module->isKnownEmpty( $context ) ) { + unset( $grpModules[$key] ); + if ( $only !== ResourceLoaderModule::TYPE_STYLES ) { + $links['states'][$key] = 'ready'; + } + } + + $isRaw |= $module->isRaw(); } - } - // Inline empty modules: since they're empty, just mark them as 'ready' - if ( count( $emptyModules ) > 0 && $only !== ResourceLoaderModule::TYPE_STYLES ) { - // If we're only getting the styles, we don't need to do anything for empty modules. - $links .= Html::inlineScript( - ResourceLoader::makeLoaderConditionalScript( - ResourceLoader::makeLoaderStateScript( $emptyModules ) - ) - ) . "\n"; - } - // If there are no modules left, skip this group - if ( count( $grpModules ) === 0 ) { - continue; - } + // If there are no non-empty modules, skip this group + if ( count( $grpModules ) === 0 ) { + continue; + } - // Inline private modules. These can't be loaded through load.php for security - // reasons, see bug 34907. Note that these modules should be loaded from - // getHeadScripts() before the first loader call. Otherwise other modules can't - // properly use them as dependencies (bug 30914) - if ( $group === 'private' ) { - if ( $only == ResourceLoaderModule::TYPE_STYLES ) { - $links .= Html::inlineStyle( - $resourceLoader->makeModuleResponse( $context, $grpModules ) - ); - } else { - $links .= Html::inlineScript( - ResourceLoader::makeLoaderConditionalScript( + // Inline private modules. These can't be loaded through load.php for security + // reasons, see bug 34907. Note that these modules should be loaded from + // getHeadScripts() before the first loader call. Otherwise other modules can't + // properly use them as dependencies (bug 30914) + if ( $group === 'private' ) { + if ( $only == ResourceLoaderModule::TYPE_STYLES ) { + $links['html'] .= Html::inlineStyle( $resourceLoader->makeModuleResponse( $context, $grpModules ) - ) - ); + ); + } else { + $links['html'] .= Html::inlineScript( + ResourceLoader::makeLoaderConditionalScript( + $resourceLoader->makeModuleResponse( $context, $grpModules ) + ) + ); + } + $links['html'] .= "\n"; + continue; } - $links .= "\n"; - continue; - } - // Special handling for the user group; because users might change their stuff - // on-wiki like user pages, or user preferences; we need to find the highest - // timestamp of these user-changeable modules so we can ensure cache misses on change - // This should NOT be done for the site group (bug 27564) because anons get that too - // and we shouldn't be putting timestamps in Squid-cached HTML - $version = null; - if ( $group === 'user' ) { - // Get the maximum timestamp - $timestamp = 1; - foreach ( $grpModules as $module ) { - $timestamp = max( $timestamp, $module->getModifiedTime( $context ) ); + + // Special handling for the user group; because users might change their stuff + // on-wiki like user pages, or user preferences; we need to find the highest + // timestamp of these user-changeable modules so we can ensure cache misses on change + // This should NOT be done for the site group (bug 27564) because anons get that too + // and we shouldn't be putting timestamps in Squid-cached HTML + $version = null; + if ( $group === 'user' ) { + // Get the maximum timestamp + $timestamp = 1; + foreach ( $grpModules as $module ) { + $timestamp = max( $timestamp, $module->getModifiedTime( $context ) ); + } + // Add a version parameter so cache will break when things change + $query['version'] = wfTimestamp( TS_ISO_8601_BASIC, $timestamp ); } - // Add a version parameter so cache will break when things change - $version = wfTimestamp( TS_ISO_8601_BASIC, $timestamp ); - } - $url = ResourceLoader::makeLoaderURL( - array_keys( $grpModules ), - $this->getLanguage()->getCode(), - $this->getSkin()->getSkinName(), - $user, - $version, - ResourceLoader::inDebugMode(), - $only === ResourceLoaderModule::TYPE_COMBINED ? null : $only, - $this->isPrintable(), - $this->getRequest()->getBool( 'handheld' ), - $extraQuery - ); - if ( $useESI && $wgResourceLoaderUseESI ) { - $esi = Xml::element( 'esi:include', array( 'src' => $url ) ); - if ( $only == ResourceLoaderModule::TYPE_STYLES ) { - $link = Html::inlineStyle( $esi ); + $query['modules'] = ResourceLoader::makePackedModulesString( array_keys( $grpModules ) ); + $moduleContext = new ResourceLoaderContext( $resourceLoader, new FauxRequest( $query ) ); + $url = $resourceLoader->createLoaderURL( $source, $moduleContext, $extraQuery ); + + if ( $useESI && $resourceLoaderUseESI ) { + $esi = Xml::element( 'esi:include', array( 'src' => $url ) ); + if ( $only == ResourceLoaderModule::TYPE_STYLES ) { + $link = Html::inlineStyle( $esi ); + } else { + $link = Html::inlineScript( $esi ); + } } else { - $link = Html::inlineScript( $esi ); + // Automatically select style/script elements + if ( $only === ResourceLoaderModule::TYPE_STYLES ) { + $link = Html::linkedStyle( $url ); + } elseif ( $loadCall ) { + $link = Html::inlineScript( + ResourceLoader::makeLoaderConditionalScript( + Xml::encodeJsCall( 'mw.loader.load', array( $url, 'text/javascript', true ) ) + ) + ); + } else { + $link = Html::linkedScript( $url ); + if ( $context->getOnly() === 'scripts' && !$context->getRaw() && !$isRaw ) { + // Wrap only=script requests in a conditional as browsers not supported + // by the startup module would unconditionally execute this module. + // Otherwise users will get "ReferenceError: mw is undefined" or + // "jQuery is undefined" from e.g. a "site" module. + $link = Html::inlineScript( + ResourceLoader::makeLoaderConditionalScript( + Xml::encodeJsCall( 'document.write', array( $link ) ) + ) + ); + } + + // For modules requested directly in the html via <link> or <script>, + // tell mw.loader they are being loading to prevent duplicate requests. + foreach ( $grpModules as $key => $module ) { + // Don't output state=loading for the startup module.. + if ( $key !== 'startup' ) { + $links['states'][$key] = 'loading'; + } + } + } } - } else { - // Automatically select style/script elements - if ( $only === ResourceLoaderModule::TYPE_STYLES ) { - $link = Html::linkedStyle( $url ); - } elseif ( $loadCall ) { - $link = Html::inlineScript( - ResourceLoader::makeLoaderConditionalScript( - Xml::encodeJsCall( 'mw.loader.load', array( $url, 'text/javascript', true ) ) - ) - ); + + if ( $group == 'noscript' ) { + $links['html'] .= Html::rawElement( 'noscript', array(), $link ) . "\n"; } else { - $link = Html::linkedScript( $url ); + $links['html'] .= $link . "\n"; } } + } - if ( $group == 'noscript' ) { - $links .= Html::rawElement( 'noscript', array(), $link ) . "\n"; + return $links; + } + + /** + * Build html output from an array of links from makeResourceLoaderLink. + * @param array $links + * @return string HTML + */ + protected static function getHtmlFromLoaderLinks( array $links ) { + $html = ''; + $states = array(); + foreach ( $links as $link ) { + if ( !is_array( $link ) ) { + $html .= $link; } else { - $links .= $link . "\n"; + $html .= $link['html']; + $states += $link['states']; } } - return $links; + + if ( count( $states ) ) { + $html = Html::inlineScript( + ResourceLoader::makeLoaderConditionalScript( + ResourceLoader::makeLoaderStateScript( $states ) + ) + ) . "\n" . $html; + } + + return $html; } /** * JS stuff to put in the "<head>". This is the startup module, config * vars and modules marked with position 'top' * - * @return String: HTML fragment + * @return string HTML fragment */ function getHeadScripts() { - global $wgResourceLoaderExperimentalAsyncLoading; - // Startup - this will immediately load jquery and mediawiki modules - $scripts = $this->makeResourceLoaderLink( 'startup', ResourceLoaderModule::TYPE_SCRIPTS, true ); + $links = array(); + $links[] = $this->makeResourceLoaderLink( 'startup', ResourceLoaderModule::TYPE_SCRIPTS, true ); // Load config before anything else - $scripts .= Html::inlineScript( + $links[] = Html::inlineScript( ResourceLoader::makeLoaderConditionalScript( ResourceLoader::makeConfigSetScript( $this->getJSVars() ) ) @@ -2816,51 +2989,58 @@ $templates // This needs to be TYPE_COMBINED so these modules are properly wrapped // in mw.loader.implement() calls and deferred until mw.user is available $embedScripts = array( 'user.options', 'user.tokens' ); - $scripts .= $this->makeResourceLoaderLink( $embedScripts, ResourceLoaderModule::TYPE_COMBINED ); + $links[] = $this->makeResourceLoaderLink( $embedScripts, ResourceLoaderModule::TYPE_COMBINED ); - // Script and Messages "only" requests marked for top inclusion + // Scripts and messages "only" requests marked for top inclusion // Messages should go first - $scripts .= $this->makeResourceLoaderLink( $this->getModuleMessages( true, 'top' ), ResourceLoaderModule::TYPE_MESSAGES ); - $scripts .= $this->makeResourceLoaderLink( $this->getModuleScripts( true, 'top' ), ResourceLoaderModule::TYPE_SCRIPTS ); + $links[] = $this->makeResourceLoaderLink( + $this->getModuleMessages( true, 'top' ), + ResourceLoaderModule::TYPE_MESSAGES + ); + $links[] = $this->makeResourceLoaderLink( + $this->getModuleScripts( true, 'top' ), + ResourceLoaderModule::TYPE_SCRIPTS + ); // Modules requests - let the client calculate dependencies and batch requests as it likes // Only load modules that have marked themselves for loading at the top $modules = $this->getModules( true, 'top' ); if ( $modules ) { - $scripts .= Html::inlineScript( + $links[] = Html::inlineScript( ResourceLoader::makeLoaderConditionalScript( Xml::encodeJsCall( 'mw.loader.load', array( $modules ) ) ) ); } - if ( $wgResourceLoaderExperimentalAsyncLoading ) { - $scripts .= $this->getScriptsForBottomQueue( true ); + if ( $this->getConfig()->get( 'ResourceLoaderExperimentalAsyncLoading' ) ) { + $links[] = $this->getScriptsForBottomQueue( true ); } - return $scripts; + return self::getHtmlFromLoaderLinks( $links ); } /** - * JS stuff to put at the 'bottom', which can either be the bottom of the "<body>" - * or the bottom of the "<head>" depending on $wgResourceLoaderExperimentalAsyncLoading: - * modules marked with position 'bottom', legacy scripts ($this->mScripts), - * user preferences, site JS and user JS + * JS stuff to put at the 'bottom', which can either be the bottom of the + * "<body>" or the bottom of the "<head>" depending on + * $wgResourceLoaderExperimentalAsyncLoading: modules marked with position + * 'bottom', legacy scripts ($this->mScripts), user preferences, site JS + * and user JS. * - * @param $inHead boolean If true, this HTML goes into the "<head>", if false it goes into the "<body>" + * @param bool $inHead If true, this HTML goes into the "<head>", + * if false it goes into the "<body>". * @return string */ function getScriptsForBottomQueue( $inHead ) { - global $wgUseSiteJs, $wgAllowUserJs; - - // Script and Messages "only" requests marked for bottom inclusion + // Scripts and messages "only" requests marked for bottom inclusion // If we're in the <head>, use load() calls rather than <script src="..."> tags // Messages should go first - $scripts = $this->makeResourceLoaderLink( $this->getModuleMessages( true, 'bottom' ), + $links = array(); + $links[] = $this->makeResourceLoaderLink( $this->getModuleMessages( true, 'bottom' ), ResourceLoaderModule::TYPE_MESSAGES, /* $useESI = */ false, /* $extraQuery = */ array(), /* $loadCall = */ $inHead ); - $scripts .= $this->makeResourceLoaderLink( $this->getModuleScripts( true, 'bottom' ), + $links[] = $this->makeResourceLoaderLink( $this->getModuleScripts( true, 'bottom' ), ResourceLoaderModule::TYPE_SCRIPTS, /* $useESI = */ false, /* $extraQuery = */ array(), /* $loadCall = */ $inHead ); @@ -2869,7 +3049,7 @@ $templates // Only load modules that have marked themselves for loading at the bottom $modules = $this->getModules( true, 'bottom' ); if ( $modules ) { - $scripts .= Html::inlineScript( + $links[] = Html::inlineScript( ResourceLoader::makeLoaderConditionalScript( Xml::encodeJsCall( 'mw.loader.load', array( $modules, null, true ) ) ) @@ -2877,88 +3057,46 @@ $templates } // Legacy Scripts - $scripts .= "\n" . $this->mScripts; - - $defaultModules = array(); + $links[] = "\n" . $this->mScripts; // Add site JS if enabled - if ( $wgUseSiteJs ) { - $scripts .= $this->makeResourceLoaderLink( 'site', ResourceLoaderModule::TYPE_SCRIPTS, - /* $useESI = */ false, /* $extraQuery = */ array(), /* $loadCall = */ $inHead - ); - $defaultModules['site'] = 'loading'; - } else { - // Site module is empty, save request by marking ready in advance (bug 46857) - $defaultModules['site'] = 'ready'; - } + $links[] = $this->makeResourceLoaderLink( 'site', ResourceLoaderModule::TYPE_SCRIPTS, + /* $useESI = */ false, /* $extraQuery = */ array(), /* $loadCall = */ $inHead + ); // Add user JS if enabled - if ( $wgAllowUserJs ) { - if ( $this->getUser()->isLoggedIn() ) { - if ( $this->getTitle() && $this->getTitle()->isJsSubpage() && $this->userCanPreview() ) { - # XXX: additional security check/prompt? - // We're on a preview of a JS subpage - // Exclude this page from the user module in case it's in there (bug 26283) - $scripts .= $this->makeResourceLoaderLink( 'user', ResourceLoaderModule::TYPE_SCRIPTS, false, - array( 'excludepage' => $this->getTitle()->getPrefixedDBkey() ), $inHead - ); - // Load the previewed JS - $scripts .= Html::inlineScript( "\n" . $this->getRequest()->getText( 'wpTextbox1' ) . "\n" ) . "\n"; - // FIXME: If the user is previewing, say, ./vector.js, his ./common.js will be loaded - // asynchronously and may arrive *after* the inline script here. So the previewed code - // may execute before ./common.js runs. Normally, ./common.js runs before ./vector.js... - } else { - // Include the user module normally, i.e., raw to avoid it being wrapped in a closure. - $scripts .= $this->makeResourceLoaderLink( 'user', ResourceLoaderModule::TYPE_SCRIPTS, - /* $useESI = */ false, /* $extraQuery = */ array(), /* $loadCall = */ $inHead - ); - } - $defaultModules['user'] = 'loading'; - } else { - // Non-logged-in users have an empty user module. - // Save request by marking ready in advance (bug 46857) - $defaultModules['user'] = 'ready'; - } + if ( $this->getConfig()->get( 'AllowUserJs' ) + && $this->getUser()->isLoggedIn() + && $this->getTitle() + && $this->getTitle()->isJsSubpage() + && $this->userCanPreview() + ) { + # XXX: additional security check/prompt? + // We're on a preview of a JS subpage + // Exclude this page from the user module in case it's in there (bug 26283) + $links[] = $this->makeResourceLoaderLink( 'user', ResourceLoaderModule::TYPE_SCRIPTS, false, + array( 'excludepage' => $this->getTitle()->getPrefixedDBkey() ), $inHead + ); + // Load the previewed JS + $links[] = Html::inlineScript( "\n" + . $this->getRequest()->getText( 'wpTextbox1' ) . "\n" ) . "\n"; + + // FIXME: If the user is previewing, say, ./vector.js, his ./common.js will be loaded + // asynchronously and may arrive *after* the inline script here. So the previewed code + // may execute before ./common.js runs. Normally, ./common.js runs before ./vector.js... } else { - // User modules are disabled on this wiki. - // Save request by marking ready in advance (bug 46857) - $defaultModules['user'] = 'ready'; + // Include the user module normally, i.e., raw to avoid it being wrapped in a closure. + $links[] = $this->makeResourceLoaderLink( 'user', ResourceLoaderModule::TYPE_SCRIPTS, + /* $useESI = */ false, /* $extraQuery = */ array(), /* $loadCall = */ $inHead + ); } // Group JS is only enabled if site JS is enabled. - if ( $wgUseSiteJs ) { - if ( $this->getUser()->isLoggedIn() ) { - $scripts .= $this->makeResourceLoaderLink( 'user.groups', ResourceLoaderModule::TYPE_COMBINED, - /* $useESI = */ false, /* $extraQuery = */ array(), /* $loadCall = */ $inHead - ); - $defaultModules['user.groups'] = 'loading'; - } else { - // Non-logged-in users have no user.groups module. - // Save request by marking ready in advance (bug 46857) - $defaultModules['user.groups'] = 'ready'; - } - } else { - // Site (and group JS) disabled - $defaultModules['user.groups'] = 'ready'; - } + $links[] = $this->makeResourceLoaderLink( 'user.groups', ResourceLoaderModule::TYPE_COMBINED, + /* $useESI = */ false, /* $extraQuery = */ array(), /* $loadCall = */ $inHead + ); - $loaderInit = ''; - if ( $inHead ) { - // We generate loader calls anyway, so no need to fix the client-side loader's state to 'loading'. - foreach ( $defaultModules as $m => $state ) { - if ( $state == 'loading' ) { - unset( $defaultModules[$m] ); - } - } - } - if ( count( $defaultModules ) > 0 ) { - $loaderInit = Html::inlineScript( - ResourceLoader::makeLoaderConditionalScript( - ResourceLoader::makeLoaderStateScript( $defaultModules ) - ) - ) . "\n"; - } - return $loaderInit . $scripts; + return self::getHtmlFromLoaderLinks( $links ); } /** @@ -2966,15 +3104,13 @@ $templates * @return string */ function getBottomScripts() { - global $wgResourceLoaderExperimentalAsyncLoading; - // Optimise jQuery ready event cross-browser. // This also enforces $.isReady to be true at </body> which fixes the // mw.loader bug in Firefox with using document.write between </body> // and the DOMContentReady event (bug 47457). $html = Html::inlineScript( 'window.jQuery && jQuery.ready();' ); - if ( !$wgResourceLoaderExperimentalAsyncLoading ) { + if ( !$this->getConfig()->get( 'ResourceLoaderExperimentalAsyncLoading' ) ) { $html .= $this->getScriptsForBottomQueue( false ); } @@ -2982,10 +3118,20 @@ $templates } /** - * Add one or more variables to be set in mw.config in JavaScript. + * Get the javascript config vars to include on this page + * + * @return array Array of javascript config vars + * @since 1.23 + */ + public function getJsConfigVars() { + return $this->mJsConfigVars; + } + + /** + * Add one or more variables to be set in mw.config in JavaScript * - * @param $keys {String|Array} Key or array of key/value pairs. - * @param $value {Mixed} [optional] Value of the configuration variable. + * @param string|array $keys Key or array of key/value pairs + * @param mixed $value [optional] Value of the configuration variable */ public function addJsConfigVars( $keys, $value = null ) { if ( is_array( $keys ) ) { @@ -3001,16 +3147,13 @@ $templates /** * Get an array containing the variables to be set in mw.config in JavaScript. * - * DO NOT CALL THIS FROM OUTSIDE OF THIS CLASS OR Skin::makeGlobalVariablesScript(). - * This is only public until that function is removed. You have been warned. - * * Do not add things here which can be evaluated in ResourceLoaderStartUpModule * - in other words, page-independent/site-wide variables (without state). * You will only be adding bloat to the html page and causing page caches to * have to be purged on configuration changes. * @return array */ - public function getJSVars() { + private function getJSVars() { global $wgContLang; $curRevisionId = 0; @@ -3019,14 +3162,19 @@ $templates $title = $this->getTitle(); $ns = $title->getNamespace(); - $canonicalNamespace = MWNamespace::exists( $ns ) ? MWNamespace::getCanonicalName( $ns ) : $title->getNsText(); + $canonicalNamespace = MWNamespace::exists( $ns ) + ? MWNamespace::getCanonicalName( $ns ) + : $title->getNsText(); + $sk = $this->getSkin(); // Get the relevant title so that AJAX features can use the correct page name // when making API requests from certain special pages (bug 34972). - $relevantTitle = $this->getSkin()->getRelevantTitle(); + $relevantTitle = $sk->getRelevantTitle(); + $relevantUser = $sk->getRelevantUser(); if ( $ns == NS_SPECIAL ) { - list( $canonicalSpecialPageName, /*...*/ ) = SpecialPageFactory::resolveAlias( $title->getDBkey() ); + list( $canonicalSpecialPageName, /*...*/ ) = + SpecialPageFactory::resolveAlias( $title->getDBkey() ); } elseif ( $this->canUseWikiPage() ) { $wikiPage = $this->getWikiPage(); $curRevisionId = $wikiPage->getLatest(); @@ -3076,6 +3224,7 @@ $templates 'wgMonthNamesShort' => $lang->getMonthAbbreviationsArray(), 'wgRelevantPageName' => $relevantTitle->getPrefixedDBkey(), ); + if ( $user->isLoggedIn() ) { $vars['wgUserId'] = $user->getId(); $vars['wgUserEditCount'] = $user->getEditCount(); @@ -3086,21 +3235,30 @@ $templates // the client side. $vars['wgUserNewMsgRevisionId'] = $user->getNewMessageRevisionId(); } + if ( $wgContLang->hasVariants() ) { $vars['wgUserVariant'] = $wgContLang->getPreferredVariant(); } // Same test as SkinTemplate - $vars['wgIsProbablyEditable'] = $title->quickUserCan( 'edit', $user ) && ( $title->exists() || $title->quickUserCan( 'create', $user ) ); + $vars['wgIsProbablyEditable'] = $title->quickUserCan( 'edit', $user ) + && ( $title->exists() || $title->quickUserCan( 'create', $user ) ); + foreach ( $title->getRestrictionTypes() as $type ) { $vars['wgRestriction' . ucfirst( $type )] = $title->getRestrictions( $type ); } + if ( $title->isMainPage() ) { $vars['wgIsMainPage'] = true; } + if ( $this->mRedirectedFrom ) { $vars['wgRedirectedFrom'] = $this->mRedirectedFrom->getPrefixedDBkey(); } + if ( $relevantUser ) { + $vars['wgRelevantUserName'] = $relevantUser->getName(); + } + // Allow extensions to add their custom variables to the mw.config map. // Use the 'ResourceLoaderGetConfigVars' hook if the variable is not // page-dependant but site-wide (without state). @@ -3108,7 +3266,7 @@ $templates wfRunHooks( 'MakeGlobalVariablesScript', array( &$vars, $this ) ); // Merge in variables from addJsConfigVars last - return array_merge( $vars, $this->mJsConfigVars ); + return array_merge( $vars, $this->getJsConfigVars() ); } /** @@ -3136,16 +3294,13 @@ $templates } /** - * @return array in format "link name or number => 'link html'". + * @return array Array in format "link name or number => 'link html'". */ public function getHeadLinksArray() { - global $wgUniversalEditButton, $wgFavicon, $wgAppleTouchIcon, $wgEnableAPI, - $wgSitename, $wgVersion, - $wgFeed, $wgOverrideSiteFeed, $wgAdvertisedFeedTypes, - $wgDisableLangConversion, $wgCanonicalLanguageLinks, - $wgRightsPage, $wgRightsUrl; + global $wgVersion; $tags = array(); + $config = $this->getConfig(); $canonicalUrl = $this->mCanonicalUrl; @@ -3188,7 +3343,7 @@ $templates } # Universal edit button - if ( $wgUniversalEditButton && $this->isArticleRelated() ) { + if ( $config->get( 'UniversalEditButton' ) && $this->isArticleRelated() ) { $user = $this->getUser(); if ( $this->getTitle()->quickUserCan( 'edit', $user ) && ( $this->getTitle()->exists() || $this->getTitle()->quickUserCan( 'create', $user ) ) ) { @@ -3198,13 +3353,13 @@ $templates 'rel' => 'alternate', 'type' => 'application/x-wiki', 'title' => $msg, - 'href' => $this->getTitle()->getLocalURL( 'action=edit' ) + 'href' => $this->getTitle()->getEditURL(), ) ); // Alternate edit link $tags['alternative-edit'] = Html::element( 'link', array( 'rel' => 'edit', 'title' => $msg, - 'href' => $this->getTitle()->getLocalURL( 'action=edit' ) + 'href' => $this->getTitle()->getEditURL(), ) ); } } @@ -3213,12 +3368,18 @@ $templates # should not matter, but Konqueror (3.5.9 at least) incorrectly # uses whichever one appears later in the HTML source. Make sure # apple-touch-icon is specified first to avoid this. - if ( $wgAppleTouchIcon !== false ) { - $tags['apple-touch-icon'] = Html::element( 'link', array( 'rel' => 'apple-touch-icon', 'href' => $wgAppleTouchIcon ) ); + if ( $config->get( 'AppleTouchIcon' ) !== false ) { + $tags['apple-touch-icon'] = Html::element( 'link', array( + 'rel' => 'apple-touch-icon', + 'href' => $config->get( 'AppleTouchIcon' ) + ) ); } - if ( $wgFavicon !== false ) { - $tags['favicon'] = Html::element( 'link', array( 'rel' => 'shortcut icon', 'href' => $wgFavicon ) ); + if ( $config->get( 'Favicon' ) !== false ) { + $tags['favicon'] = Html::element( 'link', array( + 'rel' => 'shortcut icon', + 'href' => $config->get( 'Favicon' ) + ) ); } # OpenSearch description link @@ -3229,7 +3390,7 @@ $templates 'title' => $this->msg( 'opensearch-desc' )->inContentLanguage()->text(), ) ); - if ( $wgEnableAPI ) { + if ( $config->get( 'EnableAPI' ) ) { # Real Simple Discovery link, provides auto-discovery information # for the MediaWiki API (and potentially additional custom API # support such as WordPress or Twitter-compatible APIs for a @@ -3239,44 +3400,46 @@ $templates 'type' => 'application/rsd+xml', // Output a protocol-relative URL here if $wgServer is protocol-relative // Whether RSD accepts relative or protocol-relative URLs is completely undocumented, though - 'href' => wfExpandUrl( wfAppendQuery( wfScript( 'api' ), array( 'action' => 'rsd' ) ), PROTO_RELATIVE ), + 'href' => wfExpandUrl( wfAppendQuery( + wfScript( 'api' ), + array( 'action' => 'rsd' ) ), + PROTO_RELATIVE + ), ) ); } # Language variants - if ( !$wgDisableLangConversion && $wgCanonicalLanguageLinks ) { + if ( !$config->get( 'DisableLangConversion' ) ) { $lang = $this->getTitle()->getPageLanguage(); if ( $lang->hasVariants() ) { - - $urlvar = $lang->getURLVariant(); - - if ( !$urlvar ) { - $variants = $lang->getVariants(); - foreach ( $variants as $_v ) { - $tags["variant-$_v"] = Html::element( 'link', array( - 'rel' => 'alternate', - 'hreflang' => wfBCP47( $_v ), - 'href' => $this->getTitle()->getLocalURL( array( 'variant' => $_v ) ) ) - ); - } - } else { - $canonicalUrl = $this->getTitle()->getLocalURL(); + $variants = $lang->getVariants(); + foreach ( $variants as $_v ) { + $tags["variant-$_v"] = Html::element( 'link', array( + 'rel' => 'alternate', + 'hreflang' => wfBCP47( $_v ), + 'href' => $this->getTitle()->getLocalURL( array( 'variant' => $_v ) ) ) + ); } } + # x-default link per https://support.google.com/webmasters/answer/189077?hl=en + $tags["variant-x-default"] = Html::element( 'link', array( + 'rel' => 'alternate', + 'hreflang' => 'x-default', + 'href' => $this->getTitle()->getLocalURL() ) ); } # Copyright $copyright = ''; - if ( $wgRightsPage ) { - $copy = Title::newFromText( $wgRightsPage ); + if ( $config->get( 'RightsPage' ) ) { + $copy = Title::newFromText( $config->get( 'RightsPage' ) ); if ( $copy ) { $copyright = $copy->getLocalURL(); } } - if ( !$copyright && $wgRightsUrl ) { - $copyright = $wgRightsUrl; + if ( !$copyright && $config->get( 'RightsUrl' ) ) { + $copyright = $config->get( 'RightsUrl' ); } if ( $copyright ) { @@ -3287,7 +3450,7 @@ $templates } # Feeds - if ( $wgFeed ) { + if ( $config->get( 'Feed' ) ) { foreach ( $this->getSyndicationLinks() as $format => $link ) { # Use the page name for the title. In principle, this could # lead to issues with having the same name for different feeds @@ -3309,30 +3472,31 @@ $templates # like to promote instead of the RC feed (maybe like a "Recent New Articles" # or "Breaking news" one). For this, we see if $wgOverrideSiteFeed is defined. # If so, use it instead. - if ( $wgOverrideSiteFeed ) { - foreach ( $wgOverrideSiteFeed as $type => $feedUrl ) { + $sitename = $config->get( 'Sitename' ); + if ( $config->get( 'OverrideSiteFeed' ) ) { + foreach ( $config->get( 'OverrideSiteFeed' ) as $type => $feedUrl ) { // Note, this->feedLink escapes the url. $tags[] = $this->feedLink( $type, $feedUrl, - $this->msg( "site-{$type}-feed", $wgSitename )->text() + $this->msg( "site-{$type}-feed", $sitename )->text() ); } } elseif ( !$this->getTitle()->isSpecial( 'Recentchanges' ) ) { $rctitle = SpecialPage::getTitleFor( 'Recentchanges' ); - foreach ( $wgAdvertisedFeedTypes as $format ) { + foreach ( $config->get( 'AdvertisedFeedTypes' ) as $format ) { $tags[] = $this->feedLink( $format, $rctitle->getLocalURL( array( 'feed' => $format ) ), - $this->msg( "site-{$format}-feed", $wgSitename )->text() # For grep: 'site-rss-feed', 'site-atom-feed'. + # For grep: 'site-rss-feed', 'site-atom-feed' + $this->msg( "site-{$format}-feed", $sitename )->text() ); } } } # Canonical URL - global $wgEnableCanonicalServerLink; - if ( $wgEnableCanonicalServerLink ) { + if ( $config->get( 'EnableCanonicalServerLink' ) ) { if ( $canonicalUrl !== false ) { $canonicalUrl = wfExpandUrl( $canonicalUrl, PROTO_CANONICAL ); } else { @@ -3352,18 +3516,21 @@ $templates /** * @return string HTML tag links to be put in the header. + * @deprecated since 1.24 Use OutputPage::headElement or if you have to, + * OutputPage::getHeadLinksArray directly. */ public function getHeadLinks() { + wfDeprecated( __METHOD__, '1.24' ); return implode( "\n", $this->getHeadLinksArray() ); } /** * Generate a "<link rel/>" for a feed. * - * @param string $type feed type + * @param string $type Feed type * @param string $url URL to the feed - * @param string $text value of the "title" attribute - * @return String: HTML fragment + * @param string $text Value of the "title" attribute + * @return string HTML fragment */ private function feedLink( $type, $url, $text ) { return Html::element( 'link', array( @@ -3379,9 +3546,9 @@ $templates * Meant primarily for internal use... * * @param string $style URL to the file - * @param string $media to specify a media type, 'screen', 'printable', 'handheld' or any. - * @param string $condition for IE conditional comments, specifying an IE version - * @param string $dir set to 'rtl' or 'ltr' for direction-specific sheets + * @param string $media To specify a media type, 'screen', 'printable', 'handheld' or any. + * @param string $condition For IE conditional comments, specifying an IE version + * @param string $dir Set to 'rtl' or 'ltr' for direction-specific sheets */ public function addStyle( $style, $media = '', $condition = '', $dir = '' ) { $options = array(); @@ -3401,7 +3568,7 @@ $templates /** * Adds inline CSS styles - * @param $style_css Mixed: inline CSS + * @param mixed $style_css Inline CSS * @param string $flip Set to 'flip' to flip the CSS if needed */ public function addInlineStyle( $style_css, $flip = 'noflip' ) { @@ -3409,7 +3576,7 @@ $templates # If wanted, and the interface is right-to-left, flip the CSS $style_css = CSSJanus::transform( $style_css, true, false ); } - $this->mInlineStyles .= Html::inlineStyle( $style_css ); + $this->mInlineStyles .= Html::inlineStyle( $style_css ) . "\n"; } /** @@ -3419,54 +3586,54 @@ $templates * @return string */ public function buildCssLinks() { - global $wgUseSiteCss, $wgAllowUserCss, $wgAllowUserCssPrefs, $wgContLang; + global $wgContLang; $this->getSkin()->setupSkinUserCss( $this ); // Add ResourceLoader styles - // Split the styles into four groups - $styles = array( 'other' => array(), 'user' => array(), 'site' => array(), 'private' => array(), 'noscript' => array() ); + // Split the styles into these groups + $styles = array( + 'other' => array(), + 'user' => array(), + 'site' => array(), + 'private' => array(), + 'noscript' => array() + ); + $links = array(); $otherTags = ''; // Tags to append after the normal <link> tags $resourceLoader = $this->getResourceLoader(); $moduleStyles = $this->getModuleStyles(); // Per-site custom styles - if ( $wgUseSiteCss ) { - $moduleStyles[] = 'site'; - $moduleStyles[] = 'noscript'; - if ( $this->getUser()->isLoggedIn() ) { - $moduleStyles[] = 'user.groups'; - } - } + $moduleStyles[] = 'site'; + $moduleStyles[] = 'noscript'; + $moduleStyles[] = 'user.groups'; // Per-user custom styles - if ( $wgAllowUserCss ) { - if ( $this->getTitle()->isCssSubpage() && $this->userCanPreview() ) { - // We're on a preview of a CSS subpage - // Exclude this page from the user module in case it's in there (bug 26283) - $otherTags .= $this->makeResourceLoaderLink( 'user', ResourceLoaderModule::TYPE_STYLES, false, - array( 'excludepage' => $this->getTitle()->getPrefixedDBkey() ) - ); - - // Load the previewed CSS - // If needed, Janus it first. This is user-supplied CSS, so it's - // assumed to be right for the content language directionality. - $previewedCSS = $this->getRequest()->getText( 'wpTextbox1' ); - if ( $this->getLanguage()->getDir() !== $wgContLang->getDir() ) { - $previewedCSS = CSSJanus::transform( $previewedCSS, true, false ); - } - $otherTags .= Html::inlineStyle( $previewedCSS ); - } else { - // Load the user styles normally - $moduleStyles[] = 'user'; + if ( $this->getConfig()->get( 'AllowUserCss' ) && $this->getTitle()->isCssSubpage() && $this->userCanPreview() ) { + // We're on a preview of a CSS subpage + // Exclude this page from the user module in case it's in there (bug 26283) + $link = $this->makeResourceLoaderLink( 'user', ResourceLoaderModule::TYPE_STYLES, false, + array( 'excludepage' => $this->getTitle()->getPrefixedDBkey() ) + ); + $otherTags .= $link['html']; + + // Load the previewed CSS + // If needed, Janus it first. This is user-supplied CSS, so it's + // assumed to be right for the content language directionality. + $previewedCSS = $this->getRequest()->getText( 'wpTextbox1' ); + if ( $this->getLanguage()->getDir() !== $wgContLang->getDir() ) { + $previewedCSS = CSSJanus::transform( $previewedCSS, true, false ); } + $otherTags .= Html::inlineStyle( $previewedCSS ) . "\n"; + } else { + // Load the user styles normally + $moduleStyles[] = 'user'; } // Per-user preference styles - if ( $wgAllowUserCssPrefs ) { - $moduleStyles[] = 'user.cssprefs'; - } + $moduleStyles[] = 'user.cssprefs'; foreach ( $moduleStyles as $name ) { $module = $resourceLoader->getModule( $name ); @@ -3474,38 +3641,41 @@ $templates continue; } $group = $module->getGroup(); - // Modules in groups named "other" or anything different than "user", "site" or "private" + // Modules in groups different than the ones listed on top (see $styles assignment) // will be placed in the "other" group $styles[isset( $styles[$group] ) ? $group : 'other'][] = $name; } - // We want site, private and user styles to override dynamically added styles from modules, but we want - // dynamically added styles to override statically added styles from other modules. So the order - // has to be other, dynamic, site, private, user - // Add statically added styles for other modules - $ret = $this->makeResourceLoaderLink( $styles['other'], ResourceLoaderModule::TYPE_STYLES ); + // We want site, private and user styles to override dynamically added + // styles from modules, but we want dynamically added styles to override + // statically added styles from other modules. So the order has to be + // other, dynamic, site, private, user. Add statically added styles for + // other modules + $links[] = $this->makeResourceLoaderLink( $styles['other'], ResourceLoaderModule::TYPE_STYLES ); // Add normal styles added through addStyle()/addInlineStyle() here - $ret .= implode( "\n", $this->buildCssLinksArray() ) . $this->mInlineStyles; + $links[] = implode( "\n", $this->buildCssLinksArray() ) . $this->mInlineStyles; // Add marker tag to mark the place where the client-side loader should inject dynamic styles // We use a <meta> tag with a made-up name for this because that's valid HTML - $ret .= Html::element( 'meta', array( 'name' => 'ResourceLoaderDynamicStyles', 'content' => '' ) ) . "\n"; + $links[] = Html::element( + 'meta', + array( 'name' => 'ResourceLoaderDynamicStyles', 'content' => '' ) + ) . "\n"; // Add site, private and user styles // 'private' at present only contains user.options, so put that before 'user' // Any future private modules will likely have a similar user-specific character foreach ( array( 'site', 'noscript', 'private', 'user' ) as $group ) { - $ret .= $this->makeResourceLoaderLink( $styles[$group], - ResourceLoaderModule::TYPE_STYLES + $links[] = $this->makeResourceLoaderLink( $styles[$group], + ResourceLoaderModule::TYPE_STYLES ); } // Add stuff in $otherTags (previewed user CSS if applicable) - $ret .= $otherTags; - return $ret; + return self::getHtmlFromLoaderLinks( $links ) . $otherTags; } /** - * @return Array + * @return array */ public function buildCssLinksArray() { $links = array(); @@ -3529,11 +3699,10 @@ $templates * Generate \<link\> tags for stylesheets * * @param string $style URL to the file - * @param array $options option, can contain 'condition', 'dir', 'media' - * keys - * @return String: HTML fragment + * @param array $options Option, can contain 'condition', 'dir', 'media' keys + * @return string HTML fragment */ - protected function styleLink( $style, $options ) { + protected function styleLink( $style, array $options ) { if ( isset( $options['dir'] ) ) { if ( $this->getLanguage()->getDir() != $options['dir'] ) { return ''; @@ -3554,8 +3723,8 @@ $templates substr( $style, 0, 6 ) == 'https:' ) { $url = $style; } else { - global $wgStylePath, $wgStyleVersion; - $url = $wgStylePath . '/' . $style . '?' . $wgStyleVersion; + $config = $this->getConfig(); + $url = $config->get( 'StylePath' ) . '/' . $style . '?' . $config->get( 'StyleVersion' ); } $link = Html::linkedStyle( $url, $media ); @@ -3570,8 +3739,8 @@ $templates /** * Transform "media" attribute based on request parameters * - * @param string $media current value of the "media" attribute - * @return String: modified value of the "media" attribute, or null to skip + * @param string $media Current value of the "media" attribute + * @return string Modified value of the "media" attribute, or null to skip * this stylesheet */ public static function transformCssMedia( $media ) { @@ -3592,8 +3761,10 @@ $templates } elseif ( preg_match( $screenMediaQueryRegex, $media ) === 1 ) { // This regex will not attempt to understand a comma-separated media_query_list // - // Example supported values for $media: 'screen', 'only screen', 'screen and (min-width: 982px)' ), - // Example NOT supported value for $media: '3d-glasses, screen, print and resolution > 90dpi' + // Example supported values for $media: + // 'screen', 'only screen', 'screen and (min-width: 982px)' ), + // Example NOT supported value for $media: + // '3d-glasses, screen, print and resolution > 90dpi' // // If it's a print request, we never want any kind of screen stylesheets // If it's a handheld request (currently the only other choice with a switch), @@ -3627,8 +3798,8 @@ $templates * Like addWikiMsg() except the parameters are taken as an array * instead of a variable argument list. * - * @param $name string - * @param $args array + * @param string $name + * @param array $args */ public function addWikiMsgArray( $name, $args ) { $this->addHTML( $this->msg( $name, $args )->parseAsBlock() ); @@ -3651,11 +3822,12 @@ $templates * * Is equivalent to: * - * $wgOut->addWikiText( "<div class='error'>\n" . wfMessage( 'some-error' )->plain() . "\n</div>" ); + * $wgOut->addWikiText( "<div class='error'>\n" + * . wfMessage( 'some-error' )->plain() . "\n</div>" ); * * The newline after opening div is needed in some wikitext. See bug 19226. * - * @param $wrap string + * @param string $wrap */ public function wrapWikiMsg( $wrap /*, ...*/ ) { $msgSpecs = func_get_args(); @@ -3686,12 +3858,12 @@ $templates * Include jQuery core. Use this to avoid loading it multiple times * before we get a usable script loader. * - * @param array $modules list of jQuery modules which should be loaded - * @return Array: the list of modules which were not loaded. + * @param array $modules List of jQuery modules which should be loaded + * @return array The list of modules which were not loaded. * @since 1.16 * @deprecated since 1.17 */ - public function includeJQuery( $modules = array() ) { + public function includeJQuery( array $modules = array() ) { return array(); } @@ -3711,4 +3883,21 @@ $templates public function isTOCEnabled() { return $this->mEnableTOC; } + + /** + * Enables/disables section edit links, doesn't override __NOEDITSECTION__ + * @param bool $flag + * @since 1.23 + */ + public function enableSectionEditLinks( $flag = true ) { + $this->mEnableSectionEditLinks = $flag; + } + + /** + * @return bool + * @since 1.23 + */ + public function sectionEditLinksEnabled() { + return $this->mEnableSectionEditLinks; + } } |