diff options
Diffstat (limited to 'includes/OutputPage.php')
-rw-r--r-- | includes/OutputPage.php | 390 |
1 files changed, 244 insertions, 146 deletions
diff --git a/includes/OutputPage.php b/includes/OutputPage.php index b4a81bb1..1e0c396a 100644 --- a/includes/OutputPage.php +++ b/includes/OutputPage.php @@ -43,6 +43,7 @@ class OutputPage extends ContextSource { var $mKeywords = array(); var $mLinktags = array(); + var $mCanonicalUrl = false; /// Additional stylesheets. Looks like this is for extensions. Might be replaced by resource loader. var $mExtStyles = array(); @@ -122,7 +123,7 @@ class OutputPage extends ContextSource { var $mScripts = ''; /** - * Inline CSS styles. Use addInlineStyle() sparsingly + * Inline CSS styles. Use addInlineStyle() sparingly */ var $mInlineStyles = ''; @@ -248,6 +249,11 @@ class OutputPage extends ContextSource { private $mRedirectedFrom = null; /** + * Additional key => value data + */ + private $mProperties = array(); + + /** * 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. @@ -255,7 +261,7 @@ class OutputPage extends ContextSource { function __construct( IContextSource $context = null ) { if ( $context === null ) { # Extensions should use `new RequestContext` instead of `new OutputPage` now. - wfDeprecated( __METHOD__ ); + wfDeprecated( __METHOD__, '1.18' ); } else { $this->setContext( $context ); } @@ -264,8 +270,8 @@ class OutputPage extends ContextSource { /** * Redirect to $url rather than displaying the normal page * - * @param $url String: URL - * @param $responsecode String: HTTP status code + * @param string $url URL + * @param string $responsecode HTTP status code */ public function redirect( $url, $responsecode = '302' ) { # Strip newlines as a paranoia check for header injection in PHP<5.1.2 @@ -295,8 +301,8 @@ class OutputPage extends ContextSource { * Add a new "<meta>" tag * To add an http-equiv meta tag, precede the name with "http:" * - * @param $name String tag name - * @param $val String tag value + * @param string $name tag name + * @param string $val tag value */ function addMeta( $name, $val ) { array_push( $this->mMetatags, array( $name, $val ) ); @@ -305,7 +311,7 @@ class OutputPage extends ContextSource { /** * Add a keyword or a list of keywords in the page header * - * @param $text String or array of strings + * @param string $text or array of strings */ function addKeyword( $text ) { if( is_array( $text ) ) { @@ -316,9 +322,11 @@ class OutputPage extends ContextSource { } /** - * Add a new \<link\> tag to the page header + * Add a new \<link\> tag to the page header. + * + * Note: use setCanonicalUrl() for rel=canonical. * - * @param $linkarr Array: associative array of attributes. + * @param array $linkarr associative array of attributes. */ function addLink( $linkarr ) { array_push( $this->mLinktags, $linkarr ); @@ -327,7 +335,7 @@ class OutputPage extends ContextSource { /** * Add a new \<link\> with "rel" attribute set to "meta" * - * @param $linkarr Array: 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 */ @@ -337,6 +345,14 @@ 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. + */ + function setCanonicalUrl( $url ) { + $this->mCanonicalUrl = $url; + } + + /** * Get the value of the "rel" attribute for metadata links * * @return String @@ -355,7 +371,7 @@ class OutputPage extends ContextSource { /** * Add raw HTML to the list of scripts (including \<script\> tag, etc.) * - * @param $script String: raw HTML + * @param string $script raw HTML */ function addScript( $script ) { $this->mScripts .= $script . "\n"; @@ -364,7 +380,7 @@ class OutputPage extends ContextSource { /** * Register and add a stylesheet from an extension directory. * - * @param $url String 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. @@ -385,9 +401,9 @@ class OutputPage extends ContextSource { /** * Add a JavaScript file out of skins/common, or a given relative path. * - * @param $file String: 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 $version String: 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; @@ -405,7 +421,7 @@ class OutputPage extends ContextSource { /** * Add a self-contained script tag with the given contents * - * @param $script String: JavaScript text, no "<script>" tags + * @param string $script JavaScript text, no "<script>" tags */ public function addInlineScript( $script ) { $this->mScripts .= Html::inlineScript( "\n$script\n" ) . "\n"; @@ -424,14 +440,14 @@ class OutputPage extends ContextSource { * 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 $position String if not null, only return modules with this position + * @param string $position if not null, only return modules with this position * @param $type string * @return Array */ - protected function filterModules( $modules, $position = null, $type = ResourceLoaderModule::TYPE_COMBINED ){ + protected function filterModules( $modules, $position = null, $type = ResourceLoaderModule::TYPE_COMBINED ) { $resourceLoader = $this->getResourceLoader(); $filteredModules = array(); - foreach( $modules as $val ){ + foreach( $modules as $val ) { $module = $resourceLoader->getModule( $val ); if( $module instanceof ResourceLoaderModule && $module->getOrigin() <= $this->getAllowedModules( $type ) @@ -446,8 +462,8 @@ class OutputPage extends ContextSource { /** * Get the list of modules to include on this page * - * @param $filter Bool whether to filter out insufficiently trustworthy modules - * @param $position String if not null, only return modules with this position + * @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 */ @@ -501,13 +517,15 @@ class OutputPage extends ContextSource { * @return Array of module names */ public function getModuleStyles( $filter = false, $position = null ) { - return $this->getModules( $filter, $position, 'mModuleStyles' ); + return $this->getModules( $filter, $position, 'mModuleStyles' ); } /** - * Add only CSS of one or more modules recognized by the resource loader. Module - * styles added through this function will be loaded by the resource loader when - * the page loads. + * Add only CSS of one or more modules recognized by the resource loader. + * + * Module styles added through this function will be added using standard link CSS + * 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 */ @@ -563,8 +581,8 @@ class OutputPage extends ContextSource { /** * Add or replace an header item to the output * - * @param $name String: item name - * @param $value String: raw HTML + * @param string $name item name + * @param string $value raw HTML */ public function addHeadItem( $name, $value ) { $this->mHeadItems[$name] = $value; @@ -573,7 +591,7 @@ class OutputPage extends ContextSource { /** * Check if the header item $name is already set * - * @param $name String: item name + * @param string $name item name * @return Boolean */ public function hasHeadItem( $name ) { @@ -583,7 +601,7 @@ class OutputPage extends ContextSource { /** * Set the value of the ETag HTTP header, only used if $wgUseETag is true * - * @param $tag String: value of "ETag" header + * @param string $tag value of "ETag" header */ function setETag( $tag ) { $this->mETag = $tag; @@ -610,8 +628,34 @@ class OutputPage extends ContextSource { } /** + * Set an additional output property + * @since 1.21 + * + * @param string $name + * @param mixed $value + */ + public function setProperty( $name, $value ) { + $this->mProperties[$name] = $value; + } + + /** + * Get an additional output property + * @since 1.21 + * + * @param $name + * @return mixed: Property value or null if not found + */ + public function getProperty( $name ) { + if ( isset( $this->mProperties[$name] ) ) { + return $this->mProperties[$name]; + } else { + return null; + } + } + + /** * checkLastModified tells the client to use the client-cached page if - * possible. If sucessful, the OutputPage is disabled so that + * possible. If successful, the OutputPage is disabled so that * any future call to OutputPage->output() have no effect. * * Side effect: sets mLastModified for Last-Modified header @@ -704,7 +748,7 @@ class OutputPage extends ContextSource { /** * Override the last modified timestamp * - * @param $timestamp String: new timestamp, in a format readable by + * @param string $timestamp new timestamp, in a format readable by * wfTimestamp() */ public function setLastModified( $timestamp ) { @@ -714,7 +758,7 @@ class OutputPage extends ContextSource { /** * Set the robot policy for the page: <http://www.robotstxt.org/meta.html> * - * @param $policy String: 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 @@ -734,7 +778,7 @@ class OutputPage extends ContextSource { * Set the index policy for the page, but leave the follow policy un- * touched. * - * @param $policy string Either 'index' or 'noindex'. + * @param string $policy Either 'index' or 'noindex'. * @return null */ public function setIndexPolicy( $policy ) { @@ -748,7 +792,7 @@ class OutputPage extends ContextSource { * Set the follow policy for the page, but leave the index policy un- * touched. * - * @param $policy String: either 'follow' or 'nofollow'. + * @param string $policy either 'follow' or 'nofollow'. * @return null */ public function setFollowPolicy( $policy ) { @@ -762,7 +806,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 $text String: new value of the "action text" + * @param string $text new value of the "action text" */ public function setPageTitleActionText( $text ) { $this->mPageTitleActionText = $text; @@ -777,6 +821,7 @@ class OutputPage extends ContextSource { if ( isset( $this->mPageTitleActionText ) ) { return $this->mPageTitleActionText; } + return ''; } /** @@ -851,11 +896,10 @@ class OutputPage extends ContextSource { $this->getContext()->setTitle( $t ); } - /** - * Replace the subtile with $str + * Replace the subtitle with $str * - * @param $str String|Message: new value of the subtitle + * @param string|Message $str new value of the subtitle. String should be safe HTML. */ public function setSubtitle( $str ) { $this->clearSubtitle(); @@ -866,7 +910,7 @@ class OutputPage extends ContextSource { * Add $str to the subtitle * * @deprecated in 1.19; use addSubtitle() instead - * @param $str String|Message to add to the subtitle + * @param string|Message $str to add to the subtitle */ public function appendSubtitle( $str ) { $this->addSubtitle( $str ); @@ -875,7 +919,7 @@ class OutputPage extends ContextSource { /** * Add $str to the subtitle * - * @param $str String|Message to add to the subtitle + * @param string|Message $str to add to the subtitle. String should be safe HTML. */ public function addSubtitle( $str ) { if ( $str instanceof Message ) { @@ -987,7 +1031,7 @@ class OutputPage extends ContextSource { * for the new version * @see addFeedLink() * - * @param $val String: 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 ) { @@ -1007,8 +1051,8 @@ class OutputPage extends ContextSource { /** * Add a feed link to the page header * - * @param $format String: feed type, should be a key of $wgFeedClasses - * @param $href String: URL + * @param string $format feed type, should be a key of $wgFeedClasses + * @param string $href URL */ public function addFeedLink( $format, $href ) { global $wgAdvertisedFeedTypes; @@ -1092,7 +1136,7 @@ class OutputPage extends ContextSource { /** * Add new language links * - * @param $newLinkArray array Associative array mapping language code to the page + * @param array $newLinkArray Associative array mapping language code to the page * name */ public function addLanguageLinks( $newLinkArray ) { @@ -1102,7 +1146,7 @@ class OutputPage extends ContextSource { /** * Reset the language links and add new language links * - * @param $newLinkArray array Associative array mapping language code to the page + * @param array $newLinkArray Associative array mapping language code to the page * name */ public function setLanguageLinks( $newLinkArray ) { @@ -1121,7 +1165,7 @@ class OutputPage extends ContextSource { /** * Add an array of categories, with names in the keys * - * @param $categories Array mapping category name => sort key + * @param array $categories mapping category name => sort key */ public function addCategoryLinks( $categories ) { global $wgContLang; @@ -1182,7 +1226,7 @@ class OutputPage extends ContextSource { /** * Reset the category links (but not the category list) and add $categories * - * @param $categories Array mapping category name => sort key + * @param array $categories mapping category name => sort key */ public function setCategoryLinks( $categories ) { $this->mCategoryLinks = array(); @@ -1236,11 +1280,11 @@ class OutputPage extends ContextSource { /** * Show what level of JavaScript / CSS untrustworthiness is allowed on this page * @see ResourceLoaderModule::$origin - * @param $type String ResourceLoaderModule TYPE_ constant + * @param string $type ResourceLoaderModule TYPE_ constant * @return Int ResourceLoaderModule ORIGIN_ class constant */ - public function getAllowedModules( $type ){ - if( $type == ResourceLoaderModule::TYPE_COMBINED ){ + public function getAllowedModules( $type ) { + if( $type == ResourceLoaderModule::TYPE_COMBINED ) { return min( array_values( $this->mAllowedModules ) ); } else { return isset( $this->mAllowedModules[$type] ) @@ -1254,23 +1298,23 @@ class OutputPage extends ContextSource { * @param $type String ResourceLoaderModule TYPE_ constant * @param $level Int ResourceLoaderModule class constant */ - public function setAllowedModules( $type, $level ){ + public function setAllowedModules( $type, $level ) { $this->mAllowedModules[$type] = $level; } /** - * As for setAllowedModules(), but don't inadvertantly make the page more accessible + * As for setAllowedModules(), but don't inadvertently make the page more accessible * @param $type String * @param $level Int ResourceLoaderModule class constant */ - public function reduceAllowedModules( $type, $level ){ - $this->mAllowedModules[$type] = min( $this->getAllowedModules($type), $level ); + public function reduceAllowedModules( $type, $level ) { + $this->mAllowedModules[$type] = min( $this->getAllowedModules( $type ), $level ); } /** * Prepend $text to the body HTML * - * @param $text String: HTML + * @param string $text HTML */ public function prependHTML( $text ) { $this->mBodytext = $text . $this->mBodytext; @@ -1279,7 +1323,7 @@ class OutputPage extends ContextSource { /** * Append $text to the body HTML * - * @param $text String: HTML + * @param string $text HTML */ public function addHTML( $text ) { $this->mBodytext .= $text; @@ -1423,14 +1467,17 @@ class OutputPage extends ContextSource { * @param $interface Boolean: is this text in the user interface language? */ public function addWikiText( $text, $linestart = true, $interface = true ) { - $title = $this->getTitle(); // Work arround E_STRICT + $title = $this->getTitle(); // Work around E_STRICT + if ( !$title ) { + throw new MWException( 'Title is null' ); + } $this->addWikiTextTitle( $text, $title, $linestart, /*tidy*/false, $interface ); } /** * Add wikitext with a custom Title object * - * @param $text String: wikitext + * @param string $text wikitext * @param $title Title object * @param $linestart Boolean: is this the start of a line? */ @@ -1441,7 +1488,7 @@ class OutputPage extends ContextSource { /** * Add wikitext with a custom Title object and tidy enabled. * - * @param $text String: wikitext + * @param string $text wikitext * @param $title Title object * @param $linestart Boolean: is this the start of a line? */ @@ -1452,7 +1499,7 @@ class OutputPage extends ContextSource { /** * Add wikitext with tidy enabled * - * @param $text String: wikitext + * @param string $text wikitext * @param $linestart Boolean: is this the start of a line? */ public function addWikiTextTidy( $text, $linestart = true ) { @@ -1463,14 +1510,14 @@ class OutputPage extends ContextSource { /** * Add wikitext with a custom Title object * - * @param $text String: wikitext + * @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) */ - public function addWikiTextTitle( $text, &$title, $linestart, $tidy = false, $interface = false ) { + public function addWikiTextTitle( $text, Title $title, $linestart, $tidy = false, $interface = false ) { global $wgParser; wfProfileIn( __METHOD__ ); @@ -1550,7 +1597,6 @@ class OutputPage extends ContextSource { $this->addHTML( $text ); } - /** * Add the output of a QuickTemplate to the output buffer * @@ -1571,9 +1617,10 @@ class OutputPage extends ContextSource { * @param $interface Boolean: use interface language ($wgLang instead of * $wgContLang) while parsing language sensitive magic * words like GRAMMAR and PLURAL. This also disables - * LanguageConverter. + * LanguageConverter. * @param $language Language object: target language object, will override * $interface + * @throws MWException * @return String: HTML */ public function parse( $text, $linestart = true, $interface = false, $language = null ) { @@ -1695,7 +1742,7 @@ class OutputPage extends ContextSource { /** * Add an HTTP header that will influence on the cache * - * @param $header String: header name + * @param string $header header name * @param $option Array|null * @todo FIXME: Document the $option parameter; it appears to be for * X-Vary-Options but what format is acceptable? @@ -1769,14 +1816,12 @@ class OutputPage extends ContextSource { } else { $aloption[] = 'string-contains=' . $variant; - // IE and some other browsers use another form of language code - // in their Accept-Language header, like "zh-CN" or "zh-TW". + // IE and some other browsers use BCP 47 standards in + // their Accept-Language header, like "zh-CN" or "zh-Hant". // We should handle these too. - $ievariant = explode( '-', $variant ); - if ( count( $ievariant ) == 2 ) { - $ievariant[1] = strtoupper( $ievariant[1] ); - $ievariant = implode( '-', $ievariant ); - $aloption[] = 'string-contains=' . $ievariant; + $variantBCP47 = wfBCP47( $variant ); + if ( $variantBCP47 !== $variant ) { + $aloption[] = 'string-contains=' . $variantBCP47; } } } @@ -1860,7 +1905,7 @@ class OutputPage extends ContextSource { wfDebug( __METHOD__ . ": proxy caching with ESI; {$this->mLastModified} **\n", false ); # 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=' . $wgSquidMaxage . '+' . $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 @@ -1870,7 +1915,7 @@ class OutputPage extends ContextSource { wfDebug( __METHOD__ . ": local proxy caching; {$this->mLastModified} **\n", false ); # 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 @@ -1879,7 +1924,7 @@ class OutputPage extends ContextSource { $response->header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', 0 ) . ' GMT' ); $response->header( "Cache-Control: private, must-revalidate, max-age=0" ); } - if($this->mLastModified) { + if( $this->mLastModified ) { $response->header( "Last-Modified: {$this->mLastModified}" ); } } else { @@ -1894,7 +1939,7 @@ class OutputPage extends ContextSource { } /** - * Get the message associed with the HTTP response code $code + * 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 @@ -1903,7 +1948,7 @@ class OutputPage extends ContextSource { * @deprecated since 1.18 Use HttpStatus::getMessage() instead. */ public static function getStatusMessage( $code ) { - wfDeprecated( __METHOD__ ); + wfDeprecated( __METHOD__, '1.18' ); return HttpStatus::getMessage( $code ); } @@ -1994,14 +2039,16 @@ class OutputPage extends ContextSource { wfRunHooks( 'AfterFinalPageOutput', array( $this ) ); $this->sendCacheControl(); + ob_end_flush(); + wfProfileOut( __METHOD__ ); } /** * Actually output something with print(). * - * @param $ins String: the string to output + * @param string $ins the string to output */ public function out( $ins ) { print $ins; @@ -2020,8 +2067,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 $pageTitle String|Message will be passed directly to setPageTitle() - * @param $htmlTitle String|Message 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 */ @@ -2047,7 +2094,7 @@ class OutputPage extends ContextSource { * * @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 $params Array: message parameters; ignored if $msg is 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 ) { @@ -2056,8 +2103,8 @@ class OutputPage extends ContextSource { $this->prepareErrorPage( $title ); - if ( $msg instanceof Message ){ - $this->addHTML( $msg->parse() ); + if ( $msg instanceof Message ) { + $this->addHTML( $msg->parseAsBlock() ); } else { $this->addWikiMsgArray( $msg, $params ); } @@ -2068,12 +2115,10 @@ class OutputPage extends ContextSource { /** * Output a standard permission error page * - * @param $errors Array: error message keys - * @param $action String: 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 ) { - global $wgGroupPermissions; - // 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 @@ -2082,8 +2127,8 @@ class OutputPage extends ContextSource { if ( in_array( $action, array( 'read', 'edit', 'createpage', 'createtalk', 'upload' ) ) && $this->getUser()->isAnon() && count( $errors ) == 1 && isset( $errors[0][0] ) && ( $errors[0][0] == 'badaccess-groups' || $errors[0][0] == 'badaccess-group0' ) - && ( ( isset( $wgGroupPermissions['user'][$action] ) && $wgGroupPermissions['user'][$action] ) - || ( isset( $wgGroupPermissions['autoconfirmed'][$action] ) && $wgGroupPermissions['autoconfirmed'][$action] ) ) + && ( User::groupHasPermission( 'user', $action ) + || User::groupHasPermission( 'autoconfirmed', $action ) ) ) { $displayReturnto = null; @@ -2115,7 +2160,7 @@ class OutputPage extends ContextSource { unset( $returntoquery['title'] ); unset( $returntoquery['returnto'] ); unset( $returntoquery['returntoquery'] ); - $query['returntoquery'] = wfArrayToCGI( $returntoquery ); + $query['returntoquery'] = wfArrayToCgi( $returntoquery ); } } $loginLink = Linker::linkKnown( @@ -2155,7 +2200,8 @@ 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 $permission String: key required + * @param string $permission key required + * @throws PermissionsError */ public function permissionRequired( $permission ) { throw new PermissionsError( $permission ); @@ -2173,8 +2219,8 @@ class OutputPage extends ContextSource { /** * Format a list of error messages * - * @param $errors Array of arrays returned by Title::getUserPermissionsErrors - * @param $action String: action that was denied or null if unknown + * @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. */ public function formatPermissionsErrorMessage( $errors, $action = null ) { @@ -2226,6 +2272,7 @@ class OutputPage extends ContextSource { * @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 + * @throws ReadOnlyError */ public function readOnlyPage( $source = null, $protected = false, $reasons = array(), $action = null ) { $this->setRobotPolicy( 'noindex,nofollow' ); @@ -2257,7 +2304,7 @@ class OutputPage extends ContextSource { $pageLang = $this->getTitle()->getPageLanguage(); $params = array( - 'id' => 'wpTextbox1', + 'id' => 'wpTextbox1', 'name' => 'wpTextbox1', 'cols' => $this->getUser()->getOption( 'cols' ), 'rows' => $this->getUser()->getOption( 'rows' ), @@ -2284,7 +2331,7 @@ $templates } /** - * Turn off regular page output and return an error reponse + * Turn off regular page output and return an error response * for when rate limiting has triggered. */ public function rateLimited() { @@ -2341,13 +2388,22 @@ $templates * Add a "return to" link pointing to a specified title * * @param $title Title to link - * @param $query Array query string parameters - * @param $text String text of the link (input is not escaped) - */ - public function addReturnTo( $title, $query = array(), $text = null ) { - $this->addLink( array( 'rel' => 'next', 'href' => $title->getFullURL() ) ); + * @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 + */ + public function addReturnTo( $title, $query = array(), $text = null, $options = array() ) { + if( in_array( 'http', $options ) ) { + $proto = PROTO_HTTP; + } elseif( in_array( 'https', $options ) ) { + $proto = PROTO_HTTPS; + } else { + $proto = PROTO_RELATIVE; + } + + $this->addLink( array( 'rel' => 'next', 'href' => $title->getFullURL( '', false, $proto ) ) ); $link = $this->msg( 'returnto' )->rawParams( - Linker::link( $title, $text, array(), $query ) )->escaped(); + Linker::link( $title, $text, array(), $query, $options ) )->escaped(); $this->addHTML( "<p id=\"mw-returnto\">{$link}</p>\n" ); } @@ -2357,7 +2413,7 @@ $templates * * @param $unused * @param $returnto Title or String to return to - * @param $returntoquery String: query string for the return to link + * @param string $returntoquery query string for the return to link */ public function returnToMain( $unused = null, $returnto = null, $returntoquery = null ) { if ( $returnto == null ) { @@ -2451,7 +2507,7 @@ $templates */ private function addDefaultModules() { global $wgIncludeLegacyJavaScript, $wgPreloadJavaScriptMwUtil, $wgUseAjax, - $wgAjaxWatch; + $wgAjaxWatch, $wgResponsiveImages; // Add base resources $this->addModules( array( @@ -2459,7 +2515,7 @@ $templates 'mediawiki.page.startup', 'mediawiki.page.ready', ) ); - if ( $wgIncludeLegacyJavaScript ){ + if ( $wgIncludeLegacyJavaScript ) { $this->addModules( 'mediawiki.legacy.wikibits' ); } @@ -2492,6 +2548,11 @@ $templates if ( $this->isArticle() && $this->getUser()->getOption( 'editondblclick' ) ) { $this->addModules( 'mediawiki.action.view.dblClickEdit' ); } + + // Support for high-density display images + if ( $wgResponsiveImages ) { + $this->addModules( 'mediawiki.hidpi' ); + } } /** @@ -2509,9 +2570,9 @@ $templates /** * TODO: Document * @param $modules Array/string with the module name(s) - * @param $only String ResourceLoaderModule TYPE_ class constant + * @param string $only ResourceLoaderModule TYPE_ class constant * @param $useESI boolean - * @param $extraQuery Array with extra query parameters to add to each request. array( param => value ) + * @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 */ @@ -2634,7 +2695,7 @@ $templates } // 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-changable modules so we can ensure cache misses on change + // 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; @@ -2682,7 +2743,7 @@ $templates } } - if( $group == 'noscript' ){ + if( $group == 'noscript' ) { $links .= Html::rawElement( 'noscript', array(), $link ) . "\n"; } else { $links .= $link . "\n"; @@ -2890,21 +2951,20 @@ $templates $this->mJsConfigVars[$keys] = $value; } - /** * 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 ResourceLoaderStartupScript + * 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() { - global $wgUseAjax, $wgContLang; + global $wgContLang; $latestRevID = 0; $pageID = 0; @@ -2942,18 +3002,20 @@ $templates implode( "\t", $digitTransTable ), ); + $user = $this->getUser(); + $vars = array( 'wgCanonicalNamespace' => $nsname, 'wgCanonicalSpecialPageName' => $canonicalName, 'wgNamespaceNumber' => $title->getNamespace(), - 'wgPageName' => $title->getPrefixedDBKey(), + 'wgPageName' => $title->getPrefixedDBkey(), 'wgTitle' => $title->getText(), 'wgCurRevisionId' => $latestRevID, 'wgArticleId' => $pageID, 'wgIsArticle' => $this->isArticle(), 'wgAction' => Action::getActionName( $this->getContext() ), - 'wgUserName' => $this->getUser()->isAnon() ? null : $this->getUser()->getName(), - 'wgUserGroups' => $this->getUser()->getEffectiveGroups(), + 'wgUserName' => $user->isAnon() ? null : $user->getName(), + 'wgUserGroups' => $user->getEffectiveGroups(), 'wgCategories' => $this->getCategories(), 'wgBreakFrames' => $this->getFrameOptions() == 'DENY', 'wgPageContentLanguage' => $lang->getCode(), @@ -2962,11 +3024,17 @@ $templates 'wgDefaultDateFormat' => $lang->getDefaultDateFormat(), 'wgMonthNames' => $lang->getMonthNamesArray(), 'wgMonthNamesShort' => $lang->getMonthAbbreviationsArray(), - 'wgRelevantPageName' => $relevantTitle->getPrefixedDBKey(), + 'wgRelevantPageName' => $relevantTitle->getPrefixedDBkey(), ); + if ( $user->isLoggedIn() ) { + $vars['wgUserId'] = $user->getId(); + $vars['wgUserEditCount'] = $user->getEditCount(); + $userReg = wfTimestampOrNull( TS_UNIX, $user->getRegistration() ); + $vars['wgUserRegistration'] = $userReg !== null ? ( $userReg * 1000 ) : null; + } if ( $wgContLang->hasVariants() ) { $vars['wgUserVariant'] = $wgContLang->getPreferredVariant(); - } + } foreach ( $title->getRestrictionTypes() as $type ) { $vars['wgRestriction' . ucfirst( $type )] = $title->getRestrictions( $type ); } @@ -2974,7 +3042,7 @@ $templates $vars['wgIsMainPage'] = true; } if ( $this->mRedirectedFrom ) { - $vars['wgRedirectedFrom'] = $this->mRedirectedFrom->getPrefixedDBKey(); + $vars['wgRedirectedFrom'] = $this->mRedirectedFrom->getPrefixedDBkey(); } // Allow extensions to add their custom variables to the mw.config map. @@ -3012,7 +3080,7 @@ $templates } /** - * @param $addContentType bool: Whether "<meta>" specifying content type should be returned + * @param bool $addContentType Whether "<meta>" specifying content type should be returned * * @return array in format "link name or number => 'link html'". */ @@ -3025,6 +3093,8 @@ $templates $tags = array(); + $canonicalUrl = $this->mCanonicalUrl; + if ( $addContentType ) { if ( $wgHtml5 ) { # More succinct than <meta http-equiv=Content-Type>, has the @@ -3035,7 +3105,7 @@ $templates 'http-equiv' => 'Content-Type', 'content' => "$wgMimeType; charset=UTF-8" ) ); - $tags['meta-content-style-type'] = Html::element( 'meta', array( // bug 15835 + $tags['meta-content-style-type'] = Html::element( 'meta', array( // bug 15835 'http-equiv' => 'Content-Style-Type', 'content' => 'text/css' ) ); @@ -3064,7 +3134,7 @@ $templates ); $tags['meta-keywords'] = Html::element( 'meta', array( 'name' => 'keywords', - 'content' => preg_replace( + 'content' => preg_replace( array_keys( $strip ), array_values( $strip ), implode( ',', $this->mKeywords ) @@ -3151,7 +3221,6 @@ $templates ) ); } - # Language variants if ( !$wgDisableLangConversion && $wgCanonicalLanguageLinks ) { $lang = $this->getTitle()->getPageLanguage(); @@ -3169,10 +3238,7 @@ $templates ); } } else { - $tags['canonical'] = Html::element( 'link', array( - 'rel' => 'canonical', - 'href' => $this->getTitle()->getCanonicalUrl() - ) ); + $canonicalUrl = $this->getTitle()->getLocalURL(); } } } @@ -3241,12 +3307,30 @@ $templates } } } + + # Canonical URL + global $wgEnableCanonicalServerLink; + if ( $wgEnableCanonicalServerLink ) { + if ( $canonicalUrl !== false ) { + $canonicalUrl = wfExpandUrl( $canonicalUrl, PROTO_CANONICAL ); + } else { + $reqUrl = $this->getRequest()->getRequestURL(); + $canonicalUrl = wfExpandUrl( $reqUrl, PROTO_CANONICAL ); + } + } + if ( $canonicalUrl !== false ) { + $tags[] = Html::element( 'link', array( + 'rel' => 'canonical', + 'href' => $canonicalUrl + ) ); + } + return $tags; } /** * @param $unused - * @param $addContentType bool: Whether "<meta>" specifying content type should be returned + * @param bool $addContentType Whether "<meta>" specifying content type should be returned * * @return string HTML tag links to be put in the header. */ @@ -3257,9 +3341,9 @@ $templates /** * Generate a "<link rel/>" for a feed. * - * @param $type String: feed type - * @param $url String: URL to the feed - * @param $text String: value of the "title" attribute + * @param string $type feed type + * @param string $url URL to the feed + * @param string $text value of the "title" attribute * @return String: HTML fragment */ private function feedLink( $type, $url, $text ) { @@ -3275,10 +3359,10 @@ $templates * Add a local or specified stylesheet, with the given media options. * Meant primarily for internal use... * - * @param $style String: URL to the file - * @param $media String: to specify a media type, 'screen', 'printable', 'handheld' or any. - * @param $condition String: for IE conditional comments, specifying an IE version - * @param $dir String: set to 'rtl' or 'ltr' for direction-specific sheets + * @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 */ public function addStyle( $style, $media = '', $condition = '', $dir = '' ) { $options = array(); @@ -3299,7 +3383,7 @@ $templates /** * Adds inline CSS styles * @param $style_css Mixed: inline CSS - * @param $flip String: Set to 'flip' to flip the CSS if needed + * @param string $flip Set to 'flip' to flip the CSS if needed */ public function addInlineStyle( $style_css, $flip = 'noflip' ) { if( $flip === 'flip' && $this->getLanguage()->isRTL() ) { @@ -3316,8 +3400,7 @@ $templates * @return string */ public function buildCssLinks() { - global $wgUseSiteCss, $wgAllowUserCss, $wgAllowUserCssPrefs, - $wgLang, $wgContLang; + global $wgUseSiteCss, $wgAllowUserCss, $wgAllowUserCssPrefs, $wgContLang; $this->getSkin()->setupSkinUserCss( $this ); @@ -3333,7 +3416,7 @@ $templates if ( $wgUseSiteCss ) { $moduleStyles[] = 'site'; $moduleStyles[] = 'noscript'; - if( $this->getUser()->isLoggedIn() ){ + if( $this->getUser()->isLoggedIn() ) { $moduleStyles[] = 'user.groups'; } } @@ -3351,7 +3434,7 @@ $templates // 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 ( $wgLang->getDir() !== $wgContLang->getDir() ) { + if ( $this->getLanguage()->getDir() !== $wgContLang->getDir() ) { $previewedCSS = CSSJanus::transform( $previewedCSS, true, false ); } $otherTags .= Html::inlineStyle( $previewedCSS ); @@ -3426,8 +3509,8 @@ $templates /** * Generate \<link\> tags for stylesheets * - * @param $style String: URL to the file - * @param $options Array: option, can contain 'condition', 'dir', 'media' + * @param string $style URL to the file + * @param array $options option, can contain 'condition', 'dir', 'media' * keys * @return String: HTML fragment */ @@ -3468,12 +3551,16 @@ $templates /** * Transform "media" attribute based on request parameters * - * @param $media String: current value of the "media" attribute - * @return String: modified value of the "media" attribute + * @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 ) { global $wgRequest, $wgHandheldForIPhone; + // http://www.w3.org/TR/css3-mediaqueries/#syntax + $screenMediaQueryRegex = '/^(?:only\s+)?screen\b/i'; + // Switch in on-screen display for media testing $switches = array( 'printable' => 'print', @@ -3483,8 +3570,20 @@ $templates if( $wgRequest->getBool( $switch ) ) { if( $media == $targetMedia ) { $media = ''; - } elseif( $media == 'screen' ) { - return null; + } 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' + // + // 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), + // we don't want simple 'screen' but we might want screen queries that + // have a max-width or something, so we'll pass all others on and let the + // client do the query. + if( $targetMedia == 'print' || $media == 'screen' ) { + return null; + } } } } @@ -3557,7 +3656,6 @@ $templates $msgSpecs = array_values( $msgSpecs ); $s = $wrap; foreach ( $msgSpecs as $n => $spec ) { - $options = array(); if ( is_array( $spec ) ) { $args = $spec; $name = array_shift( $args ); @@ -3568,7 +3666,7 @@ $templates '1.20' ); } - } else { + } else { $args = array(); $name = $spec; } @@ -3581,7 +3679,7 @@ $templates * Include jQuery core. Use this to avoid loading it multiple times * before we get a usable script loader. * - * @param $modules Array: list of jQuery modules which should be 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 |