2004-02-18 02:15:00 +00:00
|
|
|
<?php
|
2012-05-11 08:34:29 +00:00
|
|
|
/**
|
|
|
|
|
* Preparation for the final page rendering.
|
|
|
|
|
*
|
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
|
* (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
|
* http://www.gnu.org/copyleft/gpl.html
|
|
|
|
|
*
|
|
|
|
|
* @file
|
|
|
|
|
*/
|
|
|
|
|
|
2017-02-28 20:52:17 +00:00
|
|
|
use MediaWiki\Linker\LinkTarget;
|
2016-11-30 21:28:55 +00:00
|
|
|
use MediaWiki\MediaWikiServices;
|
2016-02-01 20:44:03 +00:00
|
|
|
use MediaWiki\Session\SessionManager;
|
2018-06-27 18:49:23 +00:00
|
|
|
use Wikimedia\Rdbms\IResultWrapper;
|
2018-01-21 04:33:38 +00:00
|
|
|
use Wikimedia\RelPath;
|
2018-01-21 04:39:17 +00:00
|
|
|
use Wikimedia\WrappedString;
|
|
|
|
|
use Wikimedia\WrappedStringList;
|
2015-06-04 03:52:45 +00:00
|
|
|
|
2004-09-02 23:28:24 +00:00
|
|
|
/**
|
2019-06-28 14:54:40 +00:00
|
|
|
* This is one of the Core classes and should
|
|
|
|
|
* be read at least once by any new developers. Also documented at
|
|
|
|
|
* https://www.mediawiki.org/wiki/Manual:Architectural_modules/OutputPage
|
2011-01-13 21:08:59 +00:00
|
|
|
*
|
|
|
|
|
* This class is used to prepare the final rendering. A skin is then
|
|
|
|
|
* applied to the output parameters (links, javascript, html, categories ...).
|
2011-04-23 19:28:35 +00:00
|
|
|
*
|
2011-05-17 22:03:20 +00:00
|
|
|
* @todo FIXME: Another class handles sending the whole page to the client.
|
2011-04-23 19:28:35 +00:00
|
|
|
*
|
2011-10-24 09:08:13 +00:00
|
|
|
* Some comments comes from a pairing session between Zak Greant and Antoine Musso
|
2011-03-26 15:18:16 +00:00
|
|
|
* in November 2010.
|
2011-01-13 21:08:59 +00:00
|
|
|
*
|
2004-09-02 23:28:24 +00:00
|
|
|
* @todo document
|
|
|
|
|
*/
|
2011-07-30 19:37:19 +00:00
|
|
|
class OutputPage extends ContextSource {
|
2019-08-30 16:01:28 +00:00
|
|
|
/** @var string[][] Should be private. Used with addMeta() which adds "<meta>" */
|
2016-02-17 09:09:32 +00:00
|
|
|
protected $mMetatags = [];
|
2011-01-13 21:08:59 +00:00
|
|
|
|
2014-05-11 01:54:15 +00:00
|
|
|
/** @var array */
|
2016-02-17 09:09:32 +00:00
|
|
|
protected $mLinktags = [];
|
2011-01-13 21:08:59 +00:00
|
|
|
|
2019-09-15 14:40:42 +00:00
|
|
|
/** @var string|bool */
|
2014-05-11 01:54:15 +00:00
|
|
|
protected $mCanonicalUrl = false;
|
2011-01-13 22:08:13 +00:00
|
|
|
|
2014-05-11 01:54:15 +00:00
|
|
|
/**
|
2019-08-05 17:00:00 +00:00
|
|
|
* @var string The contents of <h1>
|
|
|
|
|
*/
|
2018-07-24 13:38:44 +00:00
|
|
|
private $mPageTitle = '';
|
2011-01-13 21:08:59 +00:00
|
|
|
|
2016-11-09 06:24:57 +00:00
|
|
|
/**
|
|
|
|
|
* @var string The displayed title of the page. Different from page title
|
|
|
|
|
* if overridden by display title magic word or hooks. Can contain safe
|
|
|
|
|
* HTML. Different from page title which may contain messages such as
|
|
|
|
|
* "Editing X" which is displayed in h1. This can be used for other places
|
|
|
|
|
* where the page name is referred on the page.
|
|
|
|
|
*/
|
|
|
|
|
private $displayTitle;
|
|
|
|
|
|
2019-09-07 23:44:46 +00:00
|
|
|
/** @var bool See OutputPage::couldBePublicCached. */
|
|
|
|
|
private $cacheIsFinal = false;
|
|
|
|
|
|
2014-05-11 01:54:15 +00:00
|
|
|
/**
|
|
|
|
|
* @var string Contains all of the "<body>" content. Should be private we
|
|
|
|
|
* got set/get accessors and the append() method.
|
|
|
|
|
*/
|
|
|
|
|
public $mBodytext = '';
|
2010-07-08 08:12:19 +00:00
|
|
|
|
2014-05-11 01:54:15 +00:00
|
|
|
/** @var string Stores contents of "<title>" tag */
|
|
|
|
|
private $mHTMLtitle = '';
|
2011-07-27 13:14:28 +00:00
|
|
|
|
|
|
|
|
/**
|
2014-05-11 01:54:15 +00:00
|
|
|
* @var bool Is the displayed content related to the source of the
|
|
|
|
|
* corresponding wiki article.
|
2011-07-27 13:14:28 +00:00
|
|
|
*/
|
2018-07-25 18:41:42 +00:00
|
|
|
private $mIsArticle = false;
|
2014-05-11 01:54:15 +00:00
|
|
|
|
|
|
|
|
/** @var bool Stores "article flag" toggle. */
|
|
|
|
|
private $mIsArticleRelated = true;
|
2011-01-13 21:08:59 +00:00
|
|
|
|
2018-10-03 13:12:37 +00:00
|
|
|
/** @var bool Is the content subject to copyright */
|
|
|
|
|
private $mHasCopyright = false;
|
|
|
|
|
|
2011-01-14 07:54:28 +00:00
|
|
|
/**
|
2014-05-11 01:54:15 +00:00
|
|
|
* @var bool We have to set isPrintable(). Some pages should
|
2011-01-14 07:54:28 +00:00
|
|
|
* never be printed (ex: redirections).
|
2011-01-13 21:08:59 +00:00
|
|
|
*/
|
2014-05-11 01:54:15 +00:00
|
|
|
private $mPrintable = false;
|
2011-01-13 21:08:59 +00:00
|
|
|
|
2011-01-14 07:54:28 +00:00
|
|
|
/**
|
2014-05-11 01:54:15 +00:00
|
|
|
* @var array Contains the page subtitle. Special pages usually have some
|
|
|
|
|
* links here. Don't confuse with site subtitle added by skins.
|
2011-01-13 22:20:04 +00:00
|
|
|
*/
|
2016-02-17 09:09:32 +00:00
|
|
|
private $mSubtitle = [];
|
2011-01-13 21:08:59 +00:00
|
|
|
|
2014-05-11 01:54:15 +00:00
|
|
|
/** @var string */
|
|
|
|
|
public $mRedirect = '';
|
|
|
|
|
|
|
|
|
|
/** @var int */
|
|
|
|
|
protected $mStatusCode;
|
2011-01-13 21:08:59 +00:00
|
|
|
|
2011-01-14 07:54:28 +00:00
|
|
|
/**
|
2016-06-27 11:21:14 +00:00
|
|
|
* @var string Used for sending cache control.
|
2014-05-11 01:54:15 +00:00
|
|
|
* The whole caching system should probably be moved into its own class.
|
2011-01-14 07:54:28 +00:00
|
|
|
*/
|
2014-05-11 01:54:15 +00:00
|
|
|
protected $mLastModified = '';
|
2011-01-13 22:08:13 +00:00
|
|
|
|
2014-05-11 01:54:15 +00:00
|
|
|
/** @var array */
|
2016-02-17 09:09:32 +00:00
|
|
|
protected $mCategoryLinks = [];
|
2011-01-13 21:08:59 +00:00
|
|
|
|
2014-05-11 01:54:15 +00:00
|
|
|
/** @var array */
|
2016-07-08 19:11:53 +00:00
|
|
|
protected $mCategories = [
|
|
|
|
|
'hidden' => [],
|
|
|
|
|
'normal' => [],
|
|
|
|
|
];
|
2011-01-13 21:08:59 +00:00
|
|
|
|
Implement page status indicators
Page status indicators are icons (or short text snippets) usually
displayed in the top-right corner of the page, outside of the main
content. Basically, <indicator name="foo">[[File:Foo.svg|20px]]</indicator>
may be used on a page to place the icon in the indicator area. They
are also known as top icons, page icons, heading icons or title icons.
I found the discussion on bug 23796 highly illuminating. I suggest
that everyone read it before suggesting different design choices.
I spent some time with a thesaurus pondering the name. "Emblems" and
"badges" were also considered, but the former has a much more limited
meaning and the latter is already taken by Wikidata, with a similar
but subtly different feature set. I am not aware of any naming
conflicts ;) besides new talk page message "indicator" (used by core
and Echo in some documents) and OOjs UI indicators (tiny icons like
the arrow on a dropdown form element), which shouldn't be confusing.
Potential use cases include:
* "Lock" indicators for page protection levels
* Featured/good article indicators
* Redirect shortcuts display ("WP:VPT")
* Links to help/manual for special pages
* Coordinates?… or globe icon for inline pop-up maps
Design features:
* Skin-customizable. Skins can fully control where and how indicators
are shown, or may just do <?php echo $this->getIndicators(); ?> to
output the default structure. By default they are not shown at all.
* Extension-customizable. Extensions can call ParserOutput::addIndicator()
to insert an indicator from one of the numerous parser hooks.
* Wiki-customizable. In addition to just using the parser functions,
on-wiki styles and scripts can use the provided classes and ids
(.mw-indicator, #mw-indicator-<name>) to customize their display.
Design limitations:
* Every indicator must have a unique identifier (name). It's not
possible to create arrays, or to have several indicators with the
same name. In case of duplicates, the latest occurrence of the
parser function wins.
* Indicators are displayed ordered by their names (and not occurrence
order). This ensures consistency across pages and provides a simple
means of ordering or grouping them.
* Indicators are not stored, tracked or accessible outside of
ParserOutput (in particular they're not in the page_props table).
They are intended to merely reflect the content or metadata that is
already present on the page, and not be data themselves. If you ever
think you need to list pages with a given status indicator, instead
figure out what it means and use the appropriate tracking category,
special page report, already existing page_prop, or other means.
Corresponding patch in Vector: I90a8ae15ac8275d084ea5f47b6b2684d5e6c7412.
I'll implement support in the other three skins included in the tarball
and document it on mediawiki.org after this is merged.
Bug: 23796
Change-Id: I2389ff9a5332a2b1d033eb75f0946e5241cfaaf4
2014-09-24 10:44:16 +00:00
|
|
|
/** @var array */
|
2016-02-17 09:09:32 +00:00
|
|
|
protected $mIndicators = [];
|
Implement page status indicators
Page status indicators are icons (or short text snippets) usually
displayed in the top-right corner of the page, outside of the main
content. Basically, <indicator name="foo">[[File:Foo.svg|20px]]</indicator>
may be used on a page to place the icon in the indicator area. They
are also known as top icons, page icons, heading icons or title icons.
I found the discussion on bug 23796 highly illuminating. I suggest
that everyone read it before suggesting different design choices.
I spent some time with a thesaurus pondering the name. "Emblems" and
"badges" were also considered, but the former has a much more limited
meaning and the latter is already taken by Wikidata, with a similar
but subtly different feature set. I am not aware of any naming
conflicts ;) besides new talk page message "indicator" (used by core
and Echo in some documents) and OOjs UI indicators (tiny icons like
the arrow on a dropdown form element), which shouldn't be confusing.
Potential use cases include:
* "Lock" indicators for page protection levels
* Featured/good article indicators
* Redirect shortcuts display ("WP:VPT")
* Links to help/manual for special pages
* Coordinates?… or globe icon for inline pop-up maps
Design features:
* Skin-customizable. Skins can fully control where and how indicators
are shown, or may just do <?php echo $this->getIndicators(); ?> to
output the default structure. By default they are not shown at all.
* Extension-customizable. Extensions can call ParserOutput::addIndicator()
to insert an indicator from one of the numerous parser hooks.
* Wiki-customizable. In addition to just using the parser functions,
on-wiki styles and scripts can use the provided classes and ids
(.mw-indicator, #mw-indicator-<name>) to customize their display.
Design limitations:
* Every indicator must have a unique identifier (name). It's not
possible to create arrays, or to have several indicators with the
same name. In case of duplicates, the latest occurrence of the
parser function wins.
* Indicators are displayed ordered by their names (and not occurrence
order). This ensures consistency across pages and provides a simple
means of ordering or grouping them.
* Indicators are not stored, tracked or accessible outside of
ParserOutput (in particular they're not in the page_props table).
They are intended to merely reflect the content or metadata that is
already present on the page, and not be data themselves. If you ever
think you need to list pages with a given status indicator, instead
figure out what it means and use the appropriate tracking category,
special page report, already existing page_prop, or other means.
Corresponding patch in Vector: I90a8ae15ac8275d084ea5f47b6b2684d5e6c7412.
I'll implement support in the other three skins included in the tarball
and document it on mediawiki.org after this is merged.
Bug: 23796
Change-Id: I2389ff9a5332a2b1d033eb75f0946e5241cfaaf4
2014-09-24 10:44:16 +00:00
|
|
|
|
2014-05-11 01:54:15 +00:00
|
|
|
/** @var array Array of Interwiki Prefixed (non DB key) Titles (e.g. 'fr:Test page') */
|
2016-02-17 09:09:32 +00:00
|
|
|
private $mLanguageLinks = [];
|
2011-01-13 21:08:59 +00:00
|
|
|
|
2011-01-14 07:54:28 +00:00
|
|
|
/**
|
2015-10-28 03:24:40 +00:00
|
|
|
* Used for JavaScript (predates ResourceLoader)
|
2014-05-11 01:54:15 +00:00
|
|
|
* @todo We should split JS / CSS.
|
2012-07-10 12:48:06 +00:00
|
|
|
* mScripts content is inserted as is in "<head>" by Skin. This might
|
2014-05-11 01:54:15 +00:00
|
|
|
* contain either a link to a stylesheet or inline CSS.
|
2011-01-13 21:08:59 +00:00
|
|
|
*/
|
2014-05-11 01:54:15 +00:00
|
|
|
private $mScripts = '';
|
2011-01-14 07:54:28 +00:00
|
|
|
|
2014-05-11 01:54:15 +00:00
|
|
|
/** @var string Inline CSS styles. Use addInlineStyle() sparingly */
|
|
|
|
|
protected $mInlineStyles = '';
|
2011-01-13 21:08:59 +00:00
|
|
|
|
|
|
|
|
/**
|
2014-05-11 01:54:15 +00:00
|
|
|
* @var string Used by skin template.
|
2011-01-13 21:08:59 +00:00
|
|
|
* Example: $tpl->set( 'displaytitle', $out->mPageLinkTitle );
|
|
|
|
|
*/
|
2014-05-11 01:54:15 +00:00
|
|
|
public $mPageLinkTitle = '';
|
|
|
|
|
|
|
|
|
|
/** @var array Array of elements in "<head>". Parser might add its own headers! */
|
2016-02-17 09:09:32 +00:00
|
|
|
protected $mHeadItems = [];
|
2014-05-11 01:54:15 +00:00
|
|
|
|
2017-09-11 07:12:03 +00:00
|
|
|
/** @var array Additional <body> classes; there are also <body> classes from other sources */
|
|
|
|
|
protected $mAdditionalBodyClasses = [];
|
|
|
|
|
|
2014-05-11 01:54:15 +00:00
|
|
|
/** @var array */
|
2016-02-17 09:09:32 +00:00
|
|
|
protected $mModules = [];
|
2014-05-11 01:54:15 +00:00
|
|
|
|
|
|
|
|
/** @var array */
|
2016-02-17 09:09:32 +00:00
|
|
|
protected $mModuleStyles = [];
|
2010-07-08 08:12:19 +00:00
|
|
|
|
2014-05-11 01:54:15 +00:00
|
|
|
/** @var ResourceLoader */
|
|
|
|
|
protected $mResourceLoader;
|
2011-01-13 22:08:13 +00:00
|
|
|
|
resourceloader: Move queue formatting out of OutputPage
HTML formatting of the queue was distributed over several OutputPage methods.
Each method demanding a snippet of HTML by calling makeResourceLoaderLink()
with a limited amount of information. As such, makeResourceLoaderLink() was
unable to provide the client with the proper state information.
Centralising it also allows it to better reduce duplication in HTML output
and maintain a more accurate state.
Problems fixed by centralising:
1. The 'user' module is special (due to per-user 'version' and 'user' params).
It is manually requested via script-src. To avoid a separate (and wrong)
request from something that requires it, we set state=loading directly.
However, because the module is in the bottom, the old HTML formatter could
only put state=loading in the bottom also. This sometimes caused a wrong
request to be fired for modules=user if something in the top queue
triggered a requirement for it.
2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose
of allowing dependencies on style modules without risking duplicate loading
on pages where the styles are loaded already. This didn't work, because the
state information about page-style modules is output near the stylesheet,
which is after the script tag with mw.loader.load(). That runs first, and
mw.loader would still make a duplicate request before it learns the state.
Changes:
* Document reasons for style/script tag order in getHeadHtml (per 09537e83).
* Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed
before since a duplicate check in makeResourceLoaderLink() verified the
origin a second time.
* Declare explicit position 'top' on 'user.options' and 'user.tokens' module.
Previously, OutputPage hardcoded them in the top. The new formatter doesn't.
* Remove getHeadScripts().
* Remove getInlineHeadScripts().
* Remove getExternalHeadScripts().
* Remove buildCssLinks().
* Remove getScriptsForBottomQueue().
* Change where Skin::setupSkinUserCss() is called. This methods lets the skin
add modules to the queue. Previously it was called from buildCssLinks(),
via headElement(), via prepareQuickTemplate(), via OutputPage::output().
It's now in OutputPage::output() directly (slightly earlier). This is needed
because prepareQuickTemplate() calls bottomScripts() before headElement().
And bottomScript() would lazy-initialise the queue and lock it before
setupSkinUserCss() is called from headElement().
This makes execution order more predictable instead of being dependent on
the arbitrary order of data extraction in prepareQuickTemplate (which varies
from one skin to another).
* Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early
on so. This avoids wrongful loading and fixes problem 1.
Effective changes in output:
* mw.loader.state() is now before mw.loader.load(). This fixes problem 2.
* mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading".
* mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1.
* The <script async src> tag for 'startup' changed position (slightly).
Previously it was after all inline scripts and stylesheets. It's still after
all inline scripts and after most stylesheets, but before any user styles.
Since the queue is now formatted outside OutputPage, it can't inject the
meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle
of existing output. This shouldn't have any noticable impact.
Bug: T87871
Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
|
|
|
/** @var ResourceLoaderClientHtml */
|
|
|
|
|
private $rlClient;
|
|
|
|
|
|
|
|
|
|
/** @var ResourceLoaderContext */
|
|
|
|
|
private $rlClientContext;
|
|
|
|
|
|
2016-08-19 02:04:21 +00:00
|
|
|
/** @var array */
|
|
|
|
|
private $rlExemptStyleModules;
|
|
|
|
|
|
2014-05-11 01:54:15 +00:00
|
|
|
/** @var array */
|
2016-02-17 09:09:32 +00:00
|
|
|
protected $mJsConfigVars = [];
|
2009-09-17 01:19:02 +00:00
|
|
|
|
2014-05-11 01:54:15 +00:00
|
|
|
/** @var array */
|
2016-02-17 09:09:32 +00:00
|
|
|
protected $mTemplateIds = [];
|
2006-05-11 22:40:38 +00:00
|
|
|
|
2014-05-11 01:54:15 +00:00
|
|
|
/** @var array */
|
2016-02-17 09:09:32 +00:00
|
|
|
protected $mImageTimeKeys = [];
|
2011-07-06 17:55:44 +00:00
|
|
|
|
2014-05-11 01:54:15 +00:00
|
|
|
/** @var string */
|
|
|
|
|
public $mRedirectCode = '';
|
2011-07-06 17:55:44 +00:00
|
|
|
|
2014-05-11 01:54:15 +00:00
|
|
|
protected $mFeedLinksAppendQuery = null;
|
|
|
|
|
|
2014-10-10 06:46:12 +00:00
|
|
|
/** @var array
|
|
|
|
|
* What level of 'untrustworthiness' is allowed in CSS/JS modules loaded on this page?
|
2014-05-11 01:54:15 +00:00
|
|
|
* @see ResourceLoaderModule::$origin
|
2014-10-10 06:46:12 +00:00
|
|
|
* ResourceLoaderModule::ORIGIN_ALL is assumed unless overridden;
|
2014-05-11 01:54:15 +00:00
|
|
|
*/
|
2016-02-17 09:09:32 +00:00
|
|
|
protected $mAllowedModules = [
|
2014-10-10 06:46:12 +00:00
|
|
|
ResourceLoaderModule::TYPE_COMBINED => ResourceLoaderModule::ORIGIN_ALL,
|
2016-02-17 09:09:32 +00:00
|
|
|
];
|
2011-01-13 21:08:59 +00:00
|
|
|
|
2014-05-11 01:54:15 +00:00
|
|
|
/** @var bool Whether output is disabled. If this is true, the 'output' method will do nothing. */
|
|
|
|
|
protected $mDoNothing = false;
|
|
|
|
|
|
|
|
|
|
// Parser related.
|
|
|
|
|
|
|
|
|
|
/** @var int */
|
|
|
|
|
protected $mContainsNewMagic = 0;
|
2011-01-13 21:08:59 +00:00
|
|
|
|
2011-10-26 03:44:47 +00:00
|
|
|
/**
|
|
|
|
|
* lazy initialised, use parserOptions()
|
|
|
|
|
* @var ParserOptions
|
|
|
|
|
*/
|
2011-01-13 21:08:59 +00:00
|
|
|
protected $mParserOptions = null;
|
|
|
|
|
|
2011-01-14 07:54:28 +00:00
|
|
|
/**
|
2014-05-11 01:54:15 +00:00
|
|
|
* Handles the Atom / RSS links.
|
|
|
|
|
* We probably only support Atom in 2011.
|
2011-01-14 07:54:28 +00:00
|
|
|
* @see $wgAdvertisedFeedTypes
|
2011-01-13 21:08:59 +00:00
|
|
|
*/
|
2016-02-17 09:09:32 +00:00
|
|
|
private $mFeedLinks = [];
|
2009-11-07 09:26:02 +00:00
|
|
|
|
2011-01-13 21:08:59 +00:00
|
|
|
// Gwicke work on squid caching? Roughly from 2003.
|
2014-05-11 01:54:15 +00:00
|
|
|
protected $mEnableClientCache = true;
|
2011-01-13 21:08:59 +00:00
|
|
|
|
2014-05-11 01:54:15 +00:00
|
|
|
/** @var bool Flag if output should only contain the body of the article. */
|
|
|
|
|
private $mArticleBodyOnly = false;
|
|
|
|
|
|
|
|
|
|
/** @var bool */
|
|
|
|
|
protected $mNewSectionLink = false;
|
2008-04-14 07:45:50 +00:00
|
|
|
|
2014-05-11 01:54:15 +00:00
|
|
|
/** @var bool */
|
|
|
|
|
protected $mHideNewSectionLink = false;
|
2011-01-13 21:08:59 +00:00
|
|
|
|
2011-01-14 07:54:28 +00:00
|
|
|
/**
|
2014-05-11 01:54:15 +00:00
|
|
|
* @var bool Comes from the parser. This was probably made to load CSS/JS
|
|
|
|
|
* only if we had "<gallery>". Used directly in CategoryPage.php.
|
2015-10-28 03:24:40 +00:00
|
|
|
* Looks like ResourceLoader can replace this.
|
2011-01-13 21:08:59 +00:00
|
|
|
*/
|
2014-05-11 01:54:15 +00:00
|
|
|
public $mNoGallery = false;
|
|
|
|
|
|
|
|
|
|
/** @var int Cache stuff. Looks like mEnableClientCache */
|
2015-12-10 01:07:05 +00:00
|
|
|
protected $mCdnMaxage = 0;
|
|
|
|
|
/** @var int Upper limit on mCdnMaxage */
|
2015-10-01 07:24:18 +00:00
|
|
|
protected $mCdnMaxageLimit = INF;
|
2011-01-13 21:08:59 +00:00
|
|
|
|
2014-05-11 01:54:15 +00:00
|
|
|
/**
|
2015-01-01 11:19:06 +00:00
|
|
|
* @var bool Controls if anti-clickjacking / frame-breaking headers will
|
|
|
|
|
* be sent. This should be done for pages where edit actions are possible.
|
|
|
|
|
* Setters: $this->preventClickjacking() and $this->allowClickjacking().
|
2014-05-11 01:54:15 +00:00
|
|
|
*/
|
|
|
|
|
protected $mPreventClickjacking = true;
|
|
|
|
|
|
|
|
|
|
/** @var int To include the variable {{REVISIONID}} */
|
|
|
|
|
private $mRevisionId = null;
|
2011-01-13 21:08:59 +00:00
|
|
|
|
2014-05-11 01:54:15 +00:00
|
|
|
/** @var string */
|
2011-12-10 16:30:40 +00:00
|
|
|
private $mRevisionTimestamp = null;
|
2011-01-13 21:08:59 +00:00
|
|
|
|
2014-05-11 01:54:15 +00:00
|
|
|
/** @var array */
|
|
|
|
|
protected $mFileVersion = null;
|
2011-07-05 05:23:26 +00:00
|
|
|
|
2008-08-21 14:09:57 +00:00
|
|
|
/**
|
2014-05-11 01:54:15 +00:00
|
|
|
* @var array An array of stylesheet filenames (relative from skins path),
|
|
|
|
|
* with options for CSS media, IE conditions, and RTL/LTR direction.
|
2008-08-21 14:09:57 +00:00
|
|
|
* For internal use; add settings in the skin via $this->addStyle()
|
2011-01-13 21:08:59 +00:00
|
|
|
*
|
|
|
|
|
* Style again! This seems like a code duplication since we already have
|
2014-05-11 01:54:15 +00:00
|
|
|
* mStyles. This is what makes Open Source amazing.
|
2008-08-21 14:09:57 +00:00
|
|
|
*/
|
2016-02-17 09:09:32 +00:00
|
|
|
protected $styles = [];
|
2008-08-21 14:09:57 +00:00
|
|
|
|
2008-07-23 19:05:43 +00:00
|
|
|
private $mIndexPolicy = 'index';
|
|
|
|
|
private $mFollowPolicy = 'follow';
|
2018-08-02 18:26:55 +00:00
|
|
|
|
|
|
|
|
/**
|
2019-06-19 18:22:42 +00:00
|
|
|
* @var array Headers that cause the cache to vary. Key is header name,
|
|
|
|
|
* value should always be null. (Value was an array of options for
|
|
|
|
|
* the `Key` header, which was deprecated in 1.32 and removed in 1.34.)
|
2018-08-02 18:26:55 +00:00
|
|
|
*/
|
2016-02-17 09:09:32 +00:00
|
|
|
private $mVaryHeader = [
|
2019-06-19 18:22:42 +00:00
|
|
|
'Accept-Encoding' => null,
|
2016-02-17 09:09:32 +00:00
|
|
|
];
|
2010-01-28 21:58:50 +00:00
|
|
|
|
2011-11-30 12:43:10 +00:00
|
|
|
/**
|
|
|
|
|
* If the current page was reached through a redirect, $mRedirectedFrom contains the Title
|
|
|
|
|
* of the redirect.
|
|
|
|
|
*
|
|
|
|
|
* @var Title
|
|
|
|
|
*/
|
|
|
|
|
private $mRedirectedFrom = null;
|
|
|
|
|
|
2013-02-08 22:58:38 +00:00
|
|
|
/**
|
|
|
|
|
* Additional key => value data
|
|
|
|
|
*/
|
2016-02-17 09:09:32 +00:00
|
|
|
private $mProperties = [];
|
2013-02-08 22:58:38 +00:00
|
|
|
|
2013-02-14 17:05:22 +00:00
|
|
|
/**
|
2014-04-08 15:29:17 +00:00
|
|
|
* @var string|null ResourceLoader target for load.php links. If null, will be omitted
|
2013-02-14 17:05:22 +00:00
|
|
|
*/
|
|
|
|
|
private $mTarget = null;
|
|
|
|
|
|
2013-08-22 22:22:03 +00:00
|
|
|
/**
|
2017-05-20 13:39:40 +00:00
|
|
|
* @var bool Whether parser output contains a table of contents
|
2013-08-22 22:22:03 +00:00
|
|
|
*/
|
2017-05-20 13:39:40 +00:00
|
|
|
private $mEnableTOC = false;
|
2013-08-22 22:22:03 +00:00
|
|
|
|
2015-06-08 13:39:06 +00:00
|
|
|
/**
|
2017-06-07 12:11:08 +00:00
|
|
|
* @var string|null The URL to send in a <link> element with rel=license
|
2015-06-08 13:39:06 +00:00
|
|
|
*/
|
|
|
|
|
private $copyrightUrl;
|
|
|
|
|
|
2016-11-10 20:29:27 +00:00
|
|
|
/** @var array Profiling data */
|
|
|
|
|
private $limitReportJSData = [];
|
|
|
|
|
|
2017-02-28 20:52:17 +00:00
|
|
|
/** @var array Map Title to Content */
|
|
|
|
|
private $contentOverrides = [];
|
|
|
|
|
|
|
|
|
|
/** @var callable[] */
|
|
|
|
|
private $contentOverrideCallbacks = [];
|
|
|
|
|
|
2015-06-01 16:58:42 +00:00
|
|
|
/**
|
|
|
|
|
* Link: header contents
|
|
|
|
|
*/
|
|
|
|
|
private $mLinkHeader = [];
|
|
|
|
|
|
Initial support for Content Security Policy, disabled by default
The primary goal here is a defense in depth measure to
stop an attacker who found a bug in the parser allowing
them to insert malicious attributes.
This wouldn't stop someone who could insert a full
script tag (since at current it can't distinguish between
malicious and legit user js). It also would not prevent
DOM-based or reflected XSS for anons, as the nonce value
is guessable for anons when receiving a response cached
by varnish. However, the limited protection of just stopping
stored XSS where the attacker only has control of attributes,
is still a big win in my opinion. (But it wouldn't prevent
someone who has that type of xss from abusing things like
data-ooui attribute).
This will likely break many gadgets. Its expected that any
sort of rollout on Wikimedia will be done very slowly, with
lots of testing and the report-only option to begin with.
This is behind feature flags that are off by default, so
merging this patch should not cause any change in default
behaviour.
This may break some extensions (The most obvious one
is charinsert (See fe648d41005), but will probably need
some testing in report-only mode to see if anything else breaks)
This uses the unsafe-eval option of CSP, in order to
support RL's local storage thingy. For better security,
we may want to remove some of the sillier uses of eval
(e.g. jquery.ui.datepicker.js).
For more info, see spec: https://www.w3.org/TR/CSP2/
Additionally see:
https://www.mediawiki.org/wiki/Requests_for_comment/Content-Security-Policy
Bug: T135963
Change-Id: I80f6f469ba4c0b608385483457df96ccb7429ae5
2016-02-29 04:13:10 +00:00
|
|
|
/**
|
2019-10-28 05:01:17 +00:00
|
|
|
* @var ContentSecurityPolicy
|
Initial support for Content Security Policy, disabled by default
The primary goal here is a defense in depth measure to
stop an attacker who found a bug in the parser allowing
them to insert malicious attributes.
This wouldn't stop someone who could insert a full
script tag (since at current it can't distinguish between
malicious and legit user js). It also would not prevent
DOM-based or reflected XSS for anons, as the nonce value
is guessable for anons when receiving a response cached
by varnish. However, the limited protection of just stopping
stored XSS where the attacker only has control of attributes,
is still a big win in my opinion. (But it wouldn't prevent
someone who has that type of xss from abusing things like
data-ooui attribute).
This will likely break many gadgets. Its expected that any
sort of rollout on Wikimedia will be done very slowly, with
lots of testing and the report-only option to begin with.
This is behind feature flags that are off by default, so
merging this patch should not cause any change in default
behaviour.
This may break some extensions (The most obvious one
is charinsert (See fe648d41005), but will probably need
some testing in report-only mode to see if anything else breaks)
This uses the unsafe-eval option of CSP, in order to
support RL's local storage thingy. For better security,
we may want to remove some of the sillier uses of eval
(e.g. jquery.ui.datepicker.js).
For more info, see spec: https://www.w3.org/TR/CSP2/
Additionally see:
https://www.mediawiki.org/wiki/Requests_for_comment/Content-Security-Policy
Bug: T135963
Change-Id: I80f6f469ba4c0b608385483457df96ccb7429ae5
2016-02-29 04:13:10 +00:00
|
|
|
*/
|
2019-10-28 05:01:17 +00:00
|
|
|
private $CSP;
|
Initial support for Content Security Policy, disabled by default
The primary goal here is a defense in depth measure to
stop an attacker who found a bug in the parser allowing
them to insert malicious attributes.
This wouldn't stop someone who could insert a full
script tag (since at current it can't distinguish between
malicious and legit user js). It also would not prevent
DOM-based or reflected XSS for anons, as the nonce value
is guessable for anons when receiving a response cached
by varnish. However, the limited protection of just stopping
stored XSS where the attacker only has control of attributes,
is still a big win in my opinion. (But it wouldn't prevent
someone who has that type of xss from abusing things like
data-ooui attribute).
This will likely break many gadgets. Its expected that any
sort of rollout on Wikimedia will be done very slowly, with
lots of testing and the report-only option to begin with.
This is behind feature flags that are off by default, so
merging this patch should not cause any change in default
behaviour.
This may break some extensions (The most obvious one
is charinsert (See fe648d41005), but will probably need
some testing in report-only mode to see if anything else breaks)
This uses the unsafe-eval option of CSP, in order to
support RL's local storage thingy. For better security,
we may want to remove some of the sillier uses of eval
(e.g. jquery.ui.datepicker.js).
For more info, see spec: https://www.w3.org/TR/CSP2/
Additionally see:
https://www.mediawiki.org/wiki/Requests_for_comment/Content-Security-Policy
Bug: T135963
Change-Id: I80f6f469ba4c0b608385483457df96ccb7429ae5
2016-02-29 04:13:10 +00:00
|
|
|
|
2018-08-02 18:42:17 +00:00
|
|
|
/**
|
|
|
|
|
* @var array A cache of the names of the cookies that will influence the cache
|
|
|
|
|
*/
|
|
|
|
|
private static $cacheVaryCookies = null;
|
|
|
|
|
|
2011-04-04 00:37:42 +00:00
|
|
|
/**
|
|
|
|
|
* 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.
|
2017-09-09 02:12:37 +00:00
|
|
|
* @param IContextSource $context
|
2011-04-04 00:37:42 +00:00
|
|
|
*/
|
2019-09-14 10:26:31 +00:00
|
|
|
public function __construct( IContextSource $context ) {
|
2017-09-09 02:12:37 +00:00
|
|
|
$this->setContext( $context );
|
2019-10-28 05:01:17 +00:00
|
|
|
$this->CSP = new ContentSecurityPolicy(
|
|
|
|
|
$context->getRequest()->response(),
|
|
|
|
|
$context->getConfig()
|
|
|
|
|
);
|
2011-04-04 00:37:42 +00:00
|
|
|
}
|
|
|
|
|
|
2010-01-28 21:58:50 +00:00
|
|
|
/**
|
|
|
|
|
* Redirect to $url rather than displaying the normal page
|
|
|
|
|
*
|
2017-12-28 15:06:10 +00:00
|
|
|
* @param string $url
|
2019-12-08 12:37:24 +00:00
|
|
|
* @param string|int $responsecode HTTP status code
|
2010-01-28 21:58:50 +00:00
|
|
|
*/
|
2006-11-07 05:37:31 +00:00
|
|
|
public function redirect( $url, $responsecode = '302' ) {
|
2006-06-23 03:56:03 +00:00
|
|
|
# Strip newlines as a paranoia check for header injection in PHP<5.1.2
|
|
|
|
|
$this->mRedirect = str_replace( "\n", '', $url );
|
2019-12-08 12:37:24 +00:00
|
|
|
$this->mRedirectCode = (string)$responsecode;
|
2006-06-23 03:56:03 +00:00
|
|
|
}
|
2008-04-14 07:45:50 +00:00
|
|
|
|
2010-01-28 21:58:50 +00:00
|
|
|
/**
|
|
|
|
|
* Get the URL to redirect to, or an empty string if not redirect URL set
|
|
|
|
|
*
|
2014-04-10 18:50:10 +00:00
|
|
|
* @return string
|
2010-01-28 21:58:50 +00:00
|
|
|
*/
|
2007-11-06 01:16:25 +00:00
|
|
|
public function getRedirect() {
|
|
|
|
|
return $this->mRedirect;
|
|
|
|
|
}
|
2003-04-14 23:10:40 +00:00
|
|
|
|
2015-06-08 13:39:06 +00:00
|
|
|
/**
|
|
|
|
|
* Set the copyright URL to send with the output.
|
|
|
|
|
* Empty string to omit, null to reset.
|
|
|
|
|
*
|
|
|
|
|
* @since 1.26
|
|
|
|
|
*
|
|
|
|
|
* @param string|null $url
|
|
|
|
|
*/
|
|
|
|
|
public function setCopyrightUrl( $url ) {
|
|
|
|
|
$this->copyrightUrl = $url;
|
|
|
|
|
}
|
|
|
|
|
|
2006-11-07 05:37:31 +00:00
|
|
|
/**
|
|
|
|
|
* Set the HTTP status code to send with the output.
|
|
|
|
|
*
|
2014-04-10 18:50:10 +00:00
|
|
|
* @param int $statusCode
|
2006-11-07 05:37:31 +00:00
|
|
|
*/
|
2010-01-28 21:58:50 +00:00
|
|
|
public function setStatusCode( $statusCode ) {
|
|
|
|
|
$this->mStatusCode = $statusCode;
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-21 14:09:57 +00:00
|
|
|
/**
|
2012-07-10 12:48:06 +00:00
|
|
|
* Add a new "<meta>" tag
|
2008-08-21 14:09:57 +00:00
|
|
|
* To add an http-equiv meta tag, precede the name with "http:"
|
|
|
|
|
*
|
2017-12-28 15:31:56 +00:00
|
|
|
* @param string $name Name of the meta tag
|
|
|
|
|
* @param string $val Value of the meta tag
|
2008-08-21 14:09:57 +00:00
|
|
|
*/
|
2019-09-14 10:26:31 +00:00
|
|
|
public function addMeta( $name, $val ) {
|
|
|
|
|
$this->mMetatags[] = [ $name, $val ];
|
2008-08-21 14:09:57 +00:00
|
|
|
}
|
|
|
|
|
|
2014-12-03 17:40:15 +00:00
|
|
|
/**
|
|
|
|
|
* Returns the current <meta> tags
|
|
|
|
|
*
|
|
|
|
|
* @since 1.25
|
|
|
|
|
* @return array
|
|
|
|
|
*/
|
|
|
|
|
public function getMetaTags() {
|
|
|
|
|
return $this->mMetatags;
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-28 21:58:50 +00:00
|
|
|
/**
|
2012-12-31 01:11:43 +00:00
|
|
|
* Add a new \<link\> tag to the page header.
|
|
|
|
|
*
|
|
|
|
|
* Note: use setCanonicalUrl() for rel=canonical.
|
2010-01-28 21:58:50 +00:00
|
|
|
*
|
2014-07-24 17:42:24 +00:00
|
|
|
* @param array $linkarr Associative array of attributes.
|
2010-01-28 21:58:50 +00:00
|
|
|
*/
|
2019-09-14 10:26:31 +00:00
|
|
|
public function addLink( array $linkarr ) {
|
|
|
|
|
$this->mLinktags[] = $linkarr;
|
2010-01-28 21:58:50 +00:00
|
|
|
}
|
|
|
|
|
|
2014-12-03 17:40:15 +00:00
|
|
|
/**
|
|
|
|
|
* Returns the current <link> tags
|
|
|
|
|
*
|
|
|
|
|
* @since 1.25
|
|
|
|
|
* @return array
|
|
|
|
|
*/
|
|
|
|
|
public function getLinkTags() {
|
|
|
|
|
return $this->mLinktags;
|
|
|
|
|
}
|
|
|
|
|
|
2012-12-31 01:11:43 +00:00
|
|
|
/**
|
|
|
|
|
* Set the URL to be used for the <link rel=canonical>. This should be used
|
|
|
|
|
* in preference to addLink(), to avoid duplicate link tags.
|
2014-08-14 18:22:52 +00:00
|
|
|
* @param string $url
|
2012-12-31 01:11:43 +00:00
|
|
|
*/
|
2019-09-14 10:26:31 +00:00
|
|
|
public function setCanonicalUrl( $url ) {
|
2012-12-31 01:11:43 +00:00
|
|
|
$this->mCanonicalUrl = $url;
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-03 17:40:15 +00:00
|
|
|
/**
|
|
|
|
|
* 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;
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-28 21:58:50 +00:00
|
|
|
/**
|
|
|
|
|
* Add raw HTML to the list of scripts (including \<script\> tag, etc.)
|
2015-10-31 15:16:58 +00:00
|
|
|
* Internal use only. Use OutputPage::addModules() or OutputPage::addJsConfigVars()
|
|
|
|
|
* if possible.
|
2010-01-28 21:58:50 +00:00
|
|
|
*
|
2014-07-24 17:42:24 +00:00
|
|
|
* @param string $script Raw HTML
|
2010-01-28 21:58:50 +00:00
|
|
|
*/
|
2019-09-14 10:26:31 +00:00
|
|
|
public function addScript( $script ) {
|
2016-02-18 16:33:15 +00:00
|
|
|
$this->mScripts .= $script;
|
2009-08-07 00:16:54 +00:00
|
|
|
}
|
here it is ... the upload-api, script-server, js2 (javascript phase2) branch merge 1st attempt.
Here is a short overview of changes and associated default configuration variables (most everything is off by default) also see ~soon to be updated~: http://www.mediawiki.org/wiki/Media_Projects_Overview
= Upload Improvements =
==Upload API ==
* Based on the early work of Bryan Tong and others it adds the upload option to the api.
* We rewrite Special:Upload page to include use the new refactoring
* Added in token checks in both the SpecialUpload.php page so avoids DOS / xss copy-by-url JavaScript based cross site POST file submissions
== Copy by URL==
$wgAllowCopyUploads = false;
* http class rewrite includes a new http background download see: includes/HttpFunctions.php
* spins off a php process that calls: maintenance/http_session_download.php
* pushes updates to the session and gives the user a progress bar on http copy uploads from other server progress (using js2 upload interface) (if not using the js2 upload interface it does the request in-place but the download is limited to the php ini timeout time)
== Firefogg ==
* Firefogg enables resumable upload by chunks
* progress indicators and conditional invokation (js2 system)
* and of-course client side transcoding.
= Script Server =
$wgEnableScriptLoader = false;
* off by default if $wgEnableScriptLoader is turned on script files are grouped, gziped, cached etc.
for more info see: http://www.mediawiki.org/wiki/Extension:ScriptLoader
* Includes some early skin js include fixes (skin/script system still lots of love)
* Includes a "javascript class autoloader" this is packaged into mwEmbed so that the mwEmbed library can work in stand alone mode (while retaining localization and script serving) (one such application is the make page for firefogg.org : http://www.firefogg.org/make/index.html )
* The file that contains the autojavascript loading classes is: js2/php/jsAutoloadLocalClasses.php
* One can use this auto class loading dependency system with extensions and add-ons but I need to better document that.
= js2 system / mwEmbed=
$wgEnableJS2system = false
* includes initial rewrite towards more jquery based javascript code
* especially for the Special:Upload page.
* Also the edit page include support for the "add-media-wizard"
* includes dependency loader for javascript that optionally takes advantage of the script-loader
* remote embedding of javascript interfaces (like embedding video, or commons media searching)
* $wgDebugJavaScript = false; .. .this variable lets you always get "always fresh javascript". When used with the script-loader it does not minify the script-loader output.
= mwEmbed =
* Will commit a separate patch to oggHandler that conditionally outputs <video tag> to use the new javascript video player.
** mv_embed player includes: play-head, volume control, remote embedding, oggz-chop support across plugins.
* add-media-wizard adds easy inserts of media to pages (with import)
== jQuery==
* we include a base install of jQuery, jQuery ui and some plugins.
* all the javascript classes are in the scriptloader so its easy to load any set of jquery ui components that you may need using the script-server. You get a callback so you can then execute js with dependencies loaded.
== other stuff ==
there is a bit more code in js2 that pertains to sequence editing, timed text display and basic image editing. We include a base import of pixastic-lib & pixastic-editor... will work with the pixastic developer to try and ensure upstream compatibility on our usage of the library for in-browser photo and sequence manipulation.
2009-07-14 23:52:14 +00:00
|
|
|
|
2008-05-09 20:18:35 +00:00
|
|
|
/**
|
2018-05-21 21:08:02 +00:00
|
|
|
* Add a JavaScript file to be loaded as `<script>` on this page.
|
|
|
|
|
*
|
2015-10-31 15:16:58 +00:00
|
|
|
* Internal use only. Use OutputPage::addModules() if possible.
|
2010-01-28 21:58:50 +00:00
|
|
|
*
|
2018-05-21 21:08:02 +00:00
|
|
|
* @param string $file URL to file (absolute path, protocol-relative, or full url)
|
2018-06-26 21:14:43 +00:00
|
|
|
* @param string|null $unused Previously used to change the cache-busting query parameter
|
2008-05-09 20:18:35 +00:00
|
|
|
*/
|
OutputPage: Remove appending of wgStyleVersion to legacy resources
For addScriptFile(), just remove the appending of wgStyleVersion.
Going forward, anyone still using this, should simply append a query
parameter themselves in a way that is specific to that one url
(instead of relying on a generic global variable). Alternatively, one
could use OutputPage::transformResourcePath if the file is in /w/.
For addStyles(), also remove the appending of wgStyleVersion. Since this
method takes paths relative to /w/skins, we can easily update this to
automatically use transformResourcePath(), so that file-hash based query
parameters are automatically added.
Test Plan:
* Add calls to top of OutputPage::output():
`$this->addStyle( 'Vector/README.md' );`
`$this->addScriptFile( "{$GLOBALS['wgScriptPath']}/composer.json" );`
* Before, they are both inserted as `<link>` (head) and `<script>` (body)
with a query parameter based on wgStyleVersion.
* After, the `<script>` (end of body) has no query.
After, the stylesheet (head) has a SHA1 content hash as query.
Bug: T181318
Change-Id: Ie5ab5066ef7d07279086bde838d7305e9e4eabaf
2018-05-21 21:21:28 +00:00
|
|
|
public function addScriptFile( $file, $unused = null ) {
|
2019-10-28 05:01:17 +00:00
|
|
|
$this->addScript( Html::linkedScript( $file, $this->CSP->getNonce() ) );
|
2008-05-09 20:18:35 +00:00
|
|
|
}
|
2009-07-15 00:55:58 +00:00
|
|
|
|
2006-12-08 06:09:15 +00:00
|
|
|
/**
|
|
|
|
|
* Add a self-contained script tag with the given contents
|
2015-10-31 15:16:58 +00:00
|
|
|
* Internal use only. Use OutputPage::addModules() if possible.
|
2010-01-28 21:58:50 +00:00
|
|
|
*
|
resourceloader: Move queue formatting out of OutputPage
HTML formatting of the queue was distributed over several OutputPage methods.
Each method demanding a snippet of HTML by calling makeResourceLoaderLink()
with a limited amount of information. As such, makeResourceLoaderLink() was
unable to provide the client with the proper state information.
Centralising it also allows it to better reduce duplication in HTML output
and maintain a more accurate state.
Problems fixed by centralising:
1. The 'user' module is special (due to per-user 'version' and 'user' params).
It is manually requested via script-src. To avoid a separate (and wrong)
request from something that requires it, we set state=loading directly.
However, because the module is in the bottom, the old HTML formatter could
only put state=loading in the bottom also. This sometimes caused a wrong
request to be fired for modules=user if something in the top queue
triggered a requirement for it.
2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose
of allowing dependencies on style modules without risking duplicate loading
on pages where the styles are loaded already. This didn't work, because the
state information about page-style modules is output near the stylesheet,
which is after the script tag with mw.loader.load(). That runs first, and
mw.loader would still make a duplicate request before it learns the state.
Changes:
* Document reasons for style/script tag order in getHeadHtml (per 09537e83).
* Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed
before since a duplicate check in makeResourceLoaderLink() verified the
origin a second time.
* Declare explicit position 'top' on 'user.options' and 'user.tokens' module.
Previously, OutputPage hardcoded them in the top. The new formatter doesn't.
* Remove getHeadScripts().
* Remove getInlineHeadScripts().
* Remove getExternalHeadScripts().
* Remove buildCssLinks().
* Remove getScriptsForBottomQueue().
* Change where Skin::setupSkinUserCss() is called. This methods lets the skin
add modules to the queue. Previously it was called from buildCssLinks(),
via headElement(), via prepareQuickTemplate(), via OutputPage::output().
It's now in OutputPage::output() directly (slightly earlier). This is needed
because prepareQuickTemplate() calls bottomScripts() before headElement().
And bottomScript() would lazy-initialise the queue and lock it before
setupSkinUserCss() is called from headElement().
This makes execution order more predictable instead of being dependent on
the arbitrary order of data extraction in prepareQuickTemplate (which varies
from one skin to another).
* Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early
on so. This avoids wrongful loading and fixes problem 1.
Effective changes in output:
* mw.loader.state() is now before mw.loader.load(). This fixes problem 2.
* mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading".
* mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1.
* The <script async src> tag for 'startup' changed position (slightly).
Previously it was after all inline scripts and stylesheets. It's still after
all inline scripts and after most stylesheets, but before any user styles.
Since the queue is now formatted outside OutputPage, it can't inject the
meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle
of existing output. This shouldn't have any noticable impact.
Bug: T87871
Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
|
|
|
* @param string $script JavaScript text, no script tags
|
2006-12-08 06:09:15 +00:00
|
|
|
*/
|
2010-01-28 21:58:50 +00:00
|
|
|
public function addInlineScript( $script ) {
|
2019-10-28 05:01:17 +00:00
|
|
|
$this->mScripts .= Html::inlineScript( "\n$script\n", $this->CSP->getNonce() ) . "\n";
|
2006-12-08 06:09:15 +00:00
|
|
|
}
|
|
|
|
|
|
2011-02-04 16:39:17 +00:00
|
|
|
/**
|
2011-03-19 21:19:50 +00:00
|
|
|
* 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)
|
2014-04-10 18:50:10 +00:00
|
|
|
* @param array $modules
|
2018-02-12 21:06:05 +00:00
|
|
|
* @param string|null $position Unused
|
2014-04-10 18:50:10 +00:00
|
|
|
* @param string $type
|
|
|
|
|
* @return array
|
2011-02-04 16:39:17 +00:00
|
|
|
*/
|
2014-07-23 02:49:30 +00:00
|
|
|
protected function filterModules( array $modules, $position = null,
|
2014-05-11 01:54:15 +00:00
|
|
|
$type = ResourceLoaderModule::TYPE_COMBINED
|
|
|
|
|
) {
|
2011-02-04 16:39:17 +00:00
|
|
|
$resourceLoader = $this->getResourceLoader();
|
2016-02-17 09:09:32 +00:00
|
|
|
$filteredModules = [];
|
2013-04-17 14:52:47 +00:00
|
|
|
foreach ( $modules as $val ) {
|
2011-02-04 16:39:17 +00:00
|
|
|
$module = $resourceLoader->getModule( $val );
|
2013-04-17 14:52:47 +00:00
|
|
|
if ( $module instanceof ResourceLoaderModule
|
2011-04-07 12:07:25 +00:00
|
|
|
&& $module->getOrigin() <= $this->getAllowedModules( $type )
|
2013-12-01 20:39:00 +00:00
|
|
|
) {
|
2017-02-15 00:25:39 +00:00
|
|
|
if ( $this->mTarget && !in_array( $this->mTarget, $module->getTargets() ) ) {
|
|
|
|
|
$this->warnModuleTargetFilter( $module->getName() );
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2011-02-04 16:39:17 +00:00
|
|
|
$filteredModules[] = $val;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return $filteredModules;
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-15 00:25:39 +00:00
|
|
|
private function warnModuleTargetFilter( $moduleName ) {
|
|
|
|
|
static $warnings = [];
|
|
|
|
|
if ( isset( $warnings[$this->mTarget][$moduleName] ) ) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
$warnings[$this->mTarget][$moduleName] = true;
|
2019-10-16 21:14:46 +00:00
|
|
|
$this->getResourceLoader()->getLogger()->debug(
|
2017-02-15 00:25:39 +00:00
|
|
|
'Module "{module}" not loadable on target "{target}".',
|
|
|
|
|
[
|
|
|
|
|
'module' => $moduleName,
|
|
|
|
|
'target' => $this->mTarget,
|
|
|
|
|
]
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-04 04:00:09 +00:00
|
|
|
/**
|
|
|
|
|
* Get the list of modules to include on this page
|
2010-09-07 21:05:09 +00:00
|
|
|
*
|
2014-04-10 18:50:10 +00:00
|
|
|
* @param bool $filter Whether to filter out insufficiently trustworthy modules
|
2018-02-12 21:06:05 +00:00
|
|
|
* @param string|null $position Unused
|
2014-04-10 18:50:10 +00:00
|
|
|
* @param string $param
|
2017-08-11 18:04:11 +00:00
|
|
|
* @param string $type
|
2014-04-10 18:50:10 +00:00
|
|
|
* @return array Array of module names
|
2010-09-04 04:00:09 +00:00
|
|
|
*/
|
resourceloader: Move queue formatting out of OutputPage
HTML formatting of the queue was distributed over several OutputPage methods.
Each method demanding a snippet of HTML by calling makeResourceLoaderLink()
with a limited amount of information. As such, makeResourceLoaderLink() was
unable to provide the client with the proper state information.
Centralising it also allows it to better reduce duplication in HTML output
and maintain a more accurate state.
Problems fixed by centralising:
1. The 'user' module is special (due to per-user 'version' and 'user' params).
It is manually requested via script-src. To avoid a separate (and wrong)
request from something that requires it, we set state=loading directly.
However, because the module is in the bottom, the old HTML formatter could
only put state=loading in the bottom also. This sometimes caused a wrong
request to be fired for modules=user if something in the top queue
triggered a requirement for it.
2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose
of allowing dependencies on style modules without risking duplicate loading
on pages where the styles are loaded already. This didn't work, because the
state information about page-style modules is output near the stylesheet,
which is after the script tag with mw.loader.load(). That runs first, and
mw.loader would still make a duplicate request before it learns the state.
Changes:
* Document reasons for style/script tag order in getHeadHtml (per 09537e83).
* Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed
before since a duplicate check in makeResourceLoaderLink() verified the
origin a second time.
* Declare explicit position 'top' on 'user.options' and 'user.tokens' module.
Previously, OutputPage hardcoded them in the top. The new formatter doesn't.
* Remove getHeadScripts().
* Remove getInlineHeadScripts().
* Remove getExternalHeadScripts().
* Remove buildCssLinks().
* Remove getScriptsForBottomQueue().
* Change where Skin::setupSkinUserCss() is called. This methods lets the skin
add modules to the queue. Previously it was called from buildCssLinks(),
via headElement(), via prepareQuickTemplate(), via OutputPage::output().
It's now in OutputPage::output() directly (slightly earlier). This is needed
because prepareQuickTemplate() calls bottomScripts() before headElement().
And bottomScript() would lazy-initialise the queue and lock it before
setupSkinUserCss() is called from headElement().
This makes execution order more predictable instead of being dependent on
the arbitrary order of data extraction in prepareQuickTemplate (which varies
from one skin to another).
* Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early
on so. This avoids wrongful loading and fixes problem 1.
Effective changes in output:
* mw.loader.state() is now before mw.loader.load(). This fixes problem 2.
* mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading".
* mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1.
* The <script async src> tag for 'startup' changed position (slightly).
Previously it was after all inline scripts and stylesheets. It's still after
all inline scripts and after most stylesheets, but before any user styles.
Since the queue is now formatted outside OutputPage, it can't inject the
meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle
of existing output. This shouldn't have any noticable impact.
Bug: T87871
Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
|
|
|
public function getModules( $filter = false, $position = null, $param = 'mModules',
|
|
|
|
|
$type = ResourceLoaderModule::TYPE_COMBINED
|
|
|
|
|
) {
|
2011-02-04 16:39:17 +00:00
|
|
|
$modules = array_values( array_unique( $this->$param ) );
|
|
|
|
|
return $filter
|
2018-02-12 21:06:05 +00:00
|
|
|
? $this->filterModules( $modules, null, $type )
|
2011-02-04 16:39:17 +00:00
|
|
|
: $modules;
|
2010-09-04 04:00:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2018-03-02 01:21:32 +00:00
|
|
|
* Load one or more ResourceLoader modules on this page.
|
2010-09-07 21:05:09 +00:00
|
|
|
*
|
2014-04-08 15:29:17 +00:00
|
|
|
* @param string|array $modules Module name (string) or array of module names
|
2010-09-04 04:00:09 +00:00
|
|
|
*/
|
|
|
|
|
public function addModules( $modules ) {
|
|
|
|
|
$this->mModules = array_merge( $this->mModules, (array)$modules );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2018-03-02 01:21:32 +00:00
|
|
|
* Get the list of style-only modules to load on this page.
|
2010-09-07 21:05:09 +00:00
|
|
|
*
|
2014-04-10 18:50:10 +00:00
|
|
|
* @param bool $filter
|
2018-02-12 21:06:05 +00:00
|
|
|
* @param string|null $position Unused
|
2014-04-10 18:50:10 +00:00
|
|
|
* @return array Array of module names
|
2010-09-04 04:00:09 +00:00
|
|
|
*/
|
2011-04-07 12:07:25 +00:00
|
|
|
public function getModuleStyles( $filter = false, $position = null ) {
|
2018-02-12 21:06:05 +00:00
|
|
|
return $this->getModules( $filter, null, 'mModuleStyles',
|
resourceloader: Move queue formatting out of OutputPage
HTML formatting of the queue was distributed over several OutputPage methods.
Each method demanding a snippet of HTML by calling makeResourceLoaderLink()
with a limited amount of information. As such, makeResourceLoaderLink() was
unable to provide the client with the proper state information.
Centralising it also allows it to better reduce duplication in HTML output
and maintain a more accurate state.
Problems fixed by centralising:
1. The 'user' module is special (due to per-user 'version' and 'user' params).
It is manually requested via script-src. To avoid a separate (and wrong)
request from something that requires it, we set state=loading directly.
However, because the module is in the bottom, the old HTML formatter could
only put state=loading in the bottom also. This sometimes caused a wrong
request to be fired for modules=user if something in the top queue
triggered a requirement for it.
2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose
of allowing dependencies on style modules without risking duplicate loading
on pages where the styles are loaded already. This didn't work, because the
state information about page-style modules is output near the stylesheet,
which is after the script tag with mw.loader.load(). That runs first, and
mw.loader would still make a duplicate request before it learns the state.
Changes:
* Document reasons for style/script tag order in getHeadHtml (per 09537e83).
* Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed
before since a duplicate check in makeResourceLoaderLink() verified the
origin a second time.
* Declare explicit position 'top' on 'user.options' and 'user.tokens' module.
Previously, OutputPage hardcoded them in the top. The new formatter doesn't.
* Remove getHeadScripts().
* Remove getInlineHeadScripts().
* Remove getExternalHeadScripts().
* Remove buildCssLinks().
* Remove getScriptsForBottomQueue().
* Change where Skin::setupSkinUserCss() is called. This methods lets the skin
add modules to the queue. Previously it was called from buildCssLinks(),
via headElement(), via prepareQuickTemplate(), via OutputPage::output().
It's now in OutputPage::output() directly (slightly earlier). This is needed
because prepareQuickTemplate() calls bottomScripts() before headElement().
And bottomScript() would lazy-initialise the queue and lock it before
setupSkinUserCss() is called from headElement().
This makes execution order more predictable instead of being dependent on
the arbitrary order of data extraction in prepareQuickTemplate (which varies
from one skin to another).
* Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early
on so. This avoids wrongful loading and fixes problem 1.
Effective changes in output:
* mw.loader.state() is now before mw.loader.load(). This fixes problem 2.
* mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading".
* mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1.
* The <script async src> tag for 'startup' changed position (slightly).
Previously it was after all inline scripts and stylesheets. It's still after
all inline scripts and after most stylesheets, but before any user styles.
Since the queue is now formatted outside OutputPage, it can't inject the
meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle
of existing output. This shouldn't have any noticable impact.
Bug: T87871
Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
|
|
|
ResourceLoaderModule::TYPE_STYLES
|
|
|
|
|
);
|
2010-09-04 04:00:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2018-03-02 01:21:32 +00:00
|
|
|
* Load the styles of one or more ResourceLoader modules on this page.
|
2013-02-27 06:09:26 +00:00
|
|
|
*
|
2018-03-02 01:21:32 +00:00
|
|
|
* Module styles added through this function will be loaded as a stylesheet,
|
|
|
|
|
* using a standard `<link rel=stylesheet>` HTML tag, rather than as a combined
|
|
|
|
|
* Javascript and CSS package. Thus, they will even load when JavaScript is disabled.
|
2010-09-07 21:05:09 +00:00
|
|
|
*
|
2014-04-08 15:29:17 +00:00
|
|
|
* @param string|array $modules Module name (string) or array of module names
|
2010-09-04 04:00:09 +00:00
|
|
|
*/
|
|
|
|
|
public function addModuleStyles( $modules ) {
|
|
|
|
|
$this->mModuleStyles = array_merge( $this->mModuleStyles, (array)$modules );
|
|
|
|
|
}
|
|
|
|
|
|
2013-02-14 17:05:22 +00:00
|
|
|
/**
|
2014-04-08 15:29:17 +00:00
|
|
|
* @return null|string ResourceLoader target
|
2013-02-14 17:05:22 +00:00
|
|
|
*/
|
|
|
|
|
public function getTarget() {
|
|
|
|
|
return $this->mTarget;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Sets ResourceLoader target for load.php links. If null, will be omitted
|
|
|
|
|
*
|
2014-04-10 18:50:10 +00:00
|
|
|
* @param string|null $target
|
2013-02-14 17:05:22 +00:00
|
|
|
*/
|
|
|
|
|
public function setTarget( $target ) {
|
|
|
|
|
$this->mTarget = $target;
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-28 20:52:17 +00:00
|
|
|
/**
|
|
|
|
|
* Add a mapping from a LinkTarget to a Content, for things like page preview.
|
|
|
|
|
* @see self::addContentOverrideCallback()
|
|
|
|
|
* @since 1.32
|
|
|
|
|
* @param LinkTarget $target
|
|
|
|
|
* @param Content $content
|
|
|
|
|
*/
|
|
|
|
|
public function addContentOverride( LinkTarget $target, Content $content ) {
|
|
|
|
|
if ( !$this->contentOverrides ) {
|
|
|
|
|
// Register a callback for $this->contentOverrides on the first call
|
|
|
|
|
$this->addContentOverrideCallback( function ( LinkTarget $target ) {
|
|
|
|
|
$key = $target->getNamespace() . ':' . $target->getDBkey();
|
2017-10-06 22:17:58 +00:00
|
|
|
return $this->contentOverrides[$key] ?? null;
|
2017-02-28 20:52:17 +00:00
|
|
|
} );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$key = $target->getNamespace() . ':' . $target->getDBkey();
|
|
|
|
|
$this->contentOverrides[$key] = $content;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Add a callback for mapping from a Title to a Content object, for things
|
|
|
|
|
* like page preview.
|
|
|
|
|
* @see ResourceLoaderContext::getContentOverrideCallback()
|
|
|
|
|
* @since 1.32
|
|
|
|
|
* @param callable $callback
|
|
|
|
|
*/
|
|
|
|
|
public function addContentOverrideCallback( callable $callback ) {
|
|
|
|
|
$this->contentOverrideCallbacks[] = $callback;
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-02 15:40:03 +00:00
|
|
|
/**
|
|
|
|
|
* Get an array of head items
|
|
|
|
|
*
|
2014-04-10 18:50:10 +00:00
|
|
|
* @return array
|
2011-08-02 15:40:03 +00:00
|
|
|
*/
|
2019-09-14 10:26:31 +00:00
|
|
|
public function getHeadItemsArray() {
|
2011-08-02 15:40:03 +00:00
|
|
|
return $this->mHeadItems;
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-28 21:58:50 +00:00
|
|
|
/**
|
2016-07-25 23:54:13 +00:00
|
|
|
* Add or replace a head item to the output
|
2010-01-28 21:58:50 +00:00
|
|
|
*
|
2015-10-31 15:16:58 +00:00
|
|
|
* Whenever possible, use more specific options like ResourceLoader modules,
|
|
|
|
|
* OutputPage::addLink(), OutputPage::addMetaLink() and OutputPage::addFeedLink()
|
|
|
|
|
* Fallback options for those are: OutputPage::addStyle, OutputPage::addScript(),
|
|
|
|
|
* OutputPage::addInlineScript() and OutputPage::addInlineStyle()
|
|
|
|
|
* This would be your very LAST fallback.
|
|
|
|
|
*
|
2014-07-24 17:42:24 +00:00
|
|
|
* @param string $name Item name
|
|
|
|
|
* @param string $value Raw HTML
|
2010-01-28 21:58:50 +00:00
|
|
|
*/
|
|
|
|
|
public function addHeadItem( $name, $value ) {
|
2007-04-29 10:15:05 +00:00
|
|
|
$this->mHeadItems[$name] = $value;
|
|
|
|
|
}
|
2004-07-08 14:53:54 +00:00
|
|
|
|
2016-07-25 23:54:13 +00:00
|
|
|
/**
|
|
|
|
|
* Add one or more head items to the output
|
|
|
|
|
*
|
|
|
|
|
* @since 1.28
|
2017-08-11 16:09:41 +00:00
|
|
|
* @param string|string[] $values Raw HTML
|
2016-07-25 23:54:13 +00:00
|
|
|
*/
|
|
|
|
|
public function addHeadItems( $values ) {
|
|
|
|
|
$this->mHeadItems = array_merge( $this->mHeadItems, (array)$values );
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-28 21:58:50 +00:00
|
|
|
/**
|
|
|
|
|
* Check if the header item $name is already set
|
|
|
|
|
*
|
2014-04-10 18:50:10 +00:00
|
|
|
* @param string $name Item name
|
|
|
|
|
* @return bool
|
2010-01-28 21:58:50 +00:00
|
|
|
*/
|
|
|
|
|
public function hasHeadItem( $name ) {
|
Basic integrated audio/video support, with Ogg implementation.
* JavaScript video player based loosely on Greg Maxwell's player
* Image page text snippet customisation
* Abstraction of transform parameters in the parser. Introduced Linker::makeImageLink2().
* Made canRender(), mustRender() depend on file, not just on handler. Moved width=0, height=0 checking to ImageHandler::canRender(), since audio streams have width=height=0 but should be rendered.
Also:
* Automatic upgrade for oldimage rows on image page view, allows media handler selection based on oi_*_mime
* oi_*_mime unconditionally referenced, REQUIRES SCHEMA UPGRADE
* Don't destroy file info for missing files on upgrade
* Simple, centralised extension message file handling
* Made MessageCache::loadAllMessages non-static, optimised for repeated-call case due to abuse in User.php
* Support for lightweight parser output hooks, with callback whitelist for security
* Moved Linker::formatSize() to Language, to join the new formatTimePeriod() and formatBitrate()
* Introduced MagicWordArray, regex capture trick requires that magic word IDs DO NOT CONTAIN HYPHENS.
2007-08-15 10:50:09 +00:00
|
|
|
return isset( $this->mHeadItems[$name] );
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-11 07:12:03 +00:00
|
|
|
/**
|
|
|
|
|
* Add a class to the <body> element
|
|
|
|
|
*
|
|
|
|
|
* @since 1.30
|
|
|
|
|
* @param string|string[] $classes One or more classes to add
|
|
|
|
|
*/
|
|
|
|
|
public function addBodyClasses( $classes ) {
|
|
|
|
|
$this->mAdditionalBodyClasses = array_merge( $this->mAdditionalBodyClasses, (array)$classes );
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-28 21:58:50 +00:00
|
|
|
/**
|
|
|
|
|
* Set whether the output should only contain the body of the article,
|
|
|
|
|
* without any skin, sidebar, etc.
|
2010-02-01 18:04:17 +00:00
|
|
|
* Used e.g. when calling with "action=render".
|
2010-01-28 21:58:50 +00:00
|
|
|
*
|
2014-04-08 15:29:17 +00:00
|
|
|
* @param bool $only Whether to output only the body of the article
|
2010-01-28 21:58:50 +00:00
|
|
|
*/
|
|
|
|
|
public function setArticleBodyOnly( $only ) {
|
|
|
|
|
$this->mArticleBodyOnly = $only;
|
2008-08-10 07:14:08 +00:00
|
|
|
}
|
2003-04-14 23:10:40 +00:00
|
|
|
|
2010-01-28 21:58:50 +00:00
|
|
|
/**
|
|
|
|
|
* Return whether the output will contain only the body of the article
|
|
|
|
|
*
|
2014-04-10 18:50:10 +00:00
|
|
|
* @return bool
|
2010-01-28 21:58:50 +00:00
|
|
|
*/
|
|
|
|
|
public function getArticleBodyOnly() {
|
|
|
|
|
return $this->mArticleBodyOnly;
|
2004-04-04 22:33:11 +00:00
|
|
|
}
|
2004-04-04 21:58:05 +00:00
|
|
|
|
2013-02-08 22:58:38 +00:00
|
|
|
/**
|
|
|
|
|
* 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
|
|
|
|
|
*
|
2014-04-08 15:29:17 +00:00
|
|
|
* @param string $name
|
|
|
|
|
* @return mixed Property value or null if not found
|
2013-02-08 22:58:38 +00:00
|
|
|
*/
|
|
|
|
|
public function getProperty( $name ) {
|
2018-06-12 20:44:33 +00:00
|
|
|
return $this->mProperties[$name] ?? null;
|
2013-02-08 22:58:38 +00:00
|
|
|
}
|
|
|
|
|
|
2004-09-02 23:28:24 +00:00
|
|
|
/**
|
|
|
|
|
* checkLastModified tells the client to use the client-cached page if
|
2012-12-01 16:02:15 +00:00
|
|
|
* possible. If successful, the OutputPage is disabled so that
|
2006-11-07 05:37:31 +00:00
|
|
|
* any future call to OutputPage->output() have no effect.
|
|
|
|
|
*
|
2008-08-29 08:40:13 +00:00
|
|
|
* Side effect: sets mLastModified for Last-Modified header
|
|
|
|
|
*
|
2014-04-08 15:29:17 +00:00
|
|
|
* @param string $timestamp
|
2011-05-21 19:07:24 +00:00
|
|
|
*
|
2014-04-08 15:29:17 +00:00
|
|
|
* @return bool True if cache-ok headers was sent.
|
2004-09-02 23:28:24 +00:00
|
|
|
*/
|
2010-01-28 21:58:50 +00:00
|
|
|
public function checkLastModified( $timestamp ) {
|
2005-03-20 03:59:06 +00:00
|
|
|
if ( !$timestamp || $timestamp == '19700101000000' ) {
|
2008-04-16 13:46:16 +00:00
|
|
|
wfDebug( __METHOD__ . ": CACHE DISABLED, NO TIMESTAMP\n" );
|
2008-08-29 08:40:13 +00:00
|
|
|
return false;
|
2005-03-20 03:59:06 +00:00
|
|
|
}
|
2014-08-23 08:52:41 +00:00
|
|
|
$config = $this->getConfig();
|
|
|
|
|
if ( !$config->get( 'CachePages' ) ) {
|
2014-02-06 19:19:09 +00:00
|
|
|
wfDebug( __METHOD__ . ": CACHE DISABLED\n" );
|
2008-08-29 08:40:13 +00:00
|
|
|
return false;
|
2003-07-10 04:55:41 +00:00
|
|
|
}
|
2004-03-20 15:03:26 +00:00
|
|
|
|
2008-08-29 08:40:13 +00:00
|
|
|
$timestamp = wfTimestamp( TS_MW, $timestamp );
|
2016-02-17 09:09:32 +00:00
|
|
|
$modifiedTimes = [
|
2013-04-17 14:52:47 +00:00
|
|
|
'page' => $timestamp,
|
|
|
|
|
'user' => $this->getUser()->getTouched(),
|
2014-08-23 08:52:41 +00:00
|
|
|
'epoch' => $config->get( 'CacheEpoch' )
|
2016-02-17 09:09:32 +00:00
|
|
|
];
|
2017-11-01 20:55:24 +00:00
|
|
|
if ( $config->get( 'UseCdn' ) ) {
|
2018-04-20 15:00:32 +00:00
|
|
|
$modifiedTimes['sepoch'] = wfTimestamp( TS_MW, $this->getCdnCacheEpoch(
|
|
|
|
|
time(),
|
2017-11-01 20:55:24 +00:00
|
|
|
$config->get( 'CdnMaxAge' )
|
2018-04-20 15:00:32 +00:00
|
|
|
) );
|
2013-04-09 20:55:36 +00:00
|
|
|
}
|
2016-05-11 00:13:47 +00:00
|
|
|
Hooks::run( 'OutputPageCheckLastModified', [ &$modifiedTimes, $this ] );
|
2008-04-14 07:45:50 +00:00
|
|
|
|
2008-08-29 08:40:13 +00:00
|
|
|
$maxModified = max( $modifiedTimes );
|
|
|
|
|
$this->mLastModified = wfTimestamp( TS_RFC2822, $maxModified );
|
|
|
|
|
|
2012-07-19 16:35:25 +00:00
|
|
|
$clientHeader = $this->getRequest()->getHeader( 'If-Modified-Since' );
|
|
|
|
|
if ( $clientHeader === false ) {
|
2015-12-30 19:48:01 +00:00
|
|
|
wfDebug( __METHOD__ . ": client did not send If-Modified-Since header", 'private' );
|
2008-08-29 08:40:13 +00:00
|
|
|
return false;
|
2003-04-14 23:10:40 +00:00
|
|
|
}
|
2008-08-29 08:40:13 +00:00
|
|
|
|
|
|
|
|
# IE sends sizes after the date like this:
|
|
|
|
|
# Wed, 20 Aug 2003 06:51:19 GMT; length=5202
|
|
|
|
|
# this breaks strtotime().
|
2012-07-19 16:35:25 +00:00
|
|
|
$clientHeader = preg_replace( '/;.*$/', '', $clientHeader );
|
2008-08-29 08:40:13 +00:00
|
|
|
|
2018-07-23 18:26:32 +00:00
|
|
|
Wikimedia\suppressWarnings(); // E_STRICT system time warnings
|
2008-12-23 19:39:00 +00:00
|
|
|
$clientHeaderTime = strtotime( $clientHeader );
|
2018-02-10 07:52:26 +00:00
|
|
|
Wikimedia\restoreWarnings();
|
2008-12-23 19:39:00 +00:00
|
|
|
if ( !$clientHeaderTime ) {
|
2014-05-11 01:54:15 +00:00
|
|
|
wfDebug( __METHOD__
|
|
|
|
|
. ": unable to parse the client's If-Modified-Since header: $clientHeader\n" );
|
2008-08-29 08:40:13 +00:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
$clientHeaderTime = wfTimestamp( TS_MW, $clientHeaderTime );
|
|
|
|
|
|
2012-07-19 16:35:25 +00:00
|
|
|
# Make debug info
|
|
|
|
|
$info = '';
|
|
|
|
|
foreach ( $modifiedTimes as $name => $value ) {
|
|
|
|
|
if ( $info !== '' ) {
|
|
|
|
|
$info .= ', ';
|
|
|
|
|
}
|
|
|
|
|
$info .= "$name=" . wfTimestamp( TS_ISO_8601, $value );
|
|
|
|
|
}
|
|
|
|
|
|
here it is ... the upload-api, script-server, js2 (javascript phase2) branch merge 1st attempt.
Here is a short overview of changes and associated default configuration variables (most everything is off by default) also see ~soon to be updated~: http://www.mediawiki.org/wiki/Media_Projects_Overview
= Upload Improvements =
==Upload API ==
* Based on the early work of Bryan Tong and others it adds the upload option to the api.
* We rewrite Special:Upload page to include use the new refactoring
* Added in token checks in both the SpecialUpload.php page so avoids DOS / xss copy-by-url JavaScript based cross site POST file submissions
== Copy by URL==
$wgAllowCopyUploads = false;
* http class rewrite includes a new http background download see: includes/HttpFunctions.php
* spins off a php process that calls: maintenance/http_session_download.php
* pushes updates to the session and gives the user a progress bar on http copy uploads from other server progress (using js2 upload interface) (if not using the js2 upload interface it does the request in-place but the download is limited to the php ini timeout time)
== Firefogg ==
* Firefogg enables resumable upload by chunks
* progress indicators and conditional invokation (js2 system)
* and of-course client side transcoding.
= Script Server =
$wgEnableScriptLoader = false;
* off by default if $wgEnableScriptLoader is turned on script files are grouped, gziped, cached etc.
for more info see: http://www.mediawiki.org/wiki/Extension:ScriptLoader
* Includes some early skin js include fixes (skin/script system still lots of love)
* Includes a "javascript class autoloader" this is packaged into mwEmbed so that the mwEmbed library can work in stand alone mode (while retaining localization and script serving) (one such application is the make page for firefogg.org : http://www.firefogg.org/make/index.html )
* The file that contains the autojavascript loading classes is: js2/php/jsAutoloadLocalClasses.php
* One can use this auto class loading dependency system with extensions and add-ons but I need to better document that.
= js2 system / mwEmbed=
$wgEnableJS2system = false
* includes initial rewrite towards more jquery based javascript code
* especially for the Special:Upload page.
* Also the edit page include support for the "add-media-wizard"
* includes dependency loader for javascript that optionally takes advantage of the script-loader
* remote embedding of javascript interfaces (like embedding video, or commons media searching)
* $wgDebugJavaScript = false; .. .this variable lets you always get "always fresh javascript". When used with the script-loader it does not minify the script-loader output.
= mwEmbed =
* Will commit a separate patch to oggHandler that conditionally outputs <video tag> to use the new javascript video player.
** mv_embed player includes: play-head, volume control, remote embedding, oggz-chop support across plugins.
* add-media-wizard adds easy inserts of media to pages (with import)
== jQuery==
* we include a base install of jQuery, jQuery ui and some plugins.
* all the javascript classes are in the scriptloader so its easy to load any set of jquery ui components that you may need using the script-server. You get a callback so you can then execute js with dependencies loaded.
== other stuff ==
there is a bit more code in js2 that pertains to sequence editing, timed text display and basic image editing. We include a base import of pixastic-lib & pixastic-editor... will work with the pixastic developer to try and ensure upstream compatibility on our usage of the library for in-browser photo and sequence manipulation.
2009-07-14 23:52:14 +00:00
|
|
|
wfDebug( __METHOD__ . ": client sent If-Modified-Since: " .
|
2015-12-30 19:48:01 +00:00
|
|
|
wfTimestamp( TS_ISO_8601, $clientHeaderTime ), 'private' );
|
here it is ... the upload-api, script-server, js2 (javascript phase2) branch merge 1st attempt.
Here is a short overview of changes and associated default configuration variables (most everything is off by default) also see ~soon to be updated~: http://www.mediawiki.org/wiki/Media_Projects_Overview
= Upload Improvements =
==Upload API ==
* Based on the early work of Bryan Tong and others it adds the upload option to the api.
* We rewrite Special:Upload page to include use the new refactoring
* Added in token checks in both the SpecialUpload.php page so avoids DOS / xss copy-by-url JavaScript based cross site POST file submissions
== Copy by URL==
$wgAllowCopyUploads = false;
* http class rewrite includes a new http background download see: includes/HttpFunctions.php
* spins off a php process that calls: maintenance/http_session_download.php
* pushes updates to the session and gives the user a progress bar on http copy uploads from other server progress (using js2 upload interface) (if not using the js2 upload interface it does the request in-place but the download is limited to the php ini timeout time)
== Firefogg ==
* Firefogg enables resumable upload by chunks
* progress indicators and conditional invokation (js2 system)
* and of-course client side transcoding.
= Script Server =
$wgEnableScriptLoader = false;
* off by default if $wgEnableScriptLoader is turned on script files are grouped, gziped, cached etc.
for more info see: http://www.mediawiki.org/wiki/Extension:ScriptLoader
* Includes some early skin js include fixes (skin/script system still lots of love)
* Includes a "javascript class autoloader" this is packaged into mwEmbed so that the mwEmbed library can work in stand alone mode (while retaining localization and script serving) (one such application is the make page for firefogg.org : http://www.firefogg.org/make/index.html )
* The file that contains the autojavascript loading classes is: js2/php/jsAutoloadLocalClasses.php
* One can use this auto class loading dependency system with extensions and add-ons but I need to better document that.
= js2 system / mwEmbed=
$wgEnableJS2system = false
* includes initial rewrite towards more jquery based javascript code
* especially for the Special:Upload page.
* Also the edit page include support for the "add-media-wizard"
* includes dependency loader for javascript that optionally takes advantage of the script-loader
* remote embedding of javascript interfaces (like embedding video, or commons media searching)
* $wgDebugJavaScript = false; .. .this variable lets you always get "always fresh javascript". When used with the script-loader it does not minify the script-loader output.
= mwEmbed =
* Will commit a separate patch to oggHandler that conditionally outputs <video tag> to use the new javascript video player.
** mv_embed player includes: play-head, volume control, remote embedding, oggz-chop support across plugins.
* add-media-wizard adds easy inserts of media to pages (with import)
== jQuery==
* we include a base install of jQuery, jQuery ui and some plugins.
* all the javascript classes are in the scriptloader so its easy to load any set of jquery ui components that you may need using the script-server. You get a callback so you can then execute js with dependencies loaded.
== other stuff ==
there is a bit more code in js2 that pertains to sequence editing, timed text display and basic image editing. We include a base import of pixastic-lib & pixastic-editor... will work with the pixastic developer to try and ensure upstream compatibility on our usage of the library for in-browser photo and sequence manipulation.
2009-07-14 23:52:14 +00:00
|
|
|
wfDebug( __METHOD__ . ": effective Last-Modified: " .
|
2015-12-30 19:48:01 +00:00
|
|
|
wfTimestamp( TS_ISO_8601, $maxModified ), 'private' );
|
2013-04-17 14:52:47 +00:00
|
|
|
if ( $clientHeaderTime < $maxModified ) {
|
2015-12-30 19:48:01 +00:00
|
|
|
wfDebug( __METHOD__ . ": STALE, $info", 'private' );
|
2008-08-29 08:40:13 +00:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-23 19:39:00 +00:00
|
|
|
# Not modified
|
2015-05-24 12:31:11 +00:00
|
|
|
# Give a 304 Not Modified response code and disable body output
|
2015-12-30 19:48:01 +00:00
|
|
|
wfDebug( __METHOD__ . ": NOT MODIFIED, $info", 'private' );
|
2010-05-22 12:18:22 +00:00
|
|
|
ini_set( 'zlib.output_compression', 0 );
|
2015-05-24 12:31:11 +00:00
|
|
|
$this->getRequest()->response()->statusHeader( 304 );
|
2008-08-29 08:40:13 +00:00
|
|
|
$this->sendCacheControl();
|
|
|
|
|
$this->disable();
|
|
|
|
|
|
|
|
|
|
// Don't output a compressed blob when using ob_gzhandler;
|
|
|
|
|
// it's technically against HTTP spec and seems to confuse
|
|
|
|
|
// Firefox when the response gets split over two packets.
|
|
|
|
|
wfClearOutputBuffers();
|
|
|
|
|
|
|
|
|
|
return true;
|
2003-04-14 23:10:40 +00:00
|
|
|
}
|
|
|
|
|
|
2018-04-20 15:00:32 +00:00
|
|
|
/**
|
|
|
|
|
* @param int $reqTime Time of request (eg. now)
|
|
|
|
|
* @param int $maxAge Cache TTL in seconds
|
|
|
|
|
* @return int Timestamp
|
|
|
|
|
*/
|
|
|
|
|
private function getCdnCacheEpoch( $reqTime, $maxAge ) {
|
2017-11-01 20:55:24 +00:00
|
|
|
// Ensure Last-Modified is never more than $wgCdnMaxAge in the past,
|
2018-04-20 15:00:32 +00:00
|
|
|
// because even if the wiki page content hasn't changed since, static
|
|
|
|
|
// resources may have changed (skin HTML, interface messages, urls, etc.)
|
|
|
|
|
// and must roll-over in a timely manner (T46570)
|
|
|
|
|
return $reqTime - $maxAge;
|
|
|
|
|
}
|
|
|
|
|
|
2010-04-05 16:14:58 +00:00
|
|
|
/**
|
|
|
|
|
* Override the last modified timestamp
|
|
|
|
|
*
|
2014-07-24 17:42:24 +00:00
|
|
|
* @param string $timestamp New timestamp, in a format readable by
|
2010-04-05 16:14:58 +00:00
|
|
|
* wfTimestamp()
|
|
|
|
|
*/
|
|
|
|
|
public function setLastModified( $timestamp ) {
|
|
|
|
|
$this->mLastModified = wfTimestamp( TS_RFC2822, $timestamp );
|
|
|
|
|
}
|
|
|
|
|
|
2008-07-23 19:05:43 +00:00
|
|
|
/**
|
|
|
|
|
* Set the robot policy for the page: <http://www.robotstxt.org/meta.html>
|
|
|
|
|
*
|
2014-07-24 17:42:24 +00:00
|
|
|
* @param string $policy The literal string to output as the contents of
|
2013-12-02 21:20:11 +00:00
|
|
|
* the meta tag. Will be parsed according to the spec and output in
|
|
|
|
|
* standardized form.
|
2008-07-23 19:05:43 +00:00
|
|
|
* @return null
|
|
|
|
|
*/
|
|
|
|
|
public function setRobotPolicy( $policy ) {
|
2009-08-31 19:19:12 +00:00
|
|
|
$policy = Article::formatRobotPolicy( $policy );
|
2008-07-23 19:05:43 +00:00
|
|
|
|
2013-04-17 14:52:47 +00:00
|
|
|
if ( isset( $policy['index'] ) ) {
|
2009-08-31 19:19:12 +00:00
|
|
|
$this->setIndexPolicy( $policy['index'] );
|
2010-01-28 21:58:50 +00:00
|
|
|
}
|
2013-04-17 14:52:47 +00:00
|
|
|
if ( isset( $policy['follow'] ) ) {
|
2009-08-31 19:19:12 +00:00
|
|
|
$this->setFollowPolicy( $policy['follow'] );
|
2010-01-28 21:58:50 +00:00
|
|
|
}
|
2008-07-23 19:05:43 +00:00
|
|
|
}
|
|
|
|
|
|
2019-12-11 12:23:51 +00:00
|
|
|
/**
|
|
|
|
|
* Get the current robot policy for the page as a string in the form
|
|
|
|
|
* <index policy>,<follow policy>.
|
|
|
|
|
*
|
|
|
|
|
* @return string
|
|
|
|
|
*/
|
|
|
|
|
public function getRobotPolicy() {
|
|
|
|
|
return "{$this->mIndexPolicy},{$this->mFollowPolicy}";
|
|
|
|
|
}
|
|
|
|
|
|
2008-07-23 19:05:43 +00:00
|
|
|
/**
|
|
|
|
|
* Set the index policy for the page, but leave the follow policy un-
|
|
|
|
|
* touched.
|
|
|
|
|
*
|
2013-03-11 17:15:01 +00:00
|
|
|
* @param string $policy Either 'index' or 'noindex'.
|
2008-07-23 19:05:43 +00:00
|
|
|
* @return null
|
|
|
|
|
*/
|
|
|
|
|
public function setIndexPolicy( $policy ) {
|
|
|
|
|
$policy = trim( $policy );
|
2016-02-17 09:09:32 +00:00
|
|
|
if ( in_array( $policy, [ 'index', 'noindex' ] ) ) {
|
2008-07-23 19:05:43 +00:00
|
|
|
$this->mIndexPolicy = $policy;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-11 12:23:51 +00:00
|
|
|
/**
|
|
|
|
|
* Get the current index policy for the page as a string.
|
|
|
|
|
*
|
|
|
|
|
* @return string
|
|
|
|
|
*/
|
|
|
|
|
public function getIndexPolicy() {
|
|
|
|
|
return $this->mIndexPolicy;
|
|
|
|
|
}
|
|
|
|
|
|
2008-07-23 19:05:43 +00:00
|
|
|
/**
|
|
|
|
|
* Set the follow policy for the page, but leave the index policy un-
|
|
|
|
|
* touched.
|
|
|
|
|
*
|
2014-07-24 17:42:24 +00:00
|
|
|
* @param string $policy Either 'follow' or 'nofollow'.
|
2008-07-23 19:05:43 +00:00
|
|
|
* @return null
|
|
|
|
|
*/
|
|
|
|
|
public function setFollowPolicy( $policy ) {
|
|
|
|
|
$policy = trim( $policy );
|
2016-02-17 09:09:32 +00:00
|
|
|
if ( in_array( $policy, [ 'follow', 'nofollow' ] ) ) {
|
2008-07-23 19:05:43 +00:00
|
|
|
$this->mFollowPolicy = $policy;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-11 12:23:51 +00:00
|
|
|
/**
|
|
|
|
|
* Get the current follow policy for the page as a string.
|
|
|
|
|
*
|
|
|
|
|
* @return string
|
|
|
|
|
*/
|
|
|
|
|
public function getFollowPolicy() {
|
|
|
|
|
return $this->mFollowPolicy;
|
|
|
|
|
}
|
|
|
|
|
|
2009-04-09 05:15:43 +00:00
|
|
|
/**
|
2012-07-10 12:48:06 +00:00
|
|
|
* "HTML title" means the contents of "<title>".
|
2010-04-10 05:46:01 +00:00
|
|
|
* It is stored as plain, unescaped text and will be run through htmlspecialchars in the skin file.
|
2011-05-21 19:07:24 +00:00
|
|
|
*
|
2014-07-27 15:21:01 +00:00
|
|
|
* @param string|Message $name
|
2009-04-09 05:15:43 +00:00
|
|
|
*/
|
2010-04-10 13:38:50 +00:00
|
|
|
public function setHTMLTitle( $name ) {
|
2011-10-27 20:23:16 +00:00
|
|
|
if ( $name instanceof Message ) {
|
|
|
|
|
$this->mHTMLtitle = $name->setContext( $this->getContext() )->text();
|
|
|
|
|
} else {
|
|
|
|
|
$this->mHTMLtitle = $name;
|
|
|
|
|
}
|
2011-10-24 17:24:38 +00:00
|
|
|
}
|
|
|
|
|
|
2010-01-28 21:58:50 +00:00
|
|
|
/**
|
2012-07-10 12:48:06 +00:00
|
|
|
* Return the "HTML title", i.e. the content of the "<title>" tag.
|
2010-01-28 21:58:50 +00:00
|
|
|
*
|
2014-04-10 18:50:10 +00:00
|
|
|
* @return string
|
2010-01-28 21:58:50 +00:00
|
|
|
*/
|
|
|
|
|
public function getHTMLTitle() {
|
|
|
|
|
return $this->mHTMLtitle;
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-30 12:43:10 +00:00
|
|
|
/**
|
|
|
|
|
* Set $mRedirectedFrom, the Title of the page which redirected us to the current page.
|
|
|
|
|
*
|
2014-04-10 18:50:10 +00:00
|
|
|
* @param Title $t
|
2011-11-30 12:43:10 +00:00
|
|
|
*/
|
|
|
|
|
public function setRedirectedFrom( $t ) {
|
|
|
|
|
$this->mRedirectedFrom = $t;
|
|
|
|
|
}
|
|
|
|
|
|
2009-04-09 05:15:43 +00:00
|
|
|
/**
|
2014-05-11 01:54:15 +00:00
|
|
|
* "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.
|
2011-05-21 19:07:24 +00:00
|
|
|
*
|
2014-04-10 18:50:10 +00:00
|
|
|
* @param string|Message $name
|
2019-02-17 11:32:50 +00:00
|
|
|
* @param-taint $name tainted
|
|
|
|
|
* Phan-taint-check gets very confused by $name being either a string or a Message
|
2009-11-14 21:27:13 +00:00
|
|
|
*/
|
2006-11-07 05:37:31 +00:00
|
|
|
public function setPageTitle( $name ) {
|
2011-10-27 20:23:16 +00:00
|
|
|
if ( $name instanceof Message ) {
|
|
|
|
|
$name = $name->setContext( $this->getContext() )->text();
|
|
|
|
|
}
|
|
|
|
|
|
2009-04-09 05:15:43 +00:00
|
|
|
# change "<script>foo&bar</script>" to "<script>foo&bar</script>"
|
|
|
|
|
# but leave "<i>foobar</i>" alone
|
|
|
|
|
$nameWithTags = Sanitizer::normalizeCharReferences( Sanitizer::removeHTMLtags( $name ) );
|
2018-07-24 13:38:44 +00:00
|
|
|
$this->mPageTitle = $nameWithTags;
|
2009-02-04 19:43:30 +00:00
|
|
|
|
2009-04-09 05:15:43 +00:00
|
|
|
# change "<i>foo&bar</i>" to "foo&bar"
|
2013-11-12 08:36:00 +00:00
|
|
|
$this->setHTMLTitle(
|
2019-02-17 11:32:50 +00:00
|
|
|
$this->msg( 'pagetitle' )->plaintextParams( Sanitizer::stripAllTags( $nameWithTags ) )
|
2013-11-12 08:36:00 +00:00
|
|
|
->inContentLanguage()
|
|
|
|
|
);
|
2004-04-25 00:32:24 +00:00
|
|
|
}
|
here it is ... the upload-api, script-server, js2 (javascript phase2) branch merge 1st attempt.
Here is a short overview of changes and associated default configuration variables (most everything is off by default) also see ~soon to be updated~: http://www.mediawiki.org/wiki/Media_Projects_Overview
= Upload Improvements =
==Upload API ==
* Based on the early work of Bryan Tong and others it adds the upload option to the api.
* We rewrite Special:Upload page to include use the new refactoring
* Added in token checks in both the SpecialUpload.php page so avoids DOS / xss copy-by-url JavaScript based cross site POST file submissions
== Copy by URL==
$wgAllowCopyUploads = false;
* http class rewrite includes a new http background download see: includes/HttpFunctions.php
* spins off a php process that calls: maintenance/http_session_download.php
* pushes updates to the session and gives the user a progress bar on http copy uploads from other server progress (using js2 upload interface) (if not using the js2 upload interface it does the request in-place but the download is limited to the php ini timeout time)
== Firefogg ==
* Firefogg enables resumable upload by chunks
* progress indicators and conditional invokation (js2 system)
* and of-course client side transcoding.
= Script Server =
$wgEnableScriptLoader = false;
* off by default if $wgEnableScriptLoader is turned on script files are grouped, gziped, cached etc.
for more info see: http://www.mediawiki.org/wiki/Extension:ScriptLoader
* Includes some early skin js include fixes (skin/script system still lots of love)
* Includes a "javascript class autoloader" this is packaged into mwEmbed so that the mwEmbed library can work in stand alone mode (while retaining localization and script serving) (one such application is the make page for firefogg.org : http://www.firefogg.org/make/index.html )
* The file that contains the autojavascript loading classes is: js2/php/jsAutoloadLocalClasses.php
* One can use this auto class loading dependency system with extensions and add-ons but I need to better document that.
= js2 system / mwEmbed=
$wgEnableJS2system = false
* includes initial rewrite towards more jquery based javascript code
* especially for the Special:Upload page.
* Also the edit page include support for the "add-media-wizard"
* includes dependency loader for javascript that optionally takes advantage of the script-loader
* remote embedding of javascript interfaces (like embedding video, or commons media searching)
* $wgDebugJavaScript = false; .. .this variable lets you always get "always fresh javascript". When used with the script-loader it does not minify the script-loader output.
= mwEmbed =
* Will commit a separate patch to oggHandler that conditionally outputs <video tag> to use the new javascript video player.
** mv_embed player includes: play-head, volume control, remote embedding, oggz-chop support across plugins.
* add-media-wizard adds easy inserts of media to pages (with import)
== jQuery==
* we include a base install of jQuery, jQuery ui and some plugins.
* all the javascript classes are in the scriptloader so its easy to load any set of jquery ui components that you may need using the script-server. You get a callback so you can then execute js with dependencies loaded.
== other stuff ==
there is a bit more code in js2 that pertains to sequence editing, timed text display and basic image editing. We include a base import of pixastic-lib & pixastic-editor... will work with the pixastic developer to try and ensure upstream compatibility on our usage of the library for in-browser photo and sequence manipulation.
2009-07-14 23:52:14 +00:00
|
|
|
|
2010-01-28 21:58:50 +00:00
|
|
|
/**
|
2010-02-02 16:26:52 +00:00
|
|
|
* Return the "page title", i.e. the content of the \<h1\> tag.
|
2010-01-28 21:58:50 +00:00
|
|
|
*
|
2014-04-10 18:50:10 +00:00
|
|
|
* @return string
|
2010-01-28 21:58:50 +00:00
|
|
|
*/
|
|
|
|
|
public function getPageTitle() {
|
2018-07-24 13:38:44 +00:00
|
|
|
return $this->mPageTitle;
|
2010-01-28 21:58:50 +00:00
|
|
|
}
|
|
|
|
|
|
2016-11-09 06:24:57 +00:00
|
|
|
/**
|
|
|
|
|
* Same as page title but only contains name of the page, not any other text.
|
|
|
|
|
*
|
|
|
|
|
* @since 1.32
|
|
|
|
|
* @param string $html Page title text.
|
|
|
|
|
* @see OutputPage::setPageTitle
|
|
|
|
|
*/
|
|
|
|
|
public function setDisplayTitle( $html ) {
|
|
|
|
|
$this->displayTitle = $html;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns page display title.
|
|
|
|
|
*
|
|
|
|
|
* Performs some normalization, but this not as strict the magic word.
|
|
|
|
|
*
|
|
|
|
|
* @since 1.32
|
|
|
|
|
* @return string HTML
|
|
|
|
|
*/
|
|
|
|
|
public function getDisplayTitle() {
|
|
|
|
|
$html = $this->displayTitle;
|
|
|
|
|
if ( $html === null ) {
|
|
|
|
|
$html = $this->getTitle()->getPrefixedText();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Sanitizer::normalizeCharReferences( Sanitizer::removeHTMLtags( $html ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns page display title without namespace prefix if possible.
|
|
|
|
|
*
|
|
|
|
|
* @since 1.32
|
|
|
|
|
* @return string HTML
|
|
|
|
|
*/
|
|
|
|
|
public function getUnprefixedDisplayTitle() {
|
|
|
|
|
$text = $this->getDisplayTitle();
|
|
|
|
|
$nsPrefix = $this->getTitle()->getNsText() . ':';
|
|
|
|
|
$prefix = preg_quote( $nsPrefix, '/' );
|
|
|
|
|
|
|
|
|
|
return preg_replace( "/^$prefix/i", '', $text );
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-03 03:59:47 +00:00
|
|
|
/**
|
2011-04-03 10:41:14 +00:00
|
|
|
* Set the Title object to use
|
2011-04-03 03:59:47 +00:00
|
|
|
*
|
2014-04-10 18:50:10 +00:00
|
|
|
* @param Title $t
|
2011-04-03 03:59:47 +00:00
|
|
|
*/
|
2011-07-28 17:48:35 +00:00
|
|
|
public function setTitle( Title $t ) {
|
2019-08-31 16:14:38 +00:00
|
|
|
// @phan-suppress-next-next-line PhanUndeclaredMethod
|
|
|
|
|
// @fixme Not all implementations of IContextSource have this method!
|
2011-04-18 12:43:53 +00:00
|
|
|
$this->getContext()->setTitle( $t );
|
2011-04-03 10:41:14 +00:00
|
|
|
}
|
|
|
|
|
|
2010-01-28 21:58:50 +00:00
|
|
|
/**
|
2013-03-13 07:42:41 +00:00
|
|
|
* Replace the subtitle with $str
|
2010-01-28 21:58:50 +00:00
|
|
|
*
|
2014-07-24 17:42:24 +00:00
|
|
|
* @param string|Message $str New value of the subtitle. String should be safe HTML.
|
2010-01-28 21:58:50 +00:00
|
|
|
*/
|
|
|
|
|
public function setSubtitle( $str ) {
|
2011-11-08 18:02:26 +00:00
|
|
|
$this->clearSubtitle();
|
|
|
|
|
$this->addSubtitle( $str );
|
2010-01-28 21:58:50 +00:00
|
|
|
}
|
|
|
|
|
|
2011-11-08 18:02:26 +00:00
|
|
|
/**
|
|
|
|
|
* Add $str to the subtitle
|
|
|
|
|
*
|
2014-07-24 17:42:24 +00:00
|
|
|
* @param string|Message $str String or Message to add to the subtitle. String should be safe HTML.
|
2011-11-08 18:02:26 +00:00
|
|
|
*/
|
|
|
|
|
public function addSubtitle( $str ) {
|
|
|
|
|
if ( $str instanceof Message ) {
|
|
|
|
|
$this->mSubtitle[] = $str->setContext( $this->getContext() )->parse();
|
|
|
|
|
} else {
|
|
|
|
|
$this->mSubtitle[] = $str;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2014-08-03 20:04:45 +00:00
|
|
|
* Build message object for a subtitle containing a backlink to a page
|
2011-11-08 18:02:26 +00:00
|
|
|
*
|
2014-04-10 18:50:10 +00:00
|
|
|
* @param Title $title Title to link to
|
2014-08-07 08:22:57 +00:00
|
|
|
* @param array $query Array of additional parameters to include in the link
|
2014-08-03 20:04:45 +00:00
|
|
|
* @return Message
|
|
|
|
|
* @since 1.25
|
2011-11-08 18:02:26 +00:00
|
|
|
*/
|
2016-02-17 09:09:32 +00:00
|
|
|
public static function buildBacklinkSubtitle( Title $title, $query = [] ) {
|
2011-11-08 18:02:26 +00:00
|
|
|
if ( $title->isRedirect() ) {
|
|
|
|
|
$query['redirect'] = 'no';
|
|
|
|
|
}
|
2016-11-30 21:28:55 +00:00
|
|
|
$linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
|
2014-08-03 20:04:45 +00:00
|
|
|
return wfMessage( 'backlinksubtitle' )
|
2016-11-30 21:28:55 +00:00
|
|
|
->rawParams( $linkRenderer->makeLink( $title, null, [], $query ) );
|
2014-08-03 20:04:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Add a subtitle containing a backlink to a page
|
|
|
|
|
*
|
|
|
|
|
* @param Title $title Title to link to
|
|
|
|
|
* @param array $query Array of additional parameters to include in the link
|
|
|
|
|
*/
|
2016-02-17 09:09:32 +00:00
|
|
|
public function addBacklinkSubtitle( Title $title, $query = [] ) {
|
2014-08-03 20:04:45 +00:00
|
|
|
$this->addSubtitle( self::buildBacklinkSubtitle( $title, $query ) );
|
2011-11-08 18:02:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Clear the subtitles
|
|
|
|
|
*/
|
|
|
|
|
public function clearSubtitle() {
|
2016-02-17 09:09:32 +00:00
|
|
|
$this->mSubtitle = [];
|
2010-01-28 21:58:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the subtitle
|
|
|
|
|
*
|
2014-04-10 18:50:10 +00:00
|
|
|
* @return string
|
2010-01-28 21:58:50 +00:00
|
|
|
*/
|
|
|
|
|
public function getSubtitle() {
|
2011-11-08 18:02:26 +00:00
|
|
|
return implode( "<br />\n\t\t\t\t", $this->mSubtitle );
|
2010-01-28 21:58:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2014-12-16 00:41:45 +00:00
|
|
|
* Set the page as printable, i.e. it'll be displayed with all
|
2010-01-28 21:58:50 +00:00
|
|
|
* print styles included
|
|
|
|
|
*/
|
|
|
|
|
public function setPrintable() {
|
|
|
|
|
$this->mPrintable = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return whether the page is "printable"
|
|
|
|
|
*
|
2014-04-10 18:50:10 +00:00
|
|
|
* @return bool
|
2010-01-28 21:58:50 +00:00
|
|
|
*/
|
|
|
|
|
public function isPrintable() {
|
|
|
|
|
return $this->mPrintable;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Disable output completely, i.e. calling output() will have no effect
|
|
|
|
|
*/
|
|
|
|
|
public function disable() {
|
|
|
|
|
$this->mDoNothing = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return whether the output will be completely disabled
|
|
|
|
|
*
|
2014-04-10 18:50:10 +00:00
|
|
|
* @return bool
|
2010-01-28 21:58:50 +00:00
|
|
|
*/
|
|
|
|
|
public function isDisabled() {
|
|
|
|
|
return $this->mDoNothing;
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-29 21:32:45 +00:00
|
|
|
/**
|
|
|
|
|
* Show an "add new section" link?
|
|
|
|
|
*
|
2014-04-10 18:50:10 +00:00
|
|
|
* @return bool
|
2010-01-29 21:32:45 +00:00
|
|
|
*/
|
|
|
|
|
public function showNewSectionLink() {
|
|
|
|
|
return $this->mNewSectionLink;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Forcibly hide the new section link?
|
|
|
|
|
*
|
2014-04-10 18:50:10 +00:00
|
|
|
* @return bool
|
2010-01-29 21:32:45 +00:00
|
|
|
*/
|
|
|
|
|
public function forceHideNewSectionLink() {
|
|
|
|
|
return $this->mHideNewSectionLink;
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-26 20:47:34 +00:00
|
|
|
/**
|
|
|
|
|
* Add or remove feed links in the page header
|
|
|
|
|
* This is mainly kept for backward compatibility, see OutputPage::addFeedLink()
|
|
|
|
|
* for the new version
|
|
|
|
|
* @see addFeedLink()
|
|
|
|
|
*
|
2014-07-24 17:42:24 +00:00
|
|
|
* @param bool $show True: add default feeds, false: remove all feeds
|
2010-01-26 20:47:34 +00:00
|
|
|
*/
|
2010-01-13 20:23:14 +00:00
|
|
|
public function setSyndicated( $show = true ) {
|
|
|
|
|
if ( $show ) {
|
|
|
|
|
$this->setFeedAppendQuery( false );
|
|
|
|
|
} else {
|
2016-02-17 09:09:32 +00:00
|
|
|
$this->mFeedLinks = [];
|
2010-01-13 20:23:14 +00:00
|
|
|
}
|
|
|
|
|
}
|
2009-10-24 21:32:53 +00:00
|
|
|
|
2018-01-28 02:21:51 +00:00
|
|
|
/**
|
|
|
|
|
* Return effective list of advertised feed types
|
|
|
|
|
* @see addFeedLink()
|
|
|
|
|
*
|
|
|
|
|
* @return array Array of feed type names ( 'rss', 'atom' )
|
|
|
|
|
*/
|
|
|
|
|
protected function getAdvertisedFeedTypes() {
|
|
|
|
|
if ( $this->getConfig()->get( 'Feed' ) ) {
|
|
|
|
|
return $this->getConfig()->get( 'AdvertisedFeedTypes' );
|
|
|
|
|
} else {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-26 20:47:34 +00:00
|
|
|
/**
|
|
|
|
|
* Add default feeds to the page header
|
|
|
|
|
* This is mainly kept for backward compatibility, see OutputPage::addFeedLink()
|
|
|
|
|
* for the new version
|
|
|
|
|
* @see addFeedLink()
|
|
|
|
|
*
|
2014-07-24 17:42:24 +00:00
|
|
|
* @param string $val Query to append to feed links or false to output
|
2010-01-26 20:47:34 +00:00
|
|
|
* default links
|
|
|
|
|
*/
|
2009-09-30 17:35:41 +00:00
|
|
|
public function setFeedAppendQuery( $val ) {
|
2016-02-17 09:09:32 +00:00
|
|
|
$this->mFeedLinks = [];
|
2009-10-24 21:32:53 +00:00
|
|
|
|
2018-01-28 02:21:51 +00:00
|
|
|
foreach ( $this->getAdvertisedFeedTypes() as $type ) {
|
2010-01-13 20:23:14 +00:00
|
|
|
$query = "feed=$type";
|
2010-01-13 21:29:47 +00:00
|
|
|
if ( is_string( $val ) ) {
|
2010-01-13 20:23:14 +00:00
|
|
|
$query .= '&' . $val;
|
2010-01-13 21:29:47 +00:00
|
|
|
}
|
2009-09-30 17:35:41 +00:00
|
|
|
$this->mFeedLinks[$type] = $this->getTitle()->getLocalURL( $query );
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-10-24 21:32:53 +00:00
|
|
|
|
2010-01-26 20:47:34 +00:00
|
|
|
/**
|
|
|
|
|
* Add a feed link to the page header
|
|
|
|
|
*
|
2014-07-24 17:42:24 +00:00
|
|
|
* @param string $format Feed type, should be a key of $wgFeedClasses
|
2013-03-11 17:15:01 +00:00
|
|
|
* @param string $href URL
|
2010-01-26 20:47:34 +00:00
|
|
|
*/
|
2009-09-30 17:35:41 +00:00
|
|
|
public function addFeedLink( $format, $href ) {
|
2018-01-28 02:21:51 +00:00
|
|
|
if ( in_array( $format, $this->getAdvertisedFeedTypes() ) ) {
|
2010-05-11 19:36:47 +00:00
|
|
|
$this->mFeedLinks[$format] = $href;
|
|
|
|
|
}
|
2009-09-30 17:35:41 +00:00
|
|
|
}
|
2009-10-24 21:32:53 +00:00
|
|
|
|
2010-01-26 20:47:34 +00:00
|
|
|
/**
|
2010-01-27 00:11:13 +00:00
|
|
|
* Should we output feed links for this page?
|
2014-04-10 18:50:10 +00:00
|
|
|
* @return bool
|
2010-01-26 20:47:34 +00:00
|
|
|
*/
|
2010-01-28 21:58:50 +00:00
|
|
|
public function isSyndicated() {
|
|
|
|
|
return count( $this->mFeedLinks ) > 0;
|
|
|
|
|
}
|
2006-11-07 05:37:31 +00:00
|
|
|
|
2010-01-29 21:32:45 +00:00
|
|
|
/**
|
|
|
|
|
* Return URLs for each supported syndication format for this page.
|
2014-07-24 17:42:24 +00:00
|
|
|
* @return array Associating format keys with URLs
|
2010-01-29 21:32:45 +00:00
|
|
|
*/
|
|
|
|
|
public function getSyndicationLinks() {
|
|
|
|
|
return $this->mFeedLinks;
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-28 21:58:50 +00:00
|
|
|
/**
|
|
|
|
|
* Will currently always return null
|
|
|
|
|
*
|
|
|
|
|
* @return null
|
|
|
|
|
*/
|
|
|
|
|
public function getFeedAppendQuery() {
|
|
|
|
|
return $this->mFeedLinksAppendQuery;
|
|
|
|
|
}
|
2010-01-26 20:47:34 +00:00
|
|
|
|
2010-01-28 21:58:50 +00:00
|
|
|
/**
|
|
|
|
|
* Set whether the displayed content is related to the source of the
|
|
|
|
|
* corresponding article on the wiki
|
|
|
|
|
* Setting true will cause the change "article related" toggle to true
|
|
|
|
|
*
|
2018-07-25 18:41:42 +00:00
|
|
|
* @param bool $newVal
|
2010-01-28 21:58:50 +00:00
|
|
|
*/
|
2018-07-25 18:41:42 +00:00
|
|
|
public function setArticleFlag( $newVal ) {
|
|
|
|
|
$this->mIsArticle = $newVal;
|
|
|
|
|
if ( $newVal ) {
|
|
|
|
|
$this->mIsArticleRelated = $newVal;
|
2010-01-28 21:58:50 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return whether the content displayed page is related to the source of
|
|
|
|
|
* the corresponding article on the wiki
|
|
|
|
|
*
|
2014-04-10 18:50:10 +00:00
|
|
|
* @return bool
|
2010-01-28 21:58:50 +00:00
|
|
|
*/
|
|
|
|
|
public function isArticle() {
|
2018-07-25 18:41:42 +00:00
|
|
|
return $this->mIsArticle;
|
2010-01-28 21:58:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Set whether this page is related an article on the wiki
|
|
|
|
|
* Setting false will cause the change of "article flag" toggle to false
|
|
|
|
|
*
|
2018-07-25 18:41:42 +00:00
|
|
|
* @param bool $newVal
|
2010-01-28 21:58:50 +00:00
|
|
|
*/
|
2018-07-25 18:41:42 +00:00
|
|
|
public function setArticleRelated( $newVal ) {
|
|
|
|
|
$this->mIsArticleRelated = $newVal;
|
|
|
|
|
if ( !$newVal ) {
|
|
|
|
|
$this->mIsArticle = false;
|
2004-01-17 09:49:43 +00:00
|
|
|
}
|
|
|
|
|
}
|
2010-01-28 21:58:50 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return whether this page is related an article on the wiki
|
|
|
|
|
*
|
2014-04-10 18:50:10 +00:00
|
|
|
* @return bool
|
2010-01-28 21:58:50 +00:00
|
|
|
*/
|
|
|
|
|
public function isArticleRelated() {
|
|
|
|
|
return $this->mIsArticleRelated;
|
2004-01-17 09:49:43 +00:00
|
|
|
}
|
|
|
|
|
|
2018-10-03 13:12:37 +00:00
|
|
|
/**
|
|
|
|
|
* Set whether the standard copyright should be shown for the current page.
|
|
|
|
|
*
|
|
|
|
|
* @param bool $hasCopyright
|
|
|
|
|
*/
|
|
|
|
|
public function setCopyright( $hasCopyright ) {
|
|
|
|
|
$this->mHasCopyright = $hasCopyright;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return whether the standard copyright should be shown for the current page.
|
|
|
|
|
* By default, it is true for all articles but other pages
|
|
|
|
|
* can signal it by using setCopyright( true ).
|
|
|
|
|
*
|
|
|
|
|
* Used by SkinTemplate to decided whether to show the copyright.
|
|
|
|
|
*
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
public function showsCopyright() {
|
|
|
|
|
return $this->isArticle() || $this->mHasCopyright;
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-28 21:58:50 +00:00
|
|
|
/**
|
|
|
|
|
* Add new language links
|
|
|
|
|
*
|
2016-11-07 13:13:51 +00:00
|
|
|
* @param string[] $newLinkArray Array of interwiki-prefixed (non DB key) titles
|
|
|
|
|
* (e.g. 'fr:Test page')
|
2010-01-28 21:58:50 +00:00
|
|
|
*/
|
2014-07-23 02:49:30 +00:00
|
|
|
public function addLanguageLinks( array $newLinkArray ) {
|
Use array_merge() for OutputPage::$mLanguageLinks, not +
Based on documentation together with inspection of some callers, the
intent seems to be that this is an indexed array, not associative. +
will therefore do totally the wrong thing, ignoring any new values that
have the same key as an existing item (e.g., '0' or '1'). Even if it
was an associative array, + keeps the values on the left-hand side,
which means you normally want to do $foo = $bar + $foo instead of $foo
+= $bar if you want to overwrite old values with the new ones.
Before this change, calling addLanguageLinks() or
addParserOutputMetadata() would generally not add all of the links it
was supposed to if there were already links defined. (It could still
work if the arrays' keys didn't conflict for some reason, e.g.,
something passed an associative array or an indexed array with a hole.)
I don't know if anything actually hits this bug, because it's likely
that callers usually add all their links at once. I find no uses of
addLanguageLinks() at all.
I found this bug while working on adding more tests for OutputPage, and
the tests for this change will be submitted later in
Icdc0288c04b8c4ba841f9fbb3e05a0cdc8a20fa5.
Change-Id: I53f6e7ea94417b0034371e56e733e8c86af21658
2018-07-25 18:36:56 +00:00
|
|
|
$this->mLanguageLinks = array_merge( $this->mLanguageLinks, $newLinkArray );
|
2004-06-01 18:29:31 +00:00
|
|
|
}
|
2010-01-28 21:58:50 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Reset the language links and add new language links
|
|
|
|
|
*
|
2016-11-07 13:13:51 +00:00
|
|
|
* @param string[] $newLinkArray Array of interwiki-prefixed (non DB key) titles
|
|
|
|
|
* (e.g. 'fr:Test page')
|
2010-01-28 21:58:50 +00:00
|
|
|
*/
|
2014-07-23 02:49:30 +00:00
|
|
|
public function setLanguageLinks( array $newLinkArray ) {
|
2004-06-01 18:29:31 +00:00
|
|
|
$this->mLanguageLinks = $newLinkArray;
|
|
|
|
|
}
|
2004-09-17 15:24:43 +00:00
|
|
|
|
2010-01-28 21:58:50 +00:00
|
|
|
/**
|
|
|
|
|
* Get the list of language links
|
|
|
|
|
*
|
2016-11-07 13:13:51 +00:00
|
|
|
* @return string[] Array of interwiki-prefixed (non DB key) titles (e.g. 'fr:Test page')
|
2010-01-28 21:58:50 +00:00
|
|
|
*/
|
|
|
|
|
public function getLanguageLinks() {
|
|
|
|
|
return $this->mLanguageLinks;
|
2004-07-08 14:53:54 +00:00
|
|
|
}
|
2005-12-30 09:33:11 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Add an array of categories, with names in the keys
|
2010-01-28 21:58:50 +00:00
|
|
|
*
|
2014-07-24 17:42:24 +00:00
|
|
|
* @param array $categories Mapping category name => sort key
|
2005-12-30 09:33:11 +00:00
|
|
|
*/
|
2014-07-23 02:49:30 +00:00
|
|
|
public function addCategoryLinks( array $categories ) {
|
2018-07-25 18:41:42 +00:00
|
|
|
if ( !$categories ) {
|
2008-02-20 08:53:12 +00:00
|
|
|
return;
|
|
|
|
|
}
|
2008-02-25 16:38:25 +00:00
|
|
|
|
2016-07-08 19:11:53 +00:00
|
|
|
$res = $this->addCategoryLinksToLBAndGetResult( $categories );
|
2008-02-20 08:53:12 +00:00
|
|
|
|
2014-07-23 02:41:50 +00:00
|
|
|
# Set all the values to 'normal'.
|
|
|
|
|
$categories = array_fill_keys( array_keys( $categories ), 'normal' );
|
2008-02-25 16:38:25 +00:00
|
|
|
|
|
|
|
|
# Mark hidden categories
|
2008-02-20 08:53:12 +00:00
|
|
|
foreach ( $res as $row ) {
|
2008-02-25 16:38:25 +00:00
|
|
|
if ( isset( $row->pp_value ) ) {
|
|
|
|
|
$categories[$row->page_title] = 'hidden';
|
2008-02-20 08:53:12 +00:00
|
|
|
}
|
|
|
|
|
}
|
2008-02-25 16:38:25 +00:00
|
|
|
|
2017-01-01 23:07:11 +00:00
|
|
|
// Avoid PHP 7.1 warning of passing $this by reference
|
|
|
|
|
$outputPage = $this;
|
2008-02-20 08:53:12 +00:00
|
|
|
# Add the remaining categories to the skin
|
2014-12-09 07:23:30 +00:00
|
|
|
if ( Hooks::run(
|
2014-05-11 01:54:15 +00:00
|
|
|
'OutputPageMakeCategoryLinks',
|
2017-01-01 23:07:11 +00:00
|
|
|
[ &$outputPage, $categories, &$this->mCategoryLinks ] )
|
2014-05-11 01:54:15 +00:00
|
|
|
) {
|
2018-07-29 12:24:54 +00:00
|
|
|
$services = MediaWikiServices::getInstance();
|
|
|
|
|
$linkRenderer = $services->getLinkRenderer();
|
2008-07-02 20:02:51 +00:00
|
|
|
foreach ( $categories as $category => $type ) {
|
2015-12-03 14:15:59 +00:00
|
|
|
// array keys will cast numeric category names to ints, so cast back to string
|
|
|
|
|
$category = (string)$category;
|
2009-02-02 07:54:43 +00:00
|
|
|
$origcategory = $category;
|
2008-07-02 20:02:51 +00:00
|
|
|
$title = Title::makeTitleSafe( NS_CATEGORY, $category );
|
2014-07-22 19:18:07 +00:00
|
|
|
if ( !$title ) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2018-07-29 12:24:54 +00:00
|
|
|
$services->getContentLanguage()->findVariantLink( $category, $title, true );
|
2014-07-22 19:19:41 +00:00
|
|
|
if ( $category != $origcategory && array_key_exists( $category, $categories ) ) {
|
|
|
|
|
continue;
|
2010-05-22 12:18:22 +00:00
|
|
|
}
|
2018-07-29 12:24:54 +00:00
|
|
|
$text = $services->getContentLanguage()->convertHtml( $title->getText() );
|
2016-07-08 19:11:53 +00:00
|
|
|
$this->mCategories[$type][] = $title->getText();
|
2016-11-30 21:28:55 +00:00
|
|
|
$this->mCategoryLinks[$type][] = $linkRenderer->makeLink( $title, new HtmlArmor( $text ) );
|
2008-07-02 20:02:51 +00:00
|
|
|
}
|
2005-12-30 09:33:11 +00:00
|
|
|
}
|
2004-06-19 06:46:54 +00:00
|
|
|
}
|
2005-12-30 09:33:11 +00:00
|
|
|
|
2016-07-08 19:11:53 +00:00
|
|
|
/**
|
|
|
|
|
* @param array $categories
|
2018-06-27 18:49:23 +00:00
|
|
|
* @return bool|IResultWrapper
|
2016-07-08 19:11:53 +00:00
|
|
|
*/
|
|
|
|
|
protected function addCategoryLinksToLBAndGetResult( array $categories ) {
|
|
|
|
|
# Add the links to a LinkBatch
|
|
|
|
|
$arr = [ NS_CATEGORY => $categories ];
|
|
|
|
|
$lb = new LinkBatch;
|
|
|
|
|
$lb->setArray( $arr );
|
|
|
|
|
|
|
|
|
|
# Fetch existence plus the hiddencat property
|
|
|
|
|
$dbr = wfGetDB( DB_REPLICA );
|
|
|
|
|
$fields = array_merge(
|
|
|
|
|
LinkCache::getSelectFields(),
|
|
|
|
|
[ 'page_namespace', 'page_title', 'pp_value' ]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$res = $dbr->select( [ 'page', 'page_props' ],
|
|
|
|
|
$fields,
|
|
|
|
|
$lb->constructSet( 'page', $dbr ),
|
|
|
|
|
__METHOD__,
|
|
|
|
|
[],
|
|
|
|
|
[ 'page_props' => [ 'LEFT JOIN', [
|
|
|
|
|
'pp_propname' => 'hiddencat',
|
|
|
|
|
'pp_page = page_id'
|
|
|
|
|
] ] ]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
# Add the results to the link cache
|
2018-06-11 06:55:11 +00:00
|
|
|
$linkCache = MediaWikiServices::getInstance()->getLinkCache();
|
|
|
|
|
$lb->addResultToCache( $linkCache, $res );
|
2016-07-08 19:11:53 +00:00
|
|
|
|
|
|
|
|
return $res;
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-28 21:58:50 +00:00
|
|
|
/**
|
|
|
|
|
* Reset the category links (but not the category list) and add $categories
|
|
|
|
|
*
|
2014-07-24 17:42:24 +00:00
|
|
|
* @param array $categories Mapping category name => sort key
|
2010-01-28 21:58:50 +00:00
|
|
|
*/
|
2014-07-23 02:49:30 +00:00
|
|
|
public function setCategoryLinks( array $categories ) {
|
2016-02-17 09:09:32 +00:00
|
|
|
$this->mCategoryLinks = [];
|
2010-01-28 21:58:50 +00:00
|
|
|
$this->addCategoryLinks( $categories );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the list of category links, in a 2-D array with the following format:
|
|
|
|
|
* $arr[$type][] = $link, where $type is either "normal" or "hidden" (for
|
|
|
|
|
* hidden categories) and $link a HTML fragment with a link to the category
|
|
|
|
|
* page
|
|
|
|
|
*
|
2014-04-10 18:50:10 +00:00
|
|
|
* @return array
|
2010-01-28 21:58:50 +00:00
|
|
|
*/
|
|
|
|
|
public function getCategoryLinks() {
|
|
|
|
|
return $this->mCategoryLinks;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2016-07-08 19:11:53 +00:00
|
|
|
* Get the list of category names this page belongs to.
|
2010-01-28 21:58:50 +00:00
|
|
|
*
|
2016-07-08 19:11:53 +00:00
|
|
|
* @param string $type The type of categories which should be returned. Possible values:
|
|
|
|
|
* * all: all categories of all types
|
|
|
|
|
* * hidden: only the hidden categories
|
|
|
|
|
* * normal: all categories, except hidden categories
|
2014-04-10 18:50:10 +00:00
|
|
|
* @return array Array of strings
|
2010-01-28 21:58:50 +00:00
|
|
|
*/
|
2016-07-08 19:11:53 +00:00
|
|
|
public function getCategories( $type = 'all' ) {
|
|
|
|
|
if ( $type === 'all' ) {
|
|
|
|
|
$allCategories = [];
|
|
|
|
|
foreach ( $this->mCategories as $categories ) {
|
|
|
|
|
$allCategories = array_merge( $allCategories, $categories );
|
|
|
|
|
}
|
|
|
|
|
return $allCategories;
|
|
|
|
|
}
|
|
|
|
|
if ( !isset( $this->mCategories[$type] ) ) {
|
|
|
|
|
throw new InvalidArgumentException( 'Invalid category type given: ' . $type );
|
|
|
|
|
}
|
|
|
|
|
return $this->mCategories[$type];
|
2004-06-19 06:46:54 +00:00
|
|
|
}
|
|
|
|
|
|
Implement page status indicators
Page status indicators are icons (or short text snippets) usually
displayed in the top-right corner of the page, outside of the main
content. Basically, <indicator name="foo">[[File:Foo.svg|20px]]</indicator>
may be used on a page to place the icon in the indicator area. They
are also known as top icons, page icons, heading icons or title icons.
I found the discussion on bug 23796 highly illuminating. I suggest
that everyone read it before suggesting different design choices.
I spent some time with a thesaurus pondering the name. "Emblems" and
"badges" were also considered, but the former has a much more limited
meaning and the latter is already taken by Wikidata, with a similar
but subtly different feature set. I am not aware of any naming
conflicts ;) besides new talk page message "indicator" (used by core
and Echo in some documents) and OOjs UI indicators (tiny icons like
the arrow on a dropdown form element), which shouldn't be confusing.
Potential use cases include:
* "Lock" indicators for page protection levels
* Featured/good article indicators
* Redirect shortcuts display ("WP:VPT")
* Links to help/manual for special pages
* Coordinates?… or globe icon for inline pop-up maps
Design features:
* Skin-customizable. Skins can fully control where and how indicators
are shown, or may just do <?php echo $this->getIndicators(); ?> to
output the default structure. By default they are not shown at all.
* Extension-customizable. Extensions can call ParserOutput::addIndicator()
to insert an indicator from one of the numerous parser hooks.
* Wiki-customizable. In addition to just using the parser functions,
on-wiki styles and scripts can use the provided classes and ids
(.mw-indicator, #mw-indicator-<name>) to customize their display.
Design limitations:
* Every indicator must have a unique identifier (name). It's not
possible to create arrays, or to have several indicators with the
same name. In case of duplicates, the latest occurrence of the
parser function wins.
* Indicators are displayed ordered by their names (and not occurrence
order). This ensures consistency across pages and provides a simple
means of ordering or grouping them.
* Indicators are not stored, tracked or accessible outside of
ParserOutput (in particular they're not in the page_props table).
They are intended to merely reflect the content or metadata that is
already present on the page, and not be data themselves. If you ever
think you need to list pages with a given status indicator, instead
figure out what it means and use the appropriate tracking category,
special page report, already existing page_prop, or other means.
Corresponding patch in Vector: I90a8ae15ac8275d084ea5f47b6b2684d5e6c7412.
I'll implement support in the other three skins included in the tarball
and document it on mediawiki.org after this is merged.
Bug: 23796
Change-Id: I2389ff9a5332a2b1d033eb75f0946e5241cfaaf4
2014-09-24 10:44:16 +00:00
|
|
|
/**
|
2014-08-31 10:25:18 +00:00
|
|
|
* Add an array of indicators, with their identifiers as array
|
|
|
|
|
* keys and HTML contents as values.
|
Implement page status indicators
Page status indicators are icons (or short text snippets) usually
displayed in the top-right corner of the page, outside of the main
content. Basically, <indicator name="foo">[[File:Foo.svg|20px]]</indicator>
may be used on a page to place the icon in the indicator area. They
are also known as top icons, page icons, heading icons or title icons.
I found the discussion on bug 23796 highly illuminating. I suggest
that everyone read it before suggesting different design choices.
I spent some time with a thesaurus pondering the name. "Emblems" and
"badges" were also considered, but the former has a much more limited
meaning and the latter is already taken by Wikidata, with a similar
but subtly different feature set. I am not aware of any naming
conflicts ;) besides new talk page message "indicator" (used by core
and Echo in some documents) and OOjs UI indicators (tiny icons like
the arrow on a dropdown form element), which shouldn't be confusing.
Potential use cases include:
* "Lock" indicators for page protection levels
* Featured/good article indicators
* Redirect shortcuts display ("WP:VPT")
* Links to help/manual for special pages
* Coordinates?… or globe icon for inline pop-up maps
Design features:
* Skin-customizable. Skins can fully control where and how indicators
are shown, or may just do <?php echo $this->getIndicators(); ?> to
output the default structure. By default they are not shown at all.
* Extension-customizable. Extensions can call ParserOutput::addIndicator()
to insert an indicator from one of the numerous parser hooks.
* Wiki-customizable. In addition to just using the parser functions,
on-wiki styles and scripts can use the provided classes and ids
(.mw-indicator, #mw-indicator-<name>) to customize their display.
Design limitations:
* Every indicator must have a unique identifier (name). It's not
possible to create arrays, or to have several indicators with the
same name. In case of duplicates, the latest occurrence of the
parser function wins.
* Indicators are displayed ordered by their names (and not occurrence
order). This ensures consistency across pages and provides a simple
means of ordering or grouping them.
* Indicators are not stored, tracked or accessible outside of
ParserOutput (in particular they're not in the page_props table).
They are intended to merely reflect the content or metadata that is
already present on the page, and not be data themselves. If you ever
think you need to list pages with a given status indicator, instead
figure out what it means and use the appropriate tracking category,
special page report, already existing page_prop, or other means.
Corresponding patch in Vector: I90a8ae15ac8275d084ea5f47b6b2684d5e6c7412.
I'll implement support in the other three skins included in the tarball
and document it on mediawiki.org after this is merged.
Bug: 23796
Change-Id: I2389ff9a5332a2b1d033eb75f0946e5241cfaaf4
2014-09-24 10:44:16 +00:00
|
|
|
*
|
|
|
|
|
* In case of duplicate keys, existing values are overwritten.
|
|
|
|
|
*
|
|
|
|
|
* @param array $indicators
|
|
|
|
|
* @since 1.25
|
|
|
|
|
*/
|
|
|
|
|
public function setIndicators( array $indicators ) {
|
|
|
|
|
$this->mIndicators = $indicators + $this->mIndicators;
|
|
|
|
|
// Keep ordered by key
|
|
|
|
|
ksort( $this->mIndicators );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the indicators associated with this page.
|
|
|
|
|
*
|
|
|
|
|
* The array will be internally ordered by item keys.
|
|
|
|
|
*
|
|
|
|
|
* @return array Keys: identifiers, values: HTML contents
|
|
|
|
|
* @since 1.25
|
|
|
|
|
*/
|
|
|
|
|
public function getIndicators() {
|
|
|
|
|
return $this->mIndicators;
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-31 10:25:18 +00:00
|
|
|
/**
|
|
|
|
|
* Adds help link with an icon via page indicators.
|
2015-05-02 11:37:19 +00:00
|
|
|
* Link target can be overridden by a local message containing a wikilink:
|
|
|
|
|
* the message key is: lowercase action or special page name + '-helppage'.
|
|
|
|
|
* @param string $to Target MediaWiki.org page title or encoded URL.
|
|
|
|
|
* @param bool $overrideBaseUrl Whether $url is a full URL, to avoid MW.o.
|
2014-08-31 10:25:18 +00:00
|
|
|
* @since 1.25
|
|
|
|
|
*/
|
|
|
|
|
public function addHelpLink( $to, $overrideBaseUrl = false ) {
|
2015-03-07 17:36:41 +00:00
|
|
|
$this->addModuleStyles( 'mediawiki.helplink' );
|
|
|
|
|
$text = $this->msg( 'helppage-top-gethelp' )->escaped();
|
2014-08-31 10:25:18 +00:00
|
|
|
|
|
|
|
|
if ( $overrideBaseUrl ) {
|
|
|
|
|
$helpUrl = $to;
|
|
|
|
|
} else {
|
2015-03-10 18:26:39 +00:00
|
|
|
$toUrlencoded = wfUrlencode( str_replace( ' ', '_', $to ) );
|
2019-05-17 05:16:16 +00:00
|
|
|
$helpUrl = "https://www.mediawiki.org/wiki/Special:MyLanguage/$toUrlencoded";
|
2014-08-31 10:25:18 +00:00
|
|
|
}
|
2015-05-02 11:37:19 +00:00
|
|
|
|
2014-08-31 10:25:18 +00:00
|
|
|
$link = Html::rawElement(
|
|
|
|
|
'a',
|
2016-02-17 09:09:32 +00:00
|
|
|
[
|
2014-08-31 10:25:18 +00:00
|
|
|
'href' => $helpUrl,
|
|
|
|
|
'target' => '_blank',
|
|
|
|
|
'class' => 'mw-helplink',
|
2016-02-17 09:09:32 +00:00
|
|
|
],
|
2014-08-31 10:25:18 +00:00
|
|
|
$text
|
|
|
|
|
);
|
|
|
|
|
|
2016-02-17 09:09:32 +00:00
|
|
|
$this->setIndicators( [ 'mw-helplink' => $link ] );
|
2014-08-31 10:25:18 +00:00
|
|
|
}
|
|
|
|
|
|
2010-01-29 21:32:45 +00:00
|
|
|
/**
|
2014-10-10 06:46:12 +00:00
|
|
|
* Do not allow scripts which can be modified by wiki users to load on this page;
|
|
|
|
|
* only allow scripts bundled with, or generated by, the software.
|
|
|
|
|
* Site-wide styles are controlled by a config setting, since they can be
|
|
|
|
|
* used to create a custom skin/theme, but not user-specific ones.
|
2014-09-11 23:14:16 +00:00
|
|
|
*
|
2014-10-10 06:46:12 +00:00
|
|
|
* @todo this should be given a more accurate name
|
2010-01-29 21:32:45 +00:00
|
|
|
*/
|
|
|
|
|
public function disallowUserJs() {
|
2014-10-10 06:46:12 +00:00
|
|
|
$this->reduceAllowedModules(
|
|
|
|
|
ResourceLoaderModule::TYPE_SCRIPTS,
|
|
|
|
|
ResourceLoaderModule::ORIGIN_CORE_INDIVIDUAL
|
|
|
|
|
);
|
|
|
|
|
|
2017-02-20 22:44:19 +00:00
|
|
|
// Site-wide styles are controlled by a config setting, see T73621
|
2014-10-10 06:46:12 +00:00
|
|
|
// for background on why. User styles are never allowed.
|
|
|
|
|
if ( $this->getConfig()->get( 'AllowSiteCSSOnRestrictedPages' ) ) {
|
|
|
|
|
$styleOrigin = ResourceLoaderModule::ORIGIN_USER_SITEWIDE;
|
|
|
|
|
} else {
|
|
|
|
|
$styleOrigin = ResourceLoaderModule::ORIGIN_CORE_INDIVIDUAL;
|
|
|
|
|
}
|
|
|
|
|
$this->reduceAllowedModules(
|
|
|
|
|
ResourceLoaderModule::TYPE_STYLES,
|
|
|
|
|
$styleOrigin
|
|
|
|
|
);
|
2010-01-29 21:32:45 +00:00
|
|
|
}
|
|
|
|
|
|
2011-02-04 16:39:17 +00:00
|
|
|
/**
|
2014-10-10 06:46:12 +00:00
|
|
|
* Show what level of JavaScript / CSS untrustworthiness is allowed on this page
|
2011-02-04 16:39:17 +00:00
|
|
|
* @see ResourceLoaderModule::$origin
|
2014-10-10 06:46:12 +00:00
|
|
|
* @param string $type ResourceLoaderModule TYPE_ constant
|
2014-04-10 18:50:10 +00:00
|
|
|
* @return int ResourceLoaderModule ORIGIN_ class constant
|
2011-02-04 16:39:17 +00:00
|
|
|
*/
|
2014-10-10 06:46:12 +00:00
|
|
|
public function getAllowedModules( $type ) {
|
|
|
|
|
if ( $type == ResourceLoaderModule::TYPE_COMBINED ) {
|
|
|
|
|
return min( array_values( $this->mAllowedModules ) );
|
|
|
|
|
} else {
|
2017-10-06 22:17:58 +00:00
|
|
|
return $this->mAllowedModules[$type] ?? ResourceLoaderModule::ORIGIN_ALL;
|
2014-10-10 06:46:12 +00:00
|
|
|
}
|
2011-02-04 16:39:17 +00:00
|
|
|
}
|
|
|
|
|
|
2014-09-11 23:14:16 +00:00
|
|
|
/**
|
|
|
|
|
* Limit the highest level of CSS/JS untrustworthiness allowed.
|
|
|
|
|
*
|
|
|
|
|
* If passed the same or a higher level than the current level of untrustworthiness set, the
|
|
|
|
|
* level will remain unchanged.
|
|
|
|
|
*
|
2014-10-10 06:46:12 +00:00
|
|
|
* @param string $type
|
2014-09-11 23:14:16 +00:00
|
|
|
* @param int $level ResourceLoaderModule class constant
|
|
|
|
|
*/
|
2014-10-10 06:46:12 +00:00
|
|
|
public function reduceAllowedModules( $type, $level ) {
|
|
|
|
|
$this->mAllowedModules[$type] = min( $this->getAllowedModules( $type ), $level );
|
2010-01-29 21:32:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Prepend $text to the body HTML
|
|
|
|
|
*
|
2013-03-11 17:15:01 +00:00
|
|
|
* @param string $text HTML
|
2010-01-29 21:32:45 +00:00
|
|
|
*/
|
|
|
|
|
public function prependHTML( $text ) {
|
|
|
|
|
$this->mBodytext = $text . $this->mBodytext;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Append $text to the body HTML
|
|
|
|
|
*
|
2013-03-11 17:15:01 +00:00
|
|
|
* @param string $text HTML
|
2010-01-29 21:32:45 +00:00
|
|
|
*/
|
|
|
|
|
public function addHTML( $text ) {
|
|
|
|
|
$this->mBodytext .= $text;
|
|
|
|
|
}
|
2011-10-16 03:27:12 +00:00
|
|
|
|
2011-10-10 21:05:02 +00:00
|
|
|
/**
|
|
|
|
|
* Shortcut for adding an Html::element via addHTML.
|
2011-10-16 03:27:12 +00:00
|
|
|
*
|
2011-10-10 21:05:02 +00:00
|
|
|
* @since 1.19
|
2011-10-16 03:27:12 +00:00
|
|
|
*
|
2014-04-10 18:50:10 +00:00
|
|
|
* @param string $element
|
|
|
|
|
* @param array $attribs
|
|
|
|
|
* @param string $contents
|
2011-10-10 21:05:02 +00:00
|
|
|
*/
|
2016-02-17 09:09:32 +00:00
|
|
|
public function addElement( $element, array $attribs = [], $contents = '' ) {
|
2011-10-10 21:05:02 +00:00
|
|
|
$this->addHTML( Html::element( $element, $attribs, $contents ) );
|
|
|
|
|
}
|
2010-01-29 21:32:45 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Clear the body HTML
|
|
|
|
|
*/
|
|
|
|
|
public function clearHTML() {
|
|
|
|
|
$this->mBodytext = '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the body HTML
|
|
|
|
|
*
|
2014-04-08 15:29:17 +00:00
|
|
|
* @return string HTML
|
2010-01-29 21:32:45 +00:00
|
|
|
*/
|
|
|
|
|
public function getHTML() {
|
|
|
|
|
return $this->mBodytext;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get/set the ParserOptions object to use for wikitext parsing
|
|
|
|
|
*
|
2014-04-10 18:50:10 +00:00
|
|
|
* @return ParserOptions
|
2019-09-07 13:06:50 +00:00
|
|
|
* @suppress PhanUndeclaredProperty For isBogus
|
2010-01-29 21:32:45 +00:00
|
|
|
*/
|
2020-03-08 13:22:13 +00:00
|
|
|
public function parserOptions() {
|
2006-07-26 07:15:39 +00:00
|
|
|
if ( !$this->mParserOptions ) {
|
2019-03-11 16:50:46 +00:00
|
|
|
if ( !$this->getUser()->isSafeToLoad() ) {
|
2017-10-05 17:27:08 +00:00
|
|
|
// $wgUser isn't unstubbable yet, so don't try to get a
|
2016-02-03 20:18:52 +00:00
|
|
|
// ParserOptions for it. And don't cache this ParserOptions
|
|
|
|
|
// either.
|
|
|
|
|
$po = ParserOptions::newFromAnon();
|
2017-02-06 03:00:39 +00:00
|
|
|
$po->setAllowUnsafeRawHtml( false );
|
2016-02-03 20:18:52 +00:00
|
|
|
$po->isBogus = true;
|
|
|
|
|
return $po;
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-19 14:16:01 +00:00
|
|
|
$this->mParserOptions = ParserOptions::newFromContext( $this->getContext() );
|
2017-02-06 03:00:39 +00:00
|
|
|
$this->mParserOptions->setAllowUnsafeRawHtml( false );
|
2006-07-26 07:15:39 +00:00
|
|
|
}
|
2016-02-03 20:18:52 +00:00
|
|
|
|
2020-03-08 13:22:13 +00:00
|
|
|
return $this->mParserOptions;
|
2004-02-29 08:43:29 +00:00
|
|
|
}
|
2006-01-07 13:31:29 +00:00
|
|
|
|
2005-11-27 06:04:41 +00:00
|
|
|
/**
|
|
|
|
|
* Set the revision ID which will be seen by the wiki text parser
|
|
|
|
|
* for things such as embedded {{REVISIONID}} variable use.
|
2010-01-29 21:32:45 +00:00
|
|
|
*
|
2018-07-25 18:41:42 +00:00
|
|
|
* @param int|null $revid A positive integer, or null
|
2014-04-08 15:29:17 +00:00
|
|
|
* @return mixed Previous value
|
2005-11-27 06:04:41 +00:00
|
|
|
*/
|
2006-11-07 05:37:31 +00:00
|
|
|
public function setRevisionId( $revid ) {
|
2020-01-09 23:48:34 +00:00
|
|
|
$val = $revid === null ? null : intval( $revid );
|
2018-07-25 18:41:42 +00:00
|
|
|
return wfSetVar( $this->mRevisionId, $val, true );
|
2005-11-27 06:04:41 +00:00
|
|
|
}
|
here it is ... the upload-api, script-server, js2 (javascript phase2) branch merge 1st attempt.
Here is a short overview of changes and associated default configuration variables (most everything is off by default) also see ~soon to be updated~: http://www.mediawiki.org/wiki/Media_Projects_Overview
= Upload Improvements =
==Upload API ==
* Based on the early work of Bryan Tong and others it adds the upload option to the api.
* We rewrite Special:Upload page to include use the new refactoring
* Added in token checks in both the SpecialUpload.php page so avoids DOS / xss copy-by-url JavaScript based cross site POST file submissions
== Copy by URL==
$wgAllowCopyUploads = false;
* http class rewrite includes a new http background download see: includes/HttpFunctions.php
* spins off a php process that calls: maintenance/http_session_download.php
* pushes updates to the session and gives the user a progress bar on http copy uploads from other server progress (using js2 upload interface) (if not using the js2 upload interface it does the request in-place but the download is limited to the php ini timeout time)
== Firefogg ==
* Firefogg enables resumable upload by chunks
* progress indicators and conditional invokation (js2 system)
* and of-course client side transcoding.
= Script Server =
$wgEnableScriptLoader = false;
* off by default if $wgEnableScriptLoader is turned on script files are grouped, gziped, cached etc.
for more info see: http://www.mediawiki.org/wiki/Extension:ScriptLoader
* Includes some early skin js include fixes (skin/script system still lots of love)
* Includes a "javascript class autoloader" this is packaged into mwEmbed so that the mwEmbed library can work in stand alone mode (while retaining localization and script serving) (one such application is the make page for firefogg.org : http://www.firefogg.org/make/index.html )
* The file that contains the autojavascript loading classes is: js2/php/jsAutoloadLocalClasses.php
* One can use this auto class loading dependency system with extensions and add-ons but I need to better document that.
= js2 system / mwEmbed=
$wgEnableJS2system = false
* includes initial rewrite towards more jquery based javascript code
* especially for the Special:Upload page.
* Also the edit page include support for the "add-media-wizard"
* includes dependency loader for javascript that optionally takes advantage of the script-loader
* remote embedding of javascript interfaces (like embedding video, or commons media searching)
* $wgDebugJavaScript = false; .. .this variable lets you always get "always fresh javascript". When used with the script-loader it does not minify the script-loader output.
= mwEmbed =
* Will commit a separate patch to oggHandler that conditionally outputs <video tag> to use the new javascript video player.
** mv_embed player includes: play-head, volume control, remote embedding, oggz-chop support across plugins.
* add-media-wizard adds easy inserts of media to pages (with import)
== jQuery==
* we include a base install of jQuery, jQuery ui and some plugins.
* all the javascript classes are in the scriptloader so its easy to load any set of jquery ui components that you may need using the script-server. You get a callback so you can then execute js with dependencies loaded.
== other stuff ==
there is a bit more code in js2 that pertains to sequence editing, timed text display and basic image editing. We include a base import of pixastic-lib & pixastic-editor... will work with the pixastic developer to try and ensure upstream compatibility on our usage of the library for in-browser photo and sequence manipulation.
2009-07-14 23:52:14 +00:00
|
|
|
|
2010-01-29 21:32:45 +00:00
|
|
|
/**
|
2011-07-05 05:23:26 +00:00
|
|
|
* Get the displayed revision ID
|
2010-01-29 21:32:45 +00:00
|
|
|
*
|
2014-04-10 18:50:10 +00:00
|
|
|
* @return int
|
2010-01-29 21:32:45 +00:00
|
|
|
*/
|
2008-08-11 13:23:45 +00:00
|
|
|
public function getRevisionId() {
|
|
|
|
|
return $this->mRevisionId;
|
|
|
|
|
}
|
2004-02-29 08:43:29 +00:00
|
|
|
|
2019-08-21 12:10:09 +00:00
|
|
|
/**
|
|
|
|
|
* Whether the revision displayed is the latest revision of the page
|
|
|
|
|
*
|
|
|
|
|
* @since 1.34
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
public function isRevisionCurrent() {
|
|
|
|
|
return $this->mRevisionId == 0 || $this->mRevisionId == $this->getTitle()->getLatestRevID();
|
|
|
|
|
}
|
|
|
|
|
|
2011-12-10 16:30:40 +00:00
|
|
|
/**
|
|
|
|
|
* Set the timestamp of the revision which will be displayed. This is used
|
|
|
|
|
* to avoid a extra DB call in Skin::lastModified().
|
|
|
|
|
*
|
2014-04-08 15:29:17 +00:00
|
|
|
* @param string|null $timestamp
|
|
|
|
|
* @return mixed Previous value
|
2011-12-10 16:30:40 +00:00
|
|
|
*/
|
2013-03-24 10:01:51 +00:00
|
|
|
public function setRevisionTimestamp( $timestamp ) {
|
2018-07-25 18:41:42 +00:00
|
|
|
return wfSetVar( $this->mRevisionTimestamp, $timestamp, true );
|
2011-12-10 16:30:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the timestamp of displayed revision.
|
|
|
|
|
* This will be null if not filled by setRevisionTimestamp().
|
|
|
|
|
*
|
2014-04-10 18:50:10 +00:00
|
|
|
* @return string|null
|
2011-12-10 16:30:40 +00:00
|
|
|
*/
|
|
|
|
|
public function getRevisionTimestamp() {
|
|
|
|
|
return $this->mRevisionTimestamp;
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-05 05:23:26 +00:00
|
|
|
/**
|
|
|
|
|
* Set the displayed file version
|
|
|
|
|
*
|
2018-07-25 18:41:42 +00:00
|
|
|
* @param File|null $file
|
2014-04-08 15:29:17 +00:00
|
|
|
* @return mixed Previous value
|
2011-07-05 05:23:26 +00:00
|
|
|
*/
|
|
|
|
|
public function setFileVersion( $file ) {
|
2011-07-06 17:27:33 +00:00
|
|
|
$val = null;
|
2011-07-05 05:23:26 +00:00
|
|
|
if ( $file instanceof File && $file->exists() ) {
|
2016-02-17 09:09:32 +00:00
|
|
|
$val = [ 'time' => $file->getTimestamp(), 'sha1' => $file->getSha1() ];
|
2011-07-05 05:23:26 +00:00
|
|
|
}
|
2011-07-06 17:27:33 +00:00
|
|
|
return wfSetVar( $this->mFileVersion, $val, true );
|
2011-07-05 05:23:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the displayed file version
|
|
|
|
|
*
|
2014-04-10 18:50:10 +00:00
|
|
|
* @return array|null ('time' => MW timestamp, 'sha1' => sha1)
|
2011-07-05 05:23:26 +00:00
|
|
|
*/
|
|
|
|
|
public function getFileVersion() {
|
|
|
|
|
return $this->mFileVersion;
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-03 05:53:07 +00:00
|
|
|
/**
|
|
|
|
|
* Get the templates used on this page
|
|
|
|
|
*
|
2014-04-10 18:50:10 +00:00
|
|
|
* @return array (namespace => dbKey => revId)
|
2011-04-04 01:22:08 +00:00
|
|
|
* @since 1.18
|
2011-04-03 05:53:07 +00:00
|
|
|
*/
|
|
|
|
|
public function getTemplateIds() {
|
|
|
|
|
return $this->mTemplateIds;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the files used on this page
|
|
|
|
|
*
|
2017-01-16 15:42:53 +00:00
|
|
|
* @return array [ dbKey => [ 'time' => MW timestamp or null, 'sha1' => sha1 or '' ] ]
|
2011-04-04 01:22:08 +00:00
|
|
|
* @since 1.18
|
2011-04-03 05:53:07 +00:00
|
|
|
*/
|
2011-09-14 19:05:43 +00:00
|
|
|
public function getFileSearchOptions() {
|
2011-04-03 05:53:07 +00:00
|
|
|
return $this->mImageTimeKeys;
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-21 16:24:57 +00:00
|
|
|
/**
|
|
|
|
|
* Convert wikitext *in the user interface language* to HTML and
|
|
|
|
|
* add it to the buffer. The result will not be
|
|
|
|
|
* language-converted, as user interface messages are already
|
|
|
|
|
* localized into a specific variant. Assumes that the current
|
|
|
|
|
* page title will be used if optional $title is not
|
|
|
|
|
* provided. Output will be tidy.
|
|
|
|
|
*
|
|
|
|
|
* @param string $text Wikitext in the user interface language
|
|
|
|
|
* @param bool $linestart Is this the start of a line? (Defaults to true)
|
|
|
|
|
* @param Title|null $title Optional title to use; default of `null`
|
|
|
|
|
* means use current page title.
|
|
|
|
|
* @throws MWException if $title is not provided and OutputPage::getTitle()
|
|
|
|
|
* is null
|
|
|
|
|
* @since 1.32
|
|
|
|
|
*/
|
|
|
|
|
public function addWikiTextAsInterface(
|
|
|
|
|
$text, $linestart = true, Title $title = null
|
|
|
|
|
) {
|
|
|
|
|
if ( $title === null ) {
|
|
|
|
|
$title = $this->getTitle();
|
|
|
|
|
}
|
|
|
|
|
if ( !$title ) {
|
|
|
|
|
throw new MWException( 'Title is null' );
|
|
|
|
|
}
|
2019-06-19 15:57:36 +00:00
|
|
|
$this->addWikiTextTitleInternal( $text, $title, $linestart, /*interface*/true );
|
2018-09-21 16:24:57 +00:00
|
|
|
}
|
|
|
|
|
|
2018-09-27 15:04:45 +00:00
|
|
|
/**
|
|
|
|
|
* Convert wikitext *in the user interface language* to HTML and
|
|
|
|
|
* add it to the buffer with a `<div class="$wrapperClass">`
|
|
|
|
|
* wrapper. The result will not be language-converted, as user
|
|
|
|
|
* interface messages as already localized into a specific
|
|
|
|
|
* variant. The $text will be parsed in start-of-line context.
|
|
|
|
|
* Output will be tidy.
|
|
|
|
|
*
|
|
|
|
|
* @param string $wrapperClass The class attribute value for the <div>
|
|
|
|
|
* wrapper in the output HTML
|
|
|
|
|
* @param string $text Wikitext in the user interface language
|
|
|
|
|
* @since 1.32
|
|
|
|
|
*/
|
|
|
|
|
public function wrapWikiTextAsInterface(
|
|
|
|
|
$wrapperClass, $text
|
|
|
|
|
) {
|
|
|
|
|
$this->addWikiTextTitleInternal(
|
|
|
|
|
$text, $this->getTitle(),
|
2019-06-19 15:57:36 +00:00
|
|
|
/*linestart*/true, /*interface*/true,
|
2018-09-27 15:04:45 +00:00
|
|
|
$wrapperClass
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-21 16:24:57 +00:00
|
|
|
/**
|
|
|
|
|
* Convert wikitext *in the page content language* to HTML and add
|
|
|
|
|
* it to the buffer. The result with be language-converted to the
|
|
|
|
|
* user's preferred variant. Assumes that the current page title
|
|
|
|
|
* will be used if optional $title is not provided. Output will be
|
|
|
|
|
* tidy.
|
|
|
|
|
*
|
|
|
|
|
* @param string $text Wikitext in the page content language
|
|
|
|
|
* @param bool $linestart Is this the start of a line? (Defaults to true)
|
|
|
|
|
* @param Title|null $title Optional title to use; default of `null`
|
|
|
|
|
* means use current page title.
|
|
|
|
|
* @throws MWException if $title is not provided and OutputPage::getTitle()
|
|
|
|
|
* is null
|
|
|
|
|
* @since 1.32
|
|
|
|
|
*/
|
|
|
|
|
public function addWikiTextAsContent(
|
|
|
|
|
$text, $linestart = true, Title $title = null
|
|
|
|
|
) {
|
|
|
|
|
if ( $title === null ) {
|
|
|
|
|
$title = $this->getTitle();
|
|
|
|
|
}
|
|
|
|
|
if ( !$title ) {
|
|
|
|
|
throw new MWException( 'Title is null' );
|
|
|
|
|
}
|
2019-06-19 15:57:36 +00:00
|
|
|
$this->addWikiTextTitleInternal( $text, $title, $linestart, /*interface*/false );
|
2018-09-21 17:16:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Add wikitext with a custom Title object.
|
|
|
|
|
* Output is unwrapped.
|
|
|
|
|
*
|
|
|
|
|
* @param string $text Wikitext
|
|
|
|
|
* @param Title $title
|
|
|
|
|
* @param bool $linestart Is this the start of a line?
|
|
|
|
|
* @param bool $interface Whether it is an interface message
|
|
|
|
|
* (for example disables conversion)
|
2019-11-23 22:28:57 +00:00
|
|
|
* @param string|null $wrapperClass if not empty, wraps the output in
|
2018-09-27 15:04:45 +00:00
|
|
|
* a `<div class="$wrapperClass">`
|
2018-09-21 17:16:42 +00:00
|
|
|
*/
|
|
|
|
|
private function addWikiTextTitleInternal(
|
2019-06-19 15:57:36 +00:00
|
|
|
$text, Title $title, $linestart, $interface, $wrapperClass = null
|
2014-05-11 01:54:15 +00:00
|
|
|
) {
|
2018-10-26 15:14:01 +00:00
|
|
|
$parserOutput = $this->parseInternal(
|
2020-01-15 19:03:33 +00:00
|
|
|
$text, $title, $linestart, $interface
|
2010-05-22 12:18:22 +00:00
|
|
|
);
|
2008-04-14 07:45:50 +00:00
|
|
|
|
2017-11-22 20:07:51 +00:00
|
|
|
$this->addParserOutput( $parserOutput, [
|
|
|
|
|
'enableSectionEditLinks' => false,
|
2018-09-27 15:04:45 +00:00
|
|
|
'wrapperDivClass' => $wrapperClass ?? '',
|
2017-11-22 20:07:51 +00:00
|
|
|
] );
|
2006-01-01 20:08:08 +00:00
|
|
|
}
|
|
|
|
|
|
2014-05-08 20:10:13 +00:00
|
|
|
/**
|
|
|
|
|
* 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.
|
|
|
|
|
*
|
|
|
|
|
* @since 1.24
|
|
|
|
|
* @param ParserOutput $parserOutput
|
|
|
|
|
*/
|
2018-07-27 20:20:39 +00:00
|
|
|
public function addParserOutputMetadata( ParserOutput $parserOutput ) {
|
Use array_merge() for OutputPage::$mLanguageLinks, not +
Based on documentation together with inspection of some callers, the
intent seems to be that this is an indexed array, not associative. +
will therefore do totally the wrong thing, ignoring any new values that
have the same key as an existing item (e.g., '0' or '1'). Even if it
was an associative array, + keeps the values on the left-hand side,
which means you normally want to do $foo = $bar + $foo instead of $foo
+= $bar if you want to overwrite old values with the new ones.
Before this change, calling addLanguageLinks() or
addParserOutputMetadata() would generally not add all of the links it
was supposed to if there were already links defined. (It could still
work if the arrays' keys didn't conflict for some reason, e.g.,
something passed an associative array or an indexed array with a hole.)
I don't know if anything actually hits this bug, because it's likely
that callers usually add all their links at once. I find no uses of
addLanguageLinks() at all.
I found this bug while working on adding more tests for OutputPage, and
the tests for this change will be submitted later in
Icdc0288c04b8c4ba841f9fbb3e05a0cdc8a20fa5.
Change-Id: I53f6e7ea94417b0034371e56e733e8c86af21658
2018-07-25 18:36:56 +00:00
|
|
|
$this->mLanguageLinks =
|
|
|
|
|
array_merge( $this->mLanguageLinks, $parserOutput->getLanguageLinks() );
|
2005-12-30 09:33:11 +00:00
|
|
|
$this->addCategoryLinks( $parserOutput->getCategories() );
|
Implement page status indicators
Page status indicators are icons (or short text snippets) usually
displayed in the top-right corner of the page, outside of the main
content. Basically, <indicator name="foo">[[File:Foo.svg|20px]]</indicator>
may be used on a page to place the icon in the indicator area. They
are also known as top icons, page icons, heading icons or title icons.
I found the discussion on bug 23796 highly illuminating. I suggest
that everyone read it before suggesting different design choices.
I spent some time with a thesaurus pondering the name. "Emblems" and
"badges" were also considered, but the former has a much more limited
meaning and the latter is already taken by Wikidata, with a similar
but subtly different feature set. I am not aware of any naming
conflicts ;) besides new talk page message "indicator" (used by core
and Echo in some documents) and OOjs UI indicators (tiny icons like
the arrow on a dropdown form element), which shouldn't be confusing.
Potential use cases include:
* "Lock" indicators for page protection levels
* Featured/good article indicators
* Redirect shortcuts display ("WP:VPT")
* Links to help/manual for special pages
* Coordinates?… or globe icon for inline pop-up maps
Design features:
* Skin-customizable. Skins can fully control where and how indicators
are shown, or may just do <?php echo $this->getIndicators(); ?> to
output the default structure. By default they are not shown at all.
* Extension-customizable. Extensions can call ParserOutput::addIndicator()
to insert an indicator from one of the numerous parser hooks.
* Wiki-customizable. In addition to just using the parser functions,
on-wiki styles and scripts can use the provided classes and ids
(.mw-indicator, #mw-indicator-<name>) to customize their display.
Design limitations:
* Every indicator must have a unique identifier (name). It's not
possible to create arrays, or to have several indicators with the
same name. In case of duplicates, the latest occurrence of the
parser function wins.
* Indicators are displayed ordered by their names (and not occurrence
order). This ensures consistency across pages and provides a simple
means of ordering or grouping them.
* Indicators are not stored, tracked or accessible outside of
ParserOutput (in particular they're not in the page_props table).
They are intended to merely reflect the content or metadata that is
already present on the page, and not be data themselves. If you ever
think you need to list pages with a given status indicator, instead
figure out what it means and use the appropriate tracking category,
special page report, already existing page_prop, or other means.
Corresponding patch in Vector: I90a8ae15ac8275d084ea5f47b6b2684d5e6c7412.
I'll implement support in the other three skins included in the tarball
and document it on mediawiki.org after this is merged.
Bug: 23796
Change-Id: I2389ff9a5332a2b1d033eb75f0946e5241cfaaf4
2014-09-24 10:44:16 +00:00
|
|
|
$this->setIndicators( $parserOutput->getIndicators() );
|
2006-05-01 20:35:08 +00:00
|
|
|
$this->mNewSectionLink = $parserOutput->getNewSection();
|
2009-02-19 22:14:59 +00:00
|
|
|
$this->mHideNewSectionLink = $parserOutput->getHideNewSection();
|
2008-07-24 18:02:20 +00:00
|
|
|
|
2010-06-01 14:28:51 +00:00
|
|
|
if ( !$parserOutput->isCacheable() ) {
|
2005-05-28 11:09:22 +00:00
|
|
|
$this->enableClientCache( false );
|
|
|
|
|
}
|
2006-08-24 17:05:52 +00:00
|
|
|
$this->mNoGallery = $parserOutput->getNoGallery();
|
2010-01-16 13:07:58 +00:00
|
|
|
$this->mHeadItems = array_merge( $this->mHeadItems, $parserOutput->getHeadItems() );
|
2010-09-04 04:00:09 +00:00
|
|
|
$this->addModules( $parserOutput->getModules() );
|
2011-08-02 15:40:03 +00:00
|
|
|
$this->addModuleStyles( $parserOutput->getModuleStyles() );
|
2014-01-19 15:39:46 +00:00
|
|
|
$this->addJsConfigVars( $parserOutput->getJsConfigVars() );
|
2014-07-10 19:16:29 +00:00
|
|
|
$this->mPreventClickjacking = $this->mPreventClickjacking
|
|
|
|
|
|| $parserOutput->preventClickjacking();
|
2020-02-03 09:50:14 +00:00
|
|
|
$scriptSrcs = $parserOutput->getExtraCSPScriptSrcs();
|
|
|
|
|
foreach ( $scriptSrcs as $src ) {
|
|
|
|
|
$this->getCSP()->addScriptSrc( $src );
|
|
|
|
|
}
|
|
|
|
|
$defaultSrcs = $parserOutput->getExtraCSPDefaultSrcs();
|
|
|
|
|
foreach ( $defaultSrcs as $src ) {
|
|
|
|
|
$this->getCSP()->addDefaultSrc( $src );
|
|
|
|
|
}
|
|
|
|
|
$styleSrcs = $parserOutput->getExtraCSPStyleSrcs();
|
|
|
|
|
foreach ( $styleSrcs as $src ) {
|
|
|
|
|
$this->getCSP()->addStyleSrc( $src );
|
|
|
|
|
}
|
2011-03-23 17:35:40 +00:00
|
|
|
|
2020-02-07 05:31:00 +00:00
|
|
|
// If $wgImagePreconnect is true, and if the output contains
|
|
|
|
|
// images, give the user-agent a hint about foreign repos from
|
|
|
|
|
// which those images may be served. See T123582.
|
|
|
|
|
//
|
|
|
|
|
// TODO: We don't have an easy way to know from which remote(s)
|
|
|
|
|
// the image(s) will be served. For now, we only hint the first
|
|
|
|
|
// valid one.
|
|
|
|
|
if ( $this->getConfig()->get( 'ImagePreconnect' ) && count( $parserOutput->getImages() ) ) {
|
|
|
|
|
$preconnect = [];
|
|
|
|
|
$repoGroup = MediaWikiServices::getInstance()->getRepoGroup();
|
|
|
|
|
$repoGroup->forEachForeignRepo( function ( $repo ) use ( &$preconnect ) {
|
|
|
|
|
$preconnect[] = wfParseUrl( $repo->getZoneUrl( 'thumb' ) )['host'];
|
|
|
|
|
} );
|
|
|
|
|
$preconnect[] = wfParseUrl( $repoGroup->getLocalRepo()->getZoneUrl( 'thumb' ) )['host'];
|
|
|
|
|
foreach ( $preconnect as $host ) {
|
|
|
|
|
if ( $host ) {
|
|
|
|
|
$this->addLink( [ 'rel' => 'preconnect', 'href' => '//' . $host ] );
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-23 17:35:40 +00:00
|
|
|
// Template versioning...
|
|
|
|
|
foreach ( (array)$parserOutput->getTemplateIds() as $ns => $dbks ) {
|
2008-11-01 23:20:25 +00:00
|
|
|
if ( isset( $this->mTemplateIds[$ns] ) ) {
|
|
|
|
|
$this->mTemplateIds[$ns] = $dbks + $this->mTemplateIds[$ns];
|
|
|
|
|
} else {
|
|
|
|
|
$this->mTemplateIds[$ns] = $dbks;
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-03-23 17:35:40 +00:00
|
|
|
// File versioning...
|
2011-09-14 19:05:43 +00:00
|
|
|
foreach ( (array)$parserOutput->getFileSearchOptions() as $dbk => $data ) {
|
2011-03-23 17:35:40 +00:00
|
|
|
$this->mImageTimeKeys[$dbk] = $data;
|
|
|
|
|
}
|
Basic integrated audio/video support, with Ogg implementation.
* JavaScript video player based loosely on Greg Maxwell's player
* Image page text snippet customisation
* Abstraction of transform parameters in the parser. Introduced Linker::makeImageLink2().
* Made canRender(), mustRender() depend on file, not just on handler. Moved width=0, height=0 checking to ImageHandler::canRender(), since audio streams have width=height=0 but should be rendered.
Also:
* Automatic upgrade for oldimage rows on image page view, allows media handler selection based on oi_*_mime
* oi_*_mime unconditionally referenced, REQUIRES SCHEMA UPGRADE
* Don't destroy file info for missing files on upgrade
* Simple, centralised extension message file handling
* Made MessageCache::loadAllMessages non-static, optimised for repeated-call case due to abuse in User.php
* Support for lightweight parser output hooks, with callback whitelist for security
* Moved Linker::formatSize() to Language, to join the new formatTimePeriod() and formatBitrate()
* Introduced MagicWordArray, regex capture trick requires that magic word IDs DO NOT CONTAIN HYPHENS.
2007-08-15 10:50:09 +00:00
|
|
|
|
2008-02-20 08:53:12 +00:00
|
|
|
// Hooks registered in the object
|
2014-08-23 08:52:41 +00:00
|
|
|
$parserOutputHooks = $this->getConfig()->get( 'ParserOutputHooks' );
|
Basic integrated audio/video support, with Ogg implementation.
* JavaScript video player based loosely on Greg Maxwell's player
* Image page text snippet customisation
* Abstraction of transform parameters in the parser. Introduced Linker::makeImageLink2().
* Made canRender(), mustRender() depend on file, not just on handler. Moved width=0, height=0 checking to ImageHandler::canRender(), since audio streams have width=height=0 but should be rendered.
Also:
* Automatic upgrade for oldimage rows on image page view, allows media handler selection based on oi_*_mime
* oi_*_mime unconditionally referenced, REQUIRES SCHEMA UPGRADE
* Don't destroy file info for missing files on upgrade
* Simple, centralised extension message file handling
* Made MessageCache::loadAllMessages non-static, optimised for repeated-call case due to abuse in User.php
* Support for lightweight parser output hooks, with callback whitelist for security
* Moved Linker::formatSize() to Language, to join the new formatTimePeriod() and formatBitrate()
* Introduced MagicWordArray, regex capture trick requires that magic word IDs DO NOT CONTAIN HYPHENS.
2007-08-15 10:50:09 +00:00
|
|
|
foreach ( $parserOutput->getOutputHooks() as $hookInfo ) {
|
|
|
|
|
list( $hookName, $data ) = $hookInfo;
|
2014-08-23 08:52:41 +00:00
|
|
|
if ( isset( $parserOutputHooks[$hookName] ) ) {
|
2018-08-02 18:26:55 +00:00
|
|
|
$parserOutputHooks[$hookName]( $this, $parserOutput, $data );
|
Basic integrated audio/video support, with Ogg implementation.
* JavaScript video player based loosely on Greg Maxwell's player
* Image page text snippet customisation
* Abstraction of transform parameters in the parser. Introduced Linker::makeImageLink2().
* Made canRender(), mustRender() depend on file, not just on handler. Moved width=0, height=0 checking to ImageHandler::canRender(), since audio streams have width=height=0 but should be rendered.
Also:
* Automatic upgrade for oldimage rows on image page view, allows media handler selection based on oi_*_mime
* oi_*_mime unconditionally referenced, REQUIRES SCHEMA UPGRADE
* Don't destroy file info for missing files on upgrade
* Simple, centralised extension message file handling
* Made MessageCache::loadAllMessages non-static, optimised for repeated-call case due to abuse in User.php
* Support for lightweight parser output hooks, with callback whitelist for security
* Moved Linker::formatSize() to Language, to join the new formatTimePeriod() and formatBitrate()
* Introduced MagicWordArray, regex capture trick requires that magic word IDs DO NOT CONTAIN HYPHENS.
2007-08-15 10:50:09 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-10 20:29:27 +00:00
|
|
|
// Enable OOUI if requested via ParserOutput
|
2015-07-25 15:32:08 +00:00
|
|
|
if ( $parserOutput->getEnableOOUI() ) {
|
|
|
|
|
$this->enableOOUI();
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-10 20:29:27 +00:00
|
|
|
// Include parser limit report
|
|
|
|
|
if ( !$this->limitReportJSData ) {
|
|
|
|
|
$this->limitReportJSData = $parserOutput->getLimitReportJSData();
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-19 11:19:06 +00:00
|
|
|
// Link flags are ignored for now, but may in the future be
|
|
|
|
|
// used to mark individual language links.
|
2016-02-17 09:09:32 +00:00
|
|
|
$linkFlags = [];
|
2017-01-01 23:07:11 +00:00
|
|
|
// Avoid PHP 7.1 warning of passing $this by reference
|
|
|
|
|
$outputPage = $this;
|
2016-02-17 09:09:32 +00:00
|
|
|
Hooks::run( 'LanguageLinks', [ $this->getTitle(), &$this->mLanguageLinks, &$linkFlags ] );
|
2017-08-19 00:48:45 +00:00
|
|
|
Hooks::runWithoutAbort( 'OutputPageParserOutput', [ &$outputPage, $parserOutput ] );
|
2017-05-20 13:39:40 +00:00
|
|
|
|
|
|
|
|
// This check must be after 'OutputPageParserOutput' runs in addParserOutputMetadata
|
|
|
|
|
// so that extensions may modify ParserOutput to toggle TOC.
|
|
|
|
|
// This cannot be moved to addParserOutputText because that is not
|
|
|
|
|
// called by EditPage for Preview.
|
2017-11-22 23:12:40 +00:00
|
|
|
if ( $parserOutput->getTOCHTML() ) {
|
2017-05-20 13:39:40 +00:00
|
|
|
$this->mEnableTOC = true;
|
|
|
|
|
}
|
2006-01-01 20:08:08 +00:00
|
|
|
}
|
2006-01-07 13:31:29 +00:00
|
|
|
|
2006-11-07 05:37:31 +00:00
|
|
|
/**
|
2014-05-08 20:10:13 +00:00
|
|
|
* Add the HTML and enhancements for it (like ResourceLoader modules) associated with a
|
|
|
|
|
* ParserOutput object, without any other metadata.
|
|
|
|
|
*
|
|
|
|
|
* @since 1.24
|
|
|
|
|
* @param ParserOutput $parserOutput
|
2017-11-22 20:07:51 +00:00
|
|
|
* @param array $poOptions Options to ParserOutput::getText()
|
2014-05-08 20:10:13 +00:00
|
|
|
*/
|
2018-07-27 20:20:39 +00:00
|
|
|
public function addParserOutputContent( ParserOutput $parserOutput, $poOptions = [] ) {
|
2017-11-22 20:07:51 +00:00
|
|
|
$this->addParserOutputText( $parserOutput, $poOptions );
|
2014-05-08 20:10:13 +00:00
|
|
|
|
|
|
|
|
$this->addModules( $parserOutput->getModules() );
|
|
|
|
|
$this->addModuleStyles( $parserOutput->getModuleStyles() );
|
|
|
|
|
|
|
|
|
|
$this->addJsConfigVars( $parserOutput->getJsConfigVars() );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Add the HTML associated with a ParserOutput object, without any metadata.
|
|
|
|
|
*
|
|
|
|
|
* @since 1.24
|
|
|
|
|
* @param ParserOutput $parserOutput
|
2017-11-22 20:07:51 +00:00
|
|
|
* @param array $poOptions Options to ParserOutput::getText()
|
2014-05-08 20:10:13 +00:00
|
|
|
*/
|
2018-07-27 20:20:39 +00:00
|
|
|
public function addParserOutputText( ParserOutput $parserOutput, $poOptions = [] ) {
|
2017-11-22 20:07:51 +00:00
|
|
|
$text = $parserOutput->getText( $poOptions );
|
2017-01-01 23:07:11 +00:00
|
|
|
// Avoid PHP 7.1 warning of passing $this by reference
|
|
|
|
|
$outputPage = $this;
|
2017-08-19 00:48:45 +00:00
|
|
|
Hooks::runWithoutAbort( 'OutputPageBeforeHTML', [ &$outputPage, &$text ] );
|
2014-05-08 20:10:13 +00:00
|
|
|
$this->addHTML( $text );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Add everything from a ParserOutput object.
|
2010-01-29 21:32:45 +00:00
|
|
|
*
|
2014-04-10 18:50:10 +00:00
|
|
|
* @param ParserOutput $parserOutput
|
2017-11-22 20:07:51 +00:00
|
|
|
* @param array $poOptions Options to ParserOutput::getText()
|
2006-11-07 05:37:31 +00:00
|
|
|
*/
|
2019-09-14 10:26:31 +00:00
|
|
|
public function addParserOutput( ParserOutput $parserOutput, $poOptions = [] ) {
|
2014-05-08 20:10:13 +00:00
|
|
|
$this->addParserOutputMetadata( $parserOutput );
|
2017-11-22 20:07:51 +00:00
|
|
|
$this->addParserOutputText( $parserOutput, $poOptions );
|
2005-07-01 00:03:31 +00:00
|
|
|
}
|
|
|
|
|
|
2004-11-25 06:04:16 +00:00
|
|
|
/**
|
|
|
|
|
* Add the output of a QuickTemplate to the output buffer
|
2006-11-07 05:37:31 +00:00
|
|
|
*
|
2017-08-11 00:23:16 +00:00
|
|
|
* @param QuickTemplate &$template
|
2004-11-25 06:04:16 +00:00
|
|
|
*/
|
2006-11-07 05:37:31 +00:00
|
|
|
public function addTemplate( &$template ) {
|
2013-11-21 07:47:39 +00:00
|
|
|
$this->addHTML( $template->getHTML() );
|
2004-11-25 06:04:16 +00:00
|
|
|
}
|
2004-11-29 11:26:24 +00:00
|
|
|
|
2018-10-26 15:14:01 +00:00
|
|
|
/**
|
|
|
|
|
* Parse wikitext *in the page content language* and return the HTML.
|
|
|
|
|
* The result will be language-converted to the user's preferred variant.
|
|
|
|
|
* Output will be tidy.
|
|
|
|
|
*
|
|
|
|
|
* @param string $text Wikitext in the page content language
|
|
|
|
|
* @param bool $linestart Is this the start of a line? (Defaults to true)
|
|
|
|
|
* @throws MWException
|
|
|
|
|
* @return string HTML
|
2018-11-01 20:30:57 +00:00
|
|
|
* @since 1.32
|
2018-10-26 15:14:01 +00:00
|
|
|
*/
|
|
|
|
|
public function parseAsContent( $text, $linestart = true ) {
|
|
|
|
|
return $this->parseInternal(
|
2020-01-15 19:03:33 +00:00
|
|
|
$text, $this->getTitle(), $linestart, /*interface*/false
|
2018-10-26 14:05:34 +00:00
|
|
|
)->getText( [
|
|
|
|
|
'enableSectionEditLinks' => false,
|
2018-10-26 15:14:01 +00:00
|
|
|
'wrapperDivClass' => ''
|
2018-10-26 14:05:34 +00:00
|
|
|
] );
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-26 15:14:01 +00:00
|
|
|
/**
|
|
|
|
|
* Parse wikitext *in the user interface language* and return the HTML.
|
|
|
|
|
* The result will not be language-converted, as user interface messages
|
|
|
|
|
* are already localized into a specific variant.
|
|
|
|
|
* Output will be tidy.
|
|
|
|
|
*
|
|
|
|
|
* @param string $text Wikitext in the user interface language
|
|
|
|
|
* @param bool $linestart Is this the start of a line? (Defaults to true)
|
|
|
|
|
* @throws MWException
|
|
|
|
|
* @return string HTML
|
2018-11-01 20:30:57 +00:00
|
|
|
* @since 1.32
|
2018-10-26 15:14:01 +00:00
|
|
|
*/
|
|
|
|
|
public function parseAsInterface( $text, $linestart = true ) {
|
|
|
|
|
return $this->parseInternal(
|
2020-01-15 19:03:33 +00:00
|
|
|
$text, $this->getTitle(), $linestart, /*interface*/true
|
2018-10-26 15:14:01 +00:00
|
|
|
)->getText( [
|
|
|
|
|
'enableSectionEditLinks' => false,
|
|
|
|
|
'wrapperDivClass' => ''
|
|
|
|
|
] );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Parse wikitext *in the user interface language*, strip
|
|
|
|
|
* paragraph wrapper, and return the HTML.
|
|
|
|
|
* The result will not be language-converted, as user interface messages
|
|
|
|
|
* are already localized into a specific variant.
|
|
|
|
|
* Output will be tidy. Outer paragraph wrapper will only be stripped
|
|
|
|
|
* if the result is a single paragraph.
|
|
|
|
|
*
|
|
|
|
|
* @param string $text Wikitext in the user interface language
|
|
|
|
|
* @param bool $linestart Is this the start of a line? (Defaults to true)
|
|
|
|
|
* @throws MWException
|
|
|
|
|
* @return string HTML
|
2018-11-01 20:30:57 +00:00
|
|
|
* @since 1.32
|
2018-10-26 15:14:01 +00:00
|
|
|
*/
|
|
|
|
|
public function parseInlineAsInterface( $text, $linestart = true ) {
|
|
|
|
|
return Parser::stripOuterParagraph(
|
|
|
|
|
$this->parseAsInterface( $text, $linestart )
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-26 14:05:34 +00:00
|
|
|
/**
|
|
|
|
|
* Parse wikitext and return the HTML (internal implementation helper)
|
|
|
|
|
*
|
|
|
|
|
* @param string $text
|
2019-04-06 06:19:29 +00:00
|
|
|
* @param Title $title The title to use
|
2018-10-26 14:05:34 +00:00
|
|
|
* @param bool $linestart Is this the start of a line?
|
|
|
|
|
* @param bool $interface Use interface language (instead of content language) while parsing
|
|
|
|
|
* language sensitive magic words like GRAMMAR and PLURAL. This also disables
|
|
|
|
|
* LanguageConverter.
|
|
|
|
|
* @throws MWException
|
|
|
|
|
* @return ParserOutput
|
|
|
|
|
*/
|
2020-01-15 19:03:33 +00:00
|
|
|
private function parseInternal( $text, $title, $linestart, $interface ) {
|
2020-01-09 23:48:34 +00:00
|
|
|
if ( $title === null ) {
|
2009-04-09 02:22:36 +00:00
|
|
|
throw new MWException( 'Empty $mTitle in ' . __METHOD__ );
|
2008-11-24 01:58:15 +00:00
|
|
|
}
|
2011-01-05 12:24:39 +00:00
|
|
|
|
2006-07-26 07:15:39 +00:00
|
|
|
$popts = $this->parserOptions();
|
2020-04-01 21:36:23 +00:00
|
|
|
|
2018-10-26 15:14:01 +00:00
|
|
|
$oldInterface = $popts->setInterfaceMessage( (bool)$interface );
|
|
|
|
|
|
2019-04-11 13:36:15 +00:00
|
|
|
$parserOutput = MediaWikiServices::getInstance()->getParser()->getFreshParser()->parse(
|
2018-10-26 15:14:01 +00:00
|
|
|
$text, $title, $popts,
|
2010-05-22 12:18:22 +00:00
|
|
|
$linestart, true, $this->mRevisionId
|
|
|
|
|
);
|
2011-01-05 12:24:39 +00:00
|
|
|
|
2018-10-26 15:14:01 +00:00
|
|
|
$popts->setInterfaceMessage( $oldInterface );
|
|
|
|
|
|
2018-10-26 14:05:34 +00:00
|
|
|
return $parserOutput;
|
2009-01-31 01:59:13 +00:00
|
|
|
}
|
|
|
|
|
|
2004-09-02 23:28:24 +00:00
|
|
|
/**
|
2010-01-29 21:32:45 +00:00
|
|
|
* Set the value of the "s-maxage" part of the "Cache-control" HTTP header
|
|
|
|
|
*
|
2015-12-10 01:07:05 +00:00
|
|
|
* @param int $maxage Maximum cache time on the CDN, in seconds.
|
2004-09-02 23:28:24 +00:00
|
|
|
*/
|
2015-12-10 01:07:05 +00:00
|
|
|
public function setCdnMaxage( $maxage ) {
|
|
|
|
|
$this->mCdnMaxage = min( $maxage, $this->mCdnMaxageLimit );
|
2015-10-01 07:24:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2018-08-02 18:26:55 +00:00
|
|
|
* Set the value of the "s-maxage" part of the "Cache-control" HTTP header to $maxage if that is
|
|
|
|
|
* lower than the current s-maxage. Either way, $maxage is now an upper limit on s-maxage, so
|
|
|
|
|
* that future calls to setCdnMaxage() will no longer be able to raise the s-maxage above
|
|
|
|
|
* $maxage.
|
2015-10-01 07:24:18 +00:00
|
|
|
*
|
2015-12-10 01:07:05 +00:00
|
|
|
* @param int $maxage Maximum cache time on the CDN, in seconds
|
2015-10-01 07:24:18 +00:00
|
|
|
* @since 1.27
|
|
|
|
|
*/
|
|
|
|
|
public function lowerCdnMaxage( $maxage ) {
|
2015-10-10 03:55:47 +00:00
|
|
|
$this->mCdnMaxageLimit = min( $maxage, $this->mCdnMaxageLimit );
|
2015-12-10 01:07:05 +00:00
|
|
|
$this->setCdnMaxage( $this->mCdnMaxage );
|
2004-02-08 21:12:07 +00:00
|
|
|
}
|
2004-07-08 14:53:54 +00:00
|
|
|
|
2016-08-23 04:10:13 +00:00
|
|
|
/**
|
2019-09-15 14:40:42 +00:00
|
|
|
* Get TTL in [$minTTL,$maxTTL] and pass it to lowerCdnMaxage()
|
2016-08-23 04:10:13 +00:00
|
|
|
*
|
|
|
|
|
* This sets and returns $minTTL if $mtime is false or null. Otherwise,
|
|
|
|
|
* the TTL is higher the older the $mtime timestamp is. Essentially, the
|
|
|
|
|
* TTL is 90% of the age of the object, subject to the min and max.
|
|
|
|
|
*
|
2017-08-20 11:20:59 +00:00
|
|
|
* @param string|int|float|bool|null $mtime Last-Modified timestamp
|
2018-08-02 18:26:55 +00:00
|
|
|
* @param int $minTTL Minimum TTL in seconds [default: 1 minute]
|
2017-11-01 20:55:24 +00:00
|
|
|
* @param int $maxTTL Maximum TTL in seconds [default: $wgCdnMaxAge]
|
2016-08-23 04:10:13 +00:00
|
|
|
* @since 1.28
|
|
|
|
|
*/
|
|
|
|
|
public function adaptCdnTTL( $mtime, $minTTL = 0, $maxTTL = 0 ) {
|
|
|
|
|
$minTTL = $minTTL ?: IExpiringStore::TTL_MINUTE;
|
2017-11-01 20:55:24 +00:00
|
|
|
$maxTTL = $maxTTL ?: $this->getConfig()->get( 'CdnMaxAge' );
|
2016-08-23 04:10:13 +00:00
|
|
|
|
|
|
|
|
if ( $mtime === null || $mtime === false ) {
|
2019-09-15 14:40:42 +00:00
|
|
|
return; // entity does not exist
|
2016-08-23 04:10:13 +00:00
|
|
|
}
|
|
|
|
|
|
2019-09-15 14:40:42 +00:00
|
|
|
$age = MWTimestamp::time() - (int)wfTimestamp( TS_UNIX, $mtime );
|
2017-09-10 19:11:37 +00:00
|
|
|
$adaptiveTTL = max( 0.9 * $age, $minTTL );
|
2016-08-23 04:10:13 +00:00
|
|
|
$adaptiveTTL = min( $adaptiveTTL, $maxTTL );
|
|
|
|
|
|
|
|
|
|
$this->lowerCdnMaxage( (int)$adaptiveTTL );
|
|
|
|
|
}
|
|
|
|
|
|
2004-09-02 23:28:24 +00:00
|
|
|
/**
|
|
|
|
|
* Use enableClientCache(false) to force it to send nocache headers
|
2010-01-29 21:32:45 +00:00
|
|
|
*
|
2018-08-02 18:26:55 +00:00
|
|
|
* @param bool|null $state New value, or null to not set the value
|
2011-05-21 19:07:24 +00:00
|
|
|
*
|
2018-08-02 18:26:55 +00:00
|
|
|
* @return bool Old value
|
2004-09-02 23:28:24 +00:00
|
|
|
*/
|
2006-11-07 05:37:31 +00:00
|
|
|
public function enableClientCache( $state ) {
|
2004-03-23 10:19:31 +00:00
|
|
|
return wfSetVar( $this->mEnableClientCache, $state );
|
|
|
|
|
}
|
2004-07-08 14:53:54 +00:00
|
|
|
|
2019-09-07 23:44:46 +00:00
|
|
|
/**
|
|
|
|
|
* Whether the output might become publicly cached.
|
|
|
|
|
*
|
|
|
|
|
* @since 1.34
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
public function couldBePublicCached() {
|
|
|
|
|
if ( !$this->cacheIsFinal ) {
|
|
|
|
|
// - The entry point handles its own caching and/or doesn't use OutputPage.
|
|
|
|
|
// (such as load.php, AjaxDispatcher, or MediaWiki\Rest\EntryPoint).
|
|
|
|
|
//
|
|
|
|
|
// - Or, we haven't finished processing the main part of the request yet
|
|
|
|
|
// (e.g. Action::show, SpecialPage::execute), and the state may still
|
|
|
|
|
// change via enableClientCache().
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
// e.g. various error-type pages disable all client caching
|
|
|
|
|
return $this->mEnableClientCache;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Set the expectation that cache control will not change after this point.
|
|
|
|
|
*
|
|
|
|
|
* This should be called after the main processing logic has completed
|
|
|
|
|
* (e.g. Action::show or SpecialPage::execute), but may be called
|
|
|
|
|
* before Skin output has started (OutputPage::output).
|
|
|
|
|
*
|
|
|
|
|
* @since 1.34
|
|
|
|
|
*/
|
|
|
|
|
public function considerCacheSettingsFinal() {
|
|
|
|
|
$this->cacheIsFinal = true;
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-29 21:32:45 +00:00
|
|
|
/**
|
2018-08-02 18:26:55 +00:00
|
|
|
* Get the list of cookie names that will influence the cache
|
2010-01-29 21:32:45 +00:00
|
|
|
*
|
2014-04-10 18:50:10 +00:00
|
|
|
* @return array
|
2010-01-29 21:32:45 +00:00
|
|
|
*/
|
2019-09-14 10:26:31 +00:00
|
|
|
public function getCacheVaryCookies() {
|
2018-08-02 18:42:17 +00:00
|
|
|
if ( self::$cacheVaryCookies === null ) {
|
2014-08-23 08:52:41 +00:00
|
|
|
$config = $this->getConfig();
|
2018-08-02 18:42:17 +00:00
|
|
|
self::$cacheVaryCookies = array_values( array_unique( array_merge(
|
2016-02-01 20:44:03 +00:00
|
|
|
SessionManager::singleton()->getVaryCookies(),
|
2016-02-17 09:09:32 +00:00
|
|
|
[
|
2016-02-01 20:44:03 +00:00
|
|
|
'forceHTTPS',
|
2016-02-17 09:09:32 +00:00
|
|
|
],
|
2014-08-23 08:52:41 +00:00
|
|
|
$config->get( 'CacheVaryCookies' )
|
2018-08-02 18:42:17 +00:00
|
|
|
) ) );
|
|
|
|
|
Hooks::run( 'GetCacheVaryCookies', [ $this, &self::$cacheVaryCookies ] );
|
2008-04-10 08:42:36 +00:00
|
|
|
}
|
2018-08-02 18:42:17 +00:00
|
|
|
return self::$cacheVaryCookies;
|
2008-02-20 04:13:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Check if the request has a cache-varying cookie header
|
|
|
|
|
* If it does, it's very important that we don't allow public caching
|
2010-01-29 21:32:45 +00:00
|
|
|
*
|
2014-04-10 18:50:10 +00:00
|
|
|
* @return bool
|
2008-02-20 04:13:24 +00:00
|
|
|
*/
|
2019-09-14 10:26:31 +00:00
|
|
|
public function haveCacheVaryCookies() {
|
2015-10-06 07:01:10 +00:00
|
|
|
$request = $this->getRequest();
|
|
|
|
|
foreach ( $this->getCacheVaryCookies() as $cookieName ) {
|
2015-10-08 04:45:26 +00:00
|
|
|
if ( $request->getCookie( $cookieName, '', '' ) !== '' ) {
|
2010-05-22 12:18:22 +00:00
|
|
|
wfDebug( __METHOD__ . ": found $cookieName\n" );
|
2008-02-20 04:13:24 +00:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-05-22 12:18:22 +00:00
|
|
|
wfDebug( __METHOD__ . ": no cache-varying cookies found\n" );
|
2008-02-20 04:13:24 +00:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-29 21:32:45 +00:00
|
|
|
/**
|
|
|
|
|
* Add an HTTP header that will influence on the cache
|
|
|
|
|
*
|
2014-07-24 17:42:24 +00:00
|
|
|
* @param string $header Header name
|
2019-06-19 18:22:42 +00:00
|
|
|
* @param string[]|null $option Deprecated; formerly options for the
|
|
|
|
|
* Key header, deprecated in 1.32 and removed in 1.34. See
|
|
|
|
|
* https://datatracker.ietf.org/doc/draft-fielding-http-key/
|
|
|
|
|
* for the list of formerly-valid options.
|
2010-01-29 21:32:45 +00:00
|
|
|
*/
|
2015-09-08 21:59:45 +00:00
|
|
|
public function addVaryHeader( $header, array $option = null ) {
|
2019-06-19 18:22:42 +00:00
|
|
|
if ( $option !== null && count( $option ) > 0 ) {
|
|
|
|
|
wfDeprecated( 'addVaryHeader $option is ignored', '1.34' );
|
2015-09-08 21:59:45 +00:00
|
|
|
}
|
2019-06-19 18:22:42 +00:00
|
|
|
if ( !array_key_exists( $header, $this->mVaryHeader ) ) {
|
|
|
|
|
$this->mVaryHeader[$header] = null;
|
2009-11-28 19:13:23 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-06-01 10:23:53 +00:00
|
|
|
/**
|
|
|
|
|
* Return a Vary: header on which to vary caches. Based on the keys of $mVaryHeader,
|
|
|
|
|
* such as Accept-Encoding or Cookie
|
2012-08-08 08:42:17 +00:00
|
|
|
*
|
2014-04-10 18:50:10 +00:00
|
|
|
* @return string
|
2012-06-01 10:23:53 +00:00
|
|
|
*/
|
|
|
|
|
public function getVaryHeader() {
|
2016-01-31 20:43:00 +00:00
|
|
|
// If we vary on cookies, let's make sure it's always included here too.
|
|
|
|
|
if ( $this->getCacheVaryCookies() ) {
|
|
|
|
|
$this->addVaryHeader( 'Cookie' );
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-01 20:44:03 +00:00
|
|
|
foreach ( SessionManager::singleton()->getVaryHeaders() as $header => $options ) {
|
|
|
|
|
$this->addVaryHeader( $header, $options );
|
|
|
|
|
}
|
2016-03-08 08:13:12 +00:00
|
|
|
return 'Vary: ' . implode( ', ', array_keys( $this->mVaryHeader ) );
|
2012-06-01 10:23:53 +00:00
|
|
|
}
|
|
|
|
|
|
2015-06-01 16:58:42 +00:00
|
|
|
/**
|
|
|
|
|
* Add an HTTP Link: header
|
|
|
|
|
*
|
|
|
|
|
* @param string $header Header value
|
|
|
|
|
*/
|
|
|
|
|
public function addLinkHeader( $header ) {
|
|
|
|
|
$this->mLinkHeader[] = $header;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return a Link: header. Based on the values of $mLinkHeader.
|
|
|
|
|
*
|
2019-09-15 14:40:42 +00:00
|
|
|
* @return string|false
|
2015-06-01 16:58:42 +00:00
|
|
|
*/
|
|
|
|
|
public function getLinkHeader() {
|
|
|
|
|
if ( !$this->mLinkHeader ) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 'Link: ' . implode( ',', $this->mLinkHeader );
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-29 21:32:45 +00:00
|
|
|
/**
|
2018-10-18 16:01:23 +00:00
|
|
|
* T23672: Add Accept-Language to Vary header if there's no 'variant' parameter in GET.
|
2010-01-29 21:32:45 +00:00
|
|
|
*
|
|
|
|
|
* For example:
|
2018-08-02 18:26:55 +00:00
|
|
|
* /w/index.php?title=Main_page will vary based on Accept-Language; but
|
|
|
|
|
* /w/index.php?title=Main_page&variant=zh-cn will not.
|
2010-01-29 21:32:45 +00:00
|
|
|
*/
|
2018-08-02 18:26:55 +00:00
|
|
|
private function addAcceptLanguage() {
|
2014-06-11 20:00:24 +00:00
|
|
|
$title = $this->getTitle();
|
|
|
|
|
if ( !$title instanceof Title ) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$lang = $title->getPageLanguage();
|
2013-04-17 14:52:47 +00:00
|
|
|
if ( !$this->getRequest()->getCheck( 'variant' ) && $lang->hasVariants() ) {
|
2019-06-19 18:22:42 +00:00
|
|
|
$this->addVaryHeader( 'Accept-Language' );
|
2009-12-21 18:55:42 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-01-04 06:12:33 +00:00
|
|
|
/**
|
2011-04-23 19:28:35 +00:00
|
|
|
* Set a flag which will cause an X-Frame-Options header appropriate for
|
|
|
|
|
* edit pages to be sent. The header value is controlled by
|
2011-01-04 06:12:33 +00:00
|
|
|
* $wgEditPageFrameOptions.
|
|
|
|
|
*
|
2011-04-23 19:28:35 +00:00
|
|
|
* This is the default for special pages. If you display a CSRF-protected
|
2011-01-04 06:12:33 +00:00
|
|
|
* form on an ordinary view page, then you need to call this function.
|
2011-05-21 19:07:24 +00:00
|
|
|
*
|
2014-04-10 18:50:10 +00:00
|
|
|
* @param bool $enable
|
2011-01-04 06:12:33 +00:00
|
|
|
*/
|
|
|
|
|
public function preventClickjacking( $enable = true ) {
|
|
|
|
|
$this->mPreventClickjacking = $enable;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Turn off frame-breaking. Alias for $this->preventClickjacking(false).
|
|
|
|
|
* This can be called from pages which do not contain any CSRF-protected
|
|
|
|
|
* HTML form.
|
|
|
|
|
*/
|
|
|
|
|
public function allowClickjacking() {
|
|
|
|
|
$this->mPreventClickjacking = false;
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-10 19:16:29 +00:00
|
|
|
/**
|
|
|
|
|
* Get the prevent-clickjacking flag
|
|
|
|
|
*
|
|
|
|
|
* @since 1.24
|
2014-08-04 10:00:15 +00:00
|
|
|
* @return bool
|
2014-07-10 19:16:29 +00:00
|
|
|
*/
|
|
|
|
|
public function getPreventClickjacking() {
|
|
|
|
|
return $this->mPreventClickjacking;
|
|
|
|
|
}
|
|
|
|
|
|
2011-01-04 06:12:33 +00:00
|
|
|
/**
|
2011-04-23 19:28:35 +00:00
|
|
|
* Get the X-Frame-Options header value (without the name part), or false
|
|
|
|
|
* if there isn't one. This is used by Skin to determine whether to enable
|
2011-01-04 06:12:33 +00:00
|
|
|
* JavaScript frame-breaking, for clients that don't support X-Frame-Options.
|
2011-05-21 19:07:24 +00:00
|
|
|
*
|
2016-12-08 05:04:53 +00:00
|
|
|
* @return string|false
|
2011-01-04 06:12:33 +00:00
|
|
|
*/
|
|
|
|
|
public function getFrameOptions() {
|
2014-08-23 08:52:41 +00:00
|
|
|
$config = $this->getConfig();
|
|
|
|
|
if ( $config->get( 'BreakFrames' ) ) {
|
2011-01-04 06:12:33 +00:00
|
|
|
return 'DENY';
|
2014-08-23 08:52:41 +00:00
|
|
|
} elseif ( $this->mPreventClickjacking && $config->get( 'EditPageFrameOptions' ) ) {
|
|
|
|
|
return $config->get( 'EditPageFrameOptions' );
|
2011-01-04 06:12:33 +00:00
|
|
|
}
|
2011-10-16 03:27:12 +00:00
|
|
|
return false;
|
2011-01-04 06:12:33 +00:00
|
|
|
}
|
|
|
|
|
|
2019-02-20 08:49:28 +00:00
|
|
|
/**
|
|
|
|
|
* Get the Origin-Trial header values. This is used to enable Chrome Origin
|
|
|
|
|
* Trials: https://github.com/GoogleChrome/OriginTrials
|
|
|
|
|
*
|
|
|
|
|
* @return array
|
|
|
|
|
*/
|
|
|
|
|
private function getOriginTrials() {
|
|
|
|
|
$config = $this->getConfig();
|
|
|
|
|
|
|
|
|
|
return $config->get( 'OriginTrials' );
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-22 05:43:30 +00:00
|
|
|
private function getReportTo() {
|
|
|
|
|
$config = $this->getConfig();
|
|
|
|
|
|
|
|
|
|
$expiry = $config->get( 'ReportToExpiry' );
|
|
|
|
|
|
|
|
|
|
if ( !$expiry ) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$endpoints = $config->get( 'ReportToEndpoints' );
|
|
|
|
|
|
|
|
|
|
if ( !$endpoints ) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$output = [ 'max_age' => $expiry, 'endpoints' => [] ];
|
|
|
|
|
|
|
|
|
|
foreach ( $endpoints as $endpoint ) {
|
|
|
|
|
$output['endpoints'][] = [ 'url' => $endpoint ];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return json_encode( $output, JSON_UNESCAPED_SLASHES );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function getFeaturePolicyReportOnly() {
|
|
|
|
|
$config = $this->getConfig();
|
|
|
|
|
|
|
|
|
|
$features = $config->get( 'FeaturePolicyReportOnly' );
|
|
|
|
|
return implode( ';', $features );
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-29 21:32:45 +00:00
|
|
|
/**
|
|
|
|
|
* Send cache control HTTP headers
|
|
|
|
|
*/
|
2006-11-07 05:37:31 +00:00
|
|
|
public function sendCacheControl() {
|
2011-04-03 12:46:36 +00:00
|
|
|
$response = $this->getRequest()->response();
|
2014-08-23 08:52:41 +00:00
|
|
|
$config = $this->getConfig();
|
2008-12-23 19:39:00 +00:00
|
|
|
|
2012-06-01 10:23:53 +00:00
|
|
|
$this->addVaryHeader( 'Cookie' );
|
2009-12-21 18:55:42 +00:00
|
|
|
$this->addAcceptLanguage();
|
|
|
|
|
|
2008-12-23 19:39:00 +00:00
|
|
|
# don't serve compressed data to clients who can't handle it
|
2005-03-08 02:58:43 +00:00
|
|
|
# maintain different caches for logged-in users and non-logged in ones
|
2012-06-01 10:23:53 +00:00
|
|
|
$response->header( $this->getVaryHeader() );
|
2008-02-08 07:12:38 +00:00
|
|
|
|
2013-04-17 14:52:47 +00:00
|
|
|
if ( $this->mEnableClientCache ) {
|
|
|
|
|
if (
|
2017-11-01 20:55:24 +00:00
|
|
|
$config->get( 'UseCdn' ) &&
|
2016-02-24 06:03:17 +00:00
|
|
|
!$response->hasCookies() &&
|
2019-09-07 23:44:46 +00:00
|
|
|
// The client might use methods other than cookies to appear logged-in.
|
|
|
|
|
// E.g. HTTP headers, or query parameter tokens, OAuth, etc.
|
2016-02-24 06:03:17 +00:00
|
|
|
!SessionManager::getGlobalSession()->isPersistent() &&
|
|
|
|
|
!$this->isPrintable() &&
|
|
|
|
|
$this->mCdnMaxage != 0 &&
|
|
|
|
|
!$this->haveCacheVaryCookies()
|
2013-04-17 14:52:47 +00:00
|
|
|
) {
|
2019-09-08 04:11:22 +00:00
|
|
|
# We'll purge the proxy cache for anons explicitly, but require end user agents
|
|
|
|
|
# to revalidate against the proxy on each visit.
|
|
|
|
|
# IMPORTANT! The CDN 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} **", 'private' );
|
|
|
|
|
# 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->mCdnMaxage}, must-revalidate, max-age=0" );
|
2004-01-31 10:29:31 +00:00
|
|
|
} else {
|
|
|
|
|
# We do want clients to cache if they can, but they *must* check for updates
|
2019-08-26 13:16:43 +00:00
|
|
|
# on revisiting the page, after the max-age period.
|
2015-12-30 19:48:01 +00:00
|
|
|
wfDebug( __METHOD__ . ": private caching; {$this->mLastModified} **", 'private' );
|
2019-08-26 13:16:43 +00:00
|
|
|
|
|
|
|
|
if ( $response->hasCookies() || SessionManager::getGlobalSession()->isPersistent() ) {
|
|
|
|
|
$response->header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', 0 ) . ' GMT' );
|
|
|
|
|
$response->header( "Cache-Control: private, must-revalidate, max-age=0" );
|
|
|
|
|
} else {
|
|
|
|
|
$response->header(
|
|
|
|
|
'Expires: ' . gmdate( 'D, d M Y H:i:s', time() + $config->get( 'LoggedOutMaxAge' ) ) . ' GMT'
|
|
|
|
|
);
|
|
|
|
|
$response->header(
|
|
|
|
|
"Cache-Control: private, must-revalidate, max-age={$config->get( 'LoggedOutMaxAge' )}"
|
|
|
|
|
);
|
|
|
|
|
}
|
2004-01-31 10:29:31 +00:00
|
|
|
}
|
2013-04-17 14:52:47 +00:00
|
|
|
if ( $this->mLastModified ) {
|
2008-08-29 08:40:13 +00:00
|
|
|
$response->header( "Last-Modified: {$this->mLastModified}" );
|
|
|
|
|
}
|
2003-07-03 10:18:07 +00:00
|
|
|
} else {
|
2015-12-30 19:48:01 +00:00
|
|
|
wfDebug( __METHOD__ . ": no caching **", 'private' );
|
2004-03-23 10:19:31 +00:00
|
|
|
|
|
|
|
|
# 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.
|
2008-02-08 07:12:38 +00:00
|
|
|
$response->header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', 0 ) . ' GMT' );
|
|
|
|
|
$response->header( 'Cache-Control: no-cache, no-store, max-age=0, must-revalidate' );
|
|
|
|
|
$response->header( 'Pragma: no-cache' );
|
2003-07-03 10:18:07 +00:00
|
|
|
}
|
|
|
|
|
}
|
2004-07-08 14:53:54 +00:00
|
|
|
|
2018-04-10 01:22:13 +00:00
|
|
|
/**
|
|
|
|
|
* Transfer styles and JavaScript modules from skin.
|
|
|
|
|
*
|
|
|
|
|
* @param Skin $sk to load modules for
|
|
|
|
|
*/
|
|
|
|
|
public function loadSkinModules( $sk ) {
|
|
|
|
|
foreach ( $sk->getDefaultModules() as $group => $modules ) {
|
|
|
|
|
if ( $group === 'styles' ) {
|
|
|
|
|
foreach ( $modules as $key => $moduleMembers ) {
|
|
|
|
|
$this->addModuleStyles( $moduleMembers );
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
$this->addModules( $modules );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2004-09-02 23:28:24 +00:00
|
|
|
/**
|
|
|
|
|
* Finally, all the text has been munged and accumulated into
|
|
|
|
|
* the object, let's actually output it:
|
2016-08-24 21:22:11 +00:00
|
|
|
*
|
|
|
|
|
* @param bool $return Set to true to get the result as a string rather than sending it
|
|
|
|
|
* @return string|null
|
|
|
|
|
* @throws Exception
|
|
|
|
|
* @throws FatalError
|
|
|
|
|
* @throws MWException
|
2004-09-02 23:28:24 +00:00
|
|
|
*/
|
2016-08-24 21:22:11 +00:00
|
|
|
public function output( $return = false ) {
|
2013-04-17 14:52:47 +00:00
|
|
|
if ( $this->mDoNothing ) {
|
2016-08-24 21:22:11 +00:00
|
|
|
return $return ? '' : null;
|
2008-12-31 17:56:04 +00:00
|
|
|
}
|
2011-04-02 18:38:42 +00:00
|
|
|
|
2011-04-03 12:46:36 +00:00
|
|
|
$response = $this->getRequest()->response();
|
2014-08-23 08:52:41 +00:00
|
|
|
$config = $this->getConfig();
|
2011-04-02 18:38:42 +00:00
|
|
|
|
2010-01-06 19:59:42 +00:00
|
|
|
if ( $this->mRedirect != '' ) {
|
2008-02-13 05:59:14 +00:00
|
|
|
# Standards require redirect URLs to be absolute
|
2011-08-19 15:46:08 +00:00
|
|
|
$this->mRedirect = wfExpandUrl( $this->mRedirect, PROTO_CURRENT );
|
2011-12-14 00:38:21 +00:00
|
|
|
|
|
|
|
|
$redirect = $this->mRedirect;
|
|
|
|
|
$code = $this->mRedirectCode;
|
|
|
|
|
|
2016-02-17 09:09:32 +00:00
|
|
|
if ( Hooks::run( "BeforePageRedirect", [ $this, &$redirect, &$code ] ) ) {
|
2013-04-17 14:52:47 +00:00
|
|
|
if ( $code == '301' || $code == '303' ) {
|
2014-08-23 08:52:41 +00:00
|
|
|
if ( !$config->get( 'DebugRedirects' ) ) {
|
2015-05-24 12:31:11 +00:00
|
|
|
$response->statusHeader( $code );
|
2011-12-14 00:38:21 +00:00
|
|
|
}
|
|
|
|
|
$this->mLastModified = wfTimestamp( TS_RFC2822 );
|
|
|
|
|
}
|
2014-08-23 08:52:41 +00:00
|
|
|
if ( $config->get( 'VaryOnXFP' ) ) {
|
2011-12-14 00:38:21 +00:00
|
|
|
$this->addVaryHeader( 'X-Forwarded-Proto' );
|
|
|
|
|
}
|
|
|
|
|
$this->sendCacheControl();
|
|
|
|
|
|
|
|
|
|
$response->header( "Content-Type: text/html; charset=utf-8" );
|
2014-08-23 08:52:41 +00:00
|
|
|
if ( $config->get( 'DebugRedirects' ) ) {
|
2011-12-14 00:38:21 +00:00
|
|
|
$url = htmlspecialchars( $redirect );
|
2016-09-25 15:34:47 +00:00
|
|
|
print "<!DOCTYPE html>\n<html>\n<head>\n<title>Redirect</title>\n</head>\n<body>\n";
|
2011-12-14 00:38:21 +00:00
|
|
|
print "<p>Location: <a href=\"$url\">$url</a></p>\n";
|
|
|
|
|
print "</body>\n</html>\n";
|
|
|
|
|
} else {
|
|
|
|
|
$response->header( 'Location: ' . $redirect );
|
2004-03-05 03:18:31 +00:00
|
|
|
}
|
|
|
|
|
}
|
2011-12-14 00:38:21 +00:00
|
|
|
|
2016-08-24 21:22:11 +00:00
|
|
|
return $return ? '' : null;
|
2009-12-01 20:56:43 +00:00
|
|
|
} elseif ( $this->mStatusCode ) {
|
2015-05-24 12:31:11 +00:00
|
|
|
$response->statusHeader( $this->mStatusCode );
|
2008-10-10 01:15:11 +00:00
|
|
|
}
|
2010-05-22 11:47:56 +00:00
|
|
|
|
2004-11-12 09:34:11 +00:00
|
|
|
# Buffer output; final headers may depend on later processing
|
2008-09-15 01:27:22 +00:00
|
|
|
ob_start();
|
2004-11-12 09:34:11 +00:00
|
|
|
|
2014-08-23 08:52:41 +00:00
|
|
|
$response->header( 'Content-type: ' . $config->get( 'MimeType' ) . '; charset=UTF-8' );
|
2018-07-29 12:24:54 +00:00
|
|
|
$response->header( 'Content-language: ' .
|
|
|
|
|
MediaWikiServices::getInstance()->getContentLanguage()->getHtmlCode() );
|
2003-04-14 23:10:40 +00:00
|
|
|
|
2015-06-01 16:58:42 +00:00
|
|
|
$linkHeader = $this->getLinkHeader();
|
|
|
|
|
if ( $linkHeader ) {
|
|
|
|
|
$response->header( $linkHeader );
|
|
|
|
|
}
|
|
|
|
|
|
2011-01-04 06:12:33 +00:00
|
|
|
// Prevent framing, if requested
|
|
|
|
|
$frameOptions = $this->getFrameOptions();
|
|
|
|
|
if ( $frameOptions ) {
|
2011-04-02 18:38:42 +00:00
|
|
|
$response->header( "X-Frame-Options: $frameOptions" );
|
2011-01-04 06:12:33 +00:00
|
|
|
}
|
|
|
|
|
|
2019-02-20 08:49:28 +00:00
|
|
|
$originTrials = $this->getOriginTrials();
|
|
|
|
|
foreach ( $originTrials as $originTrial ) {
|
|
|
|
|
$response->header( "Origin-Trial: $originTrial", false );
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-22 05:43:30 +00:00
|
|
|
$reportTo = $this->getReportTo();
|
|
|
|
|
if ( $reportTo ) {
|
|
|
|
|
$response->header( "Report-To: $reportTo" );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$featurePolicyReportOnly = $this->getFeaturePolicyReportOnly();
|
|
|
|
|
if ( $featurePolicyReportOnly ) {
|
|
|
|
|
$response->header( "Feature-Policy-Report-Only: $featurePolicyReportOnly" );
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-22 12:18:22 +00:00
|
|
|
if ( $this->mArticleBodyOnly ) {
|
2020-03-02 09:11:14 +00:00
|
|
|
$this->CSP->sendHeaders();
|
2013-05-12 09:00:16 +00:00
|
|
|
echo $this->mBodytext;
|
2005-07-03 04:00:33 +00:00
|
|
|
} else {
|
2018-03-28 00:57:06 +00:00
|
|
|
// Enable safe mode if requested (T152169)
|
2017-03-29 21:01:11 +00:00
|
|
|
if ( $this->getRequest()->getBool( 'safemode' ) ) {
|
|
|
|
|
$this->disallowUserJs();
|
|
|
|
|
}
|
|
|
|
|
|
2011-06-22 18:24:58 +00:00
|
|
|
$sk = $this->getSkin();
|
2018-04-10 01:22:13 +00:00
|
|
|
$this->loadSkinModules( $sk );
|
2017-05-20 13:07:37 +00:00
|
|
|
|
2013-05-13 22:09:33 +00:00
|
|
|
MWDebug::addModules( $this );
|
2011-03-11 14:31:12 +00:00
|
|
|
|
2017-01-01 23:07:11 +00:00
|
|
|
// Avoid PHP 7.1 warning of passing $this by reference
|
|
|
|
|
$outputPage = $this;
|
2008-04-06 20:25:45 +00:00
|
|
|
// Hook that allows last minute changes to the output page, e.g.
|
2020-03-02 09:11:14 +00:00
|
|
|
// adding of CSS or Javascript by extensions, adding CSP sources.
|
2017-08-19 00:48:45 +00:00
|
|
|
Hooks::runWithoutAbort( 'BeforePageDisplay', [ &$outputPage, &$sk ] );
|
2008-04-06 20:25:45 +00:00
|
|
|
|
2020-03-02 09:11:14 +00:00
|
|
|
$this->CSP->sendHeaders();
|
|
|
|
|
|
2016-03-18 19:11:45 +00:00
|
|
|
try {
|
|
|
|
|
$sk->outputPage();
|
|
|
|
|
} catch ( Exception $e ) {
|
|
|
|
|
ob_end_clean(); // bug T129657
|
|
|
|
|
throw $e;
|
|
|
|
|
}
|
2005-07-03 04:00:33 +00:00
|
|
|
}
|
2005-07-01 00:03:31 +00:00
|
|
|
|
2016-03-18 19:11:45 +00:00
|
|
|
try {
|
|
|
|
|
// This hook allows last minute changes to final overall output by modifying output buffer
|
2017-08-19 00:48:45 +00:00
|
|
|
Hooks::runWithoutAbort( 'AfterFinalPageOutput', [ $this ] );
|
2016-03-18 19:11:45 +00:00
|
|
|
} catch ( Exception $e ) {
|
|
|
|
|
ob_end_clean(); // bug T129657
|
|
|
|
|
throw $e;
|
|
|
|
|
}
|
2012-07-05 14:21:21 +00:00
|
|
|
|
2004-11-12 09:34:11 +00:00
|
|
|
$this->sendCacheControl();
|
2012-07-04 21:23:56 +00:00
|
|
|
|
2016-08-24 21:22:11 +00:00
|
|
|
if ( $return ) {
|
|
|
|
|
return ob_get_clean();
|
|
|
|
|
} else {
|
|
|
|
|
ob_end_flush();
|
|
|
|
|
return null;
|
|
|
|
|
}
|
2003-04-14 23:10:40 +00:00
|
|
|
}
|
|
|
|
|
|
2011-10-28 15:45:54 +00:00
|
|
|
/**
|
|
|
|
|
* Prepare this object to display an error page; disable caching and
|
|
|
|
|
* indexing, clear the current text and redirect, set the page's title
|
2012-07-10 12:48:06 +00:00
|
|
|
* and optionally an custom HTML title (content of the "<title>" tag).
|
2011-10-28 15:45:54 +00:00
|
|
|
*
|
2014-07-24 17:42:24 +00:00
|
|
|
* @param string|Message $pageTitle Will be passed directly to setPageTitle()
|
2019-09-15 14:40:42 +00:00
|
|
|
* @param string|Message|false $htmlTitle Will be passed directly to setHTMLTitle();
|
2012-07-10 12:48:06 +00:00
|
|
|
* optional, if not passed the "<title>" attribute will be
|
2011-10-28 15:45:54 +00:00
|
|
|
* based on $pageTitle
|
|
|
|
|
*/
|
|
|
|
|
public function prepareErrorPage( $pageTitle, $htmlTitle = false ) {
|
|
|
|
|
$this->setPageTitle( $pageTitle );
|
|
|
|
|
if ( $htmlTitle !== false ) {
|
|
|
|
|
$this->setHTMLTitle( $htmlTitle );
|
|
|
|
|
}
|
|
|
|
|
$this->setRobotPolicy( 'noindex,nofollow' );
|
|
|
|
|
$this->setArticleRelated( false );
|
|
|
|
|
$this->enableClientCache( false );
|
|
|
|
|
$this->mRedirect = '';
|
2011-11-08 18:02:26 +00:00
|
|
|
$this->clearSubtitle();
|
2011-10-28 15:45:54 +00:00
|
|
|
$this->clearHTML();
|
|
|
|
|
}
|
|
|
|
|
|
2004-09-02 23:28:24 +00:00
|
|
|
/**
|
2007-05-18 20:46:42 +00:00
|
|
|
* Output a standard error page
|
2006-11-07 05:37:31 +00:00
|
|
|
*
|
2014-01-29 15:30:40 +00:00
|
|
|
* showErrorPage( 'titlemsg', 'pagetextmsg' );
|
2016-09-27 03:15:08 +00:00
|
|
|
* showErrorPage( 'titlemsg', 'pagetextmsg', [ 'param1', 'param2' ] );
|
2011-08-25 10:13:30 +00:00
|
|
|
* showErrorPage( 'titlemsg', $messageObject );
|
2014-01-29 15:30:40 +00:00
|
|
|
* showErrorPage( $titleMessageObject, $messageObject );
|
2011-08-31 14:47:08 +00:00
|
|
|
*
|
2014-04-08 15:29:17 +00:00
|
|
|
* @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
|
2004-09-02 23:28:24 +00:00
|
|
|
*/
|
2016-02-17 09:09:32 +00:00
|
|
|
public function showErrorPage( $title, $msg, $params = [] ) {
|
2013-04-17 14:52:47 +00:00
|
|
|
if ( !$title instanceof Message ) {
|
2012-08-06 21:07:24 +00:00
|
|
|
$title = $this->msg( $title );
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-18 23:17:57 +00:00
|
|
|
$this->prepareErrorPage( $title );
|
2008-04-14 07:45:50 +00:00
|
|
|
|
2013-01-26 18:32:03 +00:00
|
|
|
if ( $msg instanceof Message ) {
|
2016-02-17 09:09:32 +00:00
|
|
|
if ( $params !== [] ) {
|
2014-05-11 01:54:15 +00:00
|
|
|
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 );
|
2014-01-29 15:31:03 +00:00
|
|
|
}
|
2012-10-06 15:28:09 +00:00
|
|
|
$this->addHTML( $msg->parseAsBlock() );
|
2011-08-25 10:13:30 +00:00
|
|
|
} else {
|
2011-08-25 10:56:46 +00:00
|
|
|
$this->addWikiMsgArray( $msg, $params );
|
2011-08-25 10:13:30 +00:00
|
|
|
}
|
2008-04-14 07:45:50 +00:00
|
|
|
|
2008-04-16 13:46:16 +00:00
|
|
|
$this->returnToMain();
|
2003-04-14 23:10:40 +00:00
|
|
|
}
|
|
|
|
|
|
2007-08-06 11:30:40 +00:00
|
|
|
/**
|
|
|
|
|
* Output a standard permission error page
|
|
|
|
|
*
|
2016-10-14 22:59:11 +00:00
|
|
|
* @param array $errors Error message keys or [key, param...] arrays
|
2018-06-26 21:14:43 +00:00
|
|
|
* @param string|null $action Action that was denied or null if unknown
|
2007-08-06 11:30:40 +00:00
|
|
|
*/
|
2014-07-23 02:49:30 +00:00
|
|
|
public function showPermissionsErrorPage( array $errors, $action = null ) {
|
2019-08-22 02:22:26 +00:00
|
|
|
$services = MediaWikiServices::getInstance();
|
|
|
|
|
$permissionManager = $services->getPermissionManager();
|
2016-10-14 22:59:11 +00:00
|
|
|
foreach ( $errors as $key => $error ) {
|
|
|
|
|
$errors[$key] = (array)$error;
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-05 19:51:05 +00:00
|
|
|
// 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
|
|
|
|
|
// 2. the only error is insufficient permissions (i.e. no block or something else)
|
|
|
|
|
// 3. the error can be avoided simply by logging in
|
2019-08-22 02:22:26 +00:00
|
|
|
|
2016-02-17 09:09:32 +00:00
|
|
|
if ( in_array( $action, [ 'read', 'edit', 'createpage', 'createtalk', 'upload' ] )
|
2011-11-05 19:51:05 +00:00
|
|
|
&& $this->getUser()->isAnon() && count( $errors ) == 1 && isset( $errors[0][0] )
|
|
|
|
|
&& ( $errors[0][0] == 'badaccess-groups' || $errors[0][0] == 'badaccess-group0' )
|
2019-08-22 02:22:26 +00:00
|
|
|
&& ( $permissionManager->groupHasPermission( 'user', $action )
|
|
|
|
|
|| $permissionManager->groupHasPermission( 'autoconfirmed', $action ) )
|
2011-11-05 19:51:05 +00:00
|
|
|
) {
|
|
|
|
|
$displayReturnto = null;
|
2011-11-28 23:18:55 +00:00
|
|
|
|
2017-02-20 22:44:19 +00:00
|
|
|
# Due to T34276, if a user does not have read permissions,
|
2011-11-29 21:04:20 +00:00
|
|
|
# $this->getTitle() will just give Special:Badtitle, which is
|
|
|
|
|
# not especially useful as a returnto parameter. Use the title
|
2011-11-28 23:18:55 +00:00
|
|
|
# from the request instead, if there was one.
|
|
|
|
|
$request = $this->getRequest();
|
2015-12-11 21:50:55 +00:00
|
|
|
$returnto = Title::newFromText( $request->getVal( 'title', '' ) );
|
2011-11-05 19:51:05 +00:00
|
|
|
if ( $action == 'edit' ) {
|
|
|
|
|
$msg = 'whitelistedittext';
|
|
|
|
|
$displayReturnto = $returnto;
|
|
|
|
|
} elseif ( $action == 'createpage' || $action == 'createtalk' ) {
|
|
|
|
|
$msg = 'nocreatetext';
|
|
|
|
|
} elseif ( $action == 'upload' ) {
|
|
|
|
|
$msg = 'uploadnologintext';
|
|
|
|
|
} else { # Read
|
|
|
|
|
$msg = 'loginreqpagetext';
|
|
|
|
|
$displayReturnto = Title::newMainPage();
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-17 09:09:32 +00:00
|
|
|
$query = [];
|
2011-11-28 23:18:55 +00:00
|
|
|
|
2011-11-05 19:51:05 +00:00
|
|
|
if ( $returnto ) {
|
|
|
|
|
$query['returnto'] = $returnto->getPrefixedText();
|
2011-11-28 23:18:55 +00:00
|
|
|
|
2011-11-05 19:51:05 +00:00
|
|
|
if ( !$request->wasPosted() ) {
|
|
|
|
|
$returntoquery = $request->getValues();
|
|
|
|
|
unset( $returntoquery['title'] );
|
|
|
|
|
unset( $returntoquery['returnto'] );
|
|
|
|
|
unset( $returntoquery['returntoquery'] );
|
2013-01-28 18:04:20 +00:00
|
|
|
$query['returntoquery'] = wfArrayToCgi( $returntoquery );
|
2011-11-05 19:51:05 +00:00
|
|
|
}
|
|
|
|
|
}
|
2019-06-03 10:48:02 +00:00
|
|
|
|
2018-12-07 00:18:47 +00:00
|
|
|
$title = SpecialPage::getTitleFor( 'Userlogin' );
|
2019-06-03 10:48:02 +00:00
|
|
|
$linkRenderer = $services->getLinkRenderer();
|
2018-12-07 00:18:47 +00:00
|
|
|
$loginUrl = $title->getLinkURL( $query, false, PROTO_RELATIVE );
|
2017-01-18 20:55:20 +00:00
|
|
|
$loginLink = $linkRenderer->makeKnownLink(
|
2018-12-07 00:18:47 +00:00
|
|
|
$title,
|
2017-01-18 20:55:20 +00:00
|
|
|
$this->msg( 'loginreqlink' )->text(),
|
2016-02-17 09:09:32 +00:00
|
|
|
[],
|
2011-11-05 19:51:05 +00:00
|
|
|
$query
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$this->prepareErrorPage( $this->msg( 'loginreqtitle' ) );
|
2018-12-07 00:18:47 +00:00
|
|
|
$this->addHTML( $this->msg( $msg )->rawParams( $loginLink )->params( $loginUrl )->parse() );
|
2011-10-28 15:45:54 +00:00
|
|
|
|
2011-11-05 19:51:05 +00:00
|
|
|
# Don't return to a page the user can't read otherwise
|
|
|
|
|
# we'll end up in a pointless loop
|
2019-06-03 10:48:02 +00:00
|
|
|
if ( $displayReturnto && $permissionManager->userCan(
|
|
|
|
|
'read', $this->getUser(), $displayReturnto
|
|
|
|
|
) ) {
|
2011-11-05 19:51:05 +00:00
|
|
|
$this->returnToMain( null, $displayReturnto );
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
$this->prepareErrorPage( $this->msg( 'permissionserrors' ) );
|
2018-09-25 15:02:07 +00:00
|
|
|
$this->addWikiTextAsInterface( $this->formatPermissionsErrorMessage( $errors, $action ) );
|
2011-11-05 19:51:05 +00:00
|
|
|
}
|
2007-08-01 10:19:26 +00:00
|
|
|
}
|
|
|
|
|
|
2005-06-26 06:49:56 +00:00
|
|
|
/**
|
|
|
|
|
* Display an error page indicating that a given version of MediaWiki is
|
|
|
|
|
* required to use it
|
|
|
|
|
*
|
2014-04-08 15:29:17 +00:00
|
|
|
* @param mixed $version The version of MediaWiki needed to use the page
|
2005-06-26 06:49:56 +00:00
|
|
|
*/
|
2006-11-07 05:37:31 +00:00
|
|
|
public function versionRequired( $version ) {
|
2011-10-28 15:45:54 +00:00
|
|
|
$this->prepareErrorPage( $this->msg( 'versionrequired', $version ) );
|
2005-06-26 06:49:56 +00:00
|
|
|
|
2008-02-18 07:25:35 +00:00
|
|
|
$this->addWikiMsg( 'versionrequiredtext', $version );
|
2005-06-26 06:49:56 +00:00
|
|
|
$this->returnToMain();
|
|
|
|
|
}
|
2005-07-01 00:03:31 +00:00
|
|
|
|
2007-08-03 09:27:28 +00:00
|
|
|
/**
|
2010-01-29 21:32:45 +00:00
|
|
|
* Format a list of error messages
|
|
|
|
|
*
|
2020-02-26 04:26:00 +00:00
|
|
|
* @param array $errors Array of arrays returned by PermissionManager::getPermissionErrors
|
2018-06-26 21:14:43 +00:00
|
|
|
* @param string|null $action Action that was denied or null if unknown
|
2014-04-08 15:29:17 +00:00
|
|
|
* @return string The wikitext error-messages, formatted into a list.
|
2007-08-03 09:27:28 +00:00
|
|
|
*/
|
2014-07-23 02:49:30 +00:00
|
|
|
public function formatPermissionsErrorMessage( array $errors, $action = null ) {
|
2010-05-22 12:18:22 +00:00
|
|
|
if ( $action == null ) {
|
2011-10-23 08:13:52 +00:00
|
|
|
$text = $this->msg( 'permissionserrorstext', count( $errors ) )->plain() . "\n\n";
|
2008-05-23 10:34:11 +00:00
|
|
|
} else {
|
2011-10-23 08:13:52 +00:00
|
|
|
$action_desc = $this->msg( "action-$action" )->plain();
|
|
|
|
|
$text = $this->msg(
|
2010-05-22 12:18:22 +00:00
|
|
|
'permissionserrorstext-withaction',
|
|
|
|
|
count( $errors ),
|
|
|
|
|
$action_desc
|
2011-10-23 08:13:52 +00:00
|
|
|
)->plain() . "\n\n";
|
2008-05-23 10:34:11 +00:00
|
|
|
}
|
2008-01-02 01:16:44 +00:00
|
|
|
|
2010-05-22 12:18:22 +00:00
|
|
|
if ( count( $errors ) > 1 ) {
|
2007-08-16 07:05:38 +00:00
|
|
|
$text .= '<ul class="permissions-errors">' . "\n";
|
|
|
|
|
|
2013-04-17 14:52:47 +00:00
|
|
|
foreach ( $errors as $error ) {
|
2007-08-16 07:05:38 +00:00
|
|
|
$text .= '<li>';
|
2018-06-08 02:58:35 +00:00
|
|
|
$text .= $this->msg( ...$error )->plain();
|
2007-08-16 07:05:38 +00:00
|
|
|
$text .= "</li>\n";
|
|
|
|
|
}
|
|
|
|
|
$text .= '</ul>';
|
|
|
|
|
} else {
|
2010-05-22 12:18:22 +00:00
|
|
|
$text .= "<div class=\"permissions-errors\">\n" .
|
2018-06-08 02:58:35 +00:00
|
|
|
$this->msg( ...reset( $errors ) )->plain() .
|
2010-05-22 12:18:22 +00:00
|
|
|
"\n</div>";
|
2007-08-03 09:27:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $text;
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-07 16:04:08 +00:00
|
|
|
/**
|
2016-09-05 20:21:26 +00:00
|
|
|
* Show a warning about replica DB lag
|
2011-04-07 16:04:08 +00:00
|
|
|
*
|
|
|
|
|
* If the lag is higher than $wgSlaveLagCritical seconds,
|
|
|
|
|
* then the warning is a bit more obvious. If the lag is
|
|
|
|
|
* lower than $wgSlaveLagWarning, then no warning is shown.
|
|
|
|
|
*
|
2018-10-31 17:36:48 +00:00
|
|
|
* @param int $lag Replica lag
|
2011-04-07 16:04:08 +00:00
|
|
|
*/
|
|
|
|
|
public function showLagWarning( $lag ) {
|
2014-08-23 08:52:41 +00:00
|
|
|
$config = $this->getConfig();
|
|
|
|
|
if ( $lag >= $config->get( 'SlaveLagWarning' ) ) {
|
2016-08-21 15:01:04 +00:00
|
|
|
$lag = floor( $lag ); // floor to avoid nano seconds to display
|
2014-08-23 08:52:41 +00:00
|
|
|
$message = $lag < $config->get( 'SlaveLagCritical' )
|
2011-04-07 16:04:08 +00:00
|
|
|
? 'lag-warn-normal'
|
|
|
|
|
: 'lag-warn-high';
|
2019-12-14 20:07:58 +00:00
|
|
|
// For grep: mw-lag-warn-normal, mw-lag-warn-high
|
2016-02-17 09:09:32 +00:00
|
|
|
$wrap = Html::rawElement( 'div', [ 'class' => "mw-{$message}" ], "\n$1\n" );
|
|
|
|
|
$this->wrapWikiMsg( "$wrap\n", [ $message, $this->getLanguage()->formatNum( $lag ) ] );
|
2011-04-07 16:04:08 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-16 05:53:30 +00:00
|
|
|
/**
|
|
|
|
|
* Output an error page
|
|
|
|
|
*
|
|
|
|
|
* @note FatalError exception class provides an alternative.
|
|
|
|
|
* @param string $message Error to output. Must be escaped for HTML.
|
|
|
|
|
*/
|
2006-11-07 05:37:31 +00:00
|
|
|
public function showFatalError( $message ) {
|
2011-10-28 15:45:54 +00:00
|
|
|
$this->prepareErrorPage( $this->msg( 'internalerror' ) );
|
|
|
|
|
|
|
|
|
|
$this->addHTML( $message );
|
2003-04-14 23:10:40 +00:00
|
|
|
}
|
|
|
|
|
|
2004-09-02 23:28:24 +00:00
|
|
|
/**
|
2007-07-29 23:53:45 +00:00
|
|
|
* Add a "return to" link pointing to a specified title
|
|
|
|
|
*
|
2019-09-13 09:34:33 +00:00
|
|
|
* @param LinkTarget $title Title to link
|
2014-04-10 18:50:10 +00:00
|
|
|
* @param array $query Query string parameters
|
2018-06-26 21:14:43 +00:00
|
|
|
* @param string|null $text Text of the link (input is not escaped)
|
2014-04-10 18:50:10 +00:00
|
|
|
* @param array $options Options array to pass to Linker
|
2007-07-29 23:53:45 +00:00
|
|
|
*/
|
2016-02-17 09:09:32 +00:00
|
|
|
public function addReturnTo( $title, array $query = [], $text = null, $options = [] ) {
|
2016-11-30 21:28:55 +00:00
|
|
|
$linkRenderer = MediaWikiServices::getInstance()
|
|
|
|
|
->getLinkRendererFactory()->createFromLegacyOptions( $options );
|
2011-10-23 08:13:52 +00:00
|
|
|
$link = $this->msg( 'returnto' )->rawParams(
|
2016-11-30 21:28:55 +00:00
|
|
|
$linkRenderer->makeLink( $title, $text, [], $query ) )->escaped();
|
2009-06-15 11:11:17 +00:00
|
|
|
$this->addHTML( "<p id=\"mw-returnto\">{$link}</p>\n" );
|
2007-07-29 23:53:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Add a "return to" link pointing to a specified title,
|
|
|
|
|
* or the title indicated in the request, or else the main page
|
|
|
|
|
*
|
2018-06-26 21:14:43 +00:00
|
|
|
* @param mixed|null $unused
|
|
|
|
|
* @param Title|string|null $returnto Title or String to return to
|
|
|
|
|
* @param string|null $returntoquery Query string for the return to link
|
2004-09-02 23:28:24 +00:00
|
|
|
*/
|
2009-07-14 21:25:33 +00:00
|
|
|
public function returnToMain( $unused = null, $returnto = null, $returntoquery = null ) {
|
|
|
|
|
if ( $returnto == null ) {
|
2011-04-03 12:46:36 +00:00
|
|
|
$returnto = $this->getRequest()->getText( 'returnto' );
|
2004-04-01 12:44:54 +00:00
|
|
|
}
|
2009-08-07 00:16:54 +00:00
|
|
|
|
2009-07-14 21:25:33 +00:00
|
|
|
if ( $returntoquery == null ) {
|
2011-04-03 12:46:36 +00:00
|
|
|
$returntoquery = $this->getRequest()->getText( 'returntoquery' );
|
2009-07-14 21:25:33 +00:00
|
|
|
}
|
2008-04-14 07:45:50 +00:00
|
|
|
|
2010-01-06 19:59:42 +00:00
|
|
|
if ( $returnto === '' ) {
|
2006-12-03 00:22:14 +00:00
|
|
|
$returnto = Title::newMainPage();
|
2003-04-14 23:10:40 +00:00
|
|
|
}
|
2006-06-21 16:28:04 +00:00
|
|
|
|
|
|
|
|
if ( is_object( $returnto ) ) {
|
|
|
|
|
$titleObj = $returnto;
|
|
|
|
|
} else {
|
|
|
|
|
$titleObj = Title::newFromText( $returnto );
|
|
|
|
|
}
|
2016-02-07 13:07:20 +00:00
|
|
|
// We don't want people to return to external interwiki. That
|
|
|
|
|
// might potentially be used as part of a phishing scheme
|
|
|
|
|
if ( !is_object( $titleObj ) || $titleObj->isExternal() ) {
|
2006-06-23 06:53:05 +00:00
|
|
|
$titleObj = Title::newMainPage();
|
|
|
|
|
}
|
2006-06-21 16:28:04 +00:00
|
|
|
|
2012-08-08 08:42:17 +00:00
|
|
|
$this->addReturnTo( $titleObj, wfCgiToArray( $returntoquery ) );
|
2003-04-14 23:10:40 +00:00
|
|
|
}
|
|
|
|
|
|
resourceloader: Move queue formatting out of OutputPage
HTML formatting of the queue was distributed over several OutputPage methods.
Each method demanding a snippet of HTML by calling makeResourceLoaderLink()
with a limited amount of information. As such, makeResourceLoaderLink() was
unable to provide the client with the proper state information.
Centralising it also allows it to better reduce duplication in HTML output
and maintain a more accurate state.
Problems fixed by centralising:
1. The 'user' module is special (due to per-user 'version' and 'user' params).
It is manually requested via script-src. To avoid a separate (and wrong)
request from something that requires it, we set state=loading directly.
However, because the module is in the bottom, the old HTML formatter could
only put state=loading in the bottom also. This sometimes caused a wrong
request to be fired for modules=user if something in the top queue
triggered a requirement for it.
2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose
of allowing dependencies on style modules without risking duplicate loading
on pages where the styles are loaded already. This didn't work, because the
state information about page-style modules is output near the stylesheet,
which is after the script tag with mw.loader.load(). That runs first, and
mw.loader would still make a duplicate request before it learns the state.
Changes:
* Document reasons for style/script tag order in getHeadHtml (per 09537e83).
* Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed
before since a duplicate check in makeResourceLoaderLink() verified the
origin a second time.
* Declare explicit position 'top' on 'user.options' and 'user.tokens' module.
Previously, OutputPage hardcoded them in the top. The new formatter doesn't.
* Remove getHeadScripts().
* Remove getInlineHeadScripts().
* Remove getExternalHeadScripts().
* Remove buildCssLinks().
* Remove getScriptsForBottomQueue().
* Change where Skin::setupSkinUserCss() is called. This methods lets the skin
add modules to the queue. Previously it was called from buildCssLinks(),
via headElement(), via prepareQuickTemplate(), via OutputPage::output().
It's now in OutputPage::output() directly (slightly earlier). This is needed
because prepareQuickTemplate() calls bottomScripts() before headElement().
And bottomScript() would lazy-initialise the queue and lock it before
setupSkinUserCss() is called from headElement().
This makes execution order more predictable instead of being dependent on
the arbitrary order of data extraction in prepareQuickTemplate (which varies
from one skin to another).
* Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early
on so. This avoids wrongful loading and fixes problem 1.
Effective changes in output:
* mw.loader.state() is now before mw.loader.load(). This fixes problem 2.
* mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading".
* mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1.
* The <script async src> tag for 'startup' changed position (slightly).
Previously it was after all inline scripts and stylesheets. It's still after
all inline scripts and after most stylesheets, but before any user styles.
Since the queue is now formatted outside OutputPage, it can't inject the
meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle
of existing output. This shouldn't have any noticable impact.
Bug: T87871
Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
|
|
|
private function getRlClientContext() {
|
|
|
|
|
if ( !$this->rlClientContext ) {
|
|
|
|
|
$query = ResourceLoader::makeLoaderQuery(
|
|
|
|
|
[], // modules; not relevant
|
|
|
|
|
$this->getLanguage()->getCode(),
|
|
|
|
|
$this->getSkin()->getSkinName(),
|
|
|
|
|
$this->getUser()->isLoggedIn() ? $this->getUser()->getName() : null,
|
|
|
|
|
null, // version; not relevant
|
|
|
|
|
ResourceLoader::inDebugMode(),
|
|
|
|
|
null, // only; not relevant
|
|
|
|
|
$this->isPrintable(),
|
|
|
|
|
$this->getRequest()->getBool( 'handheld' )
|
|
|
|
|
);
|
|
|
|
|
$this->rlClientContext = new ResourceLoaderContext(
|
|
|
|
|
$this->getResourceLoader(),
|
|
|
|
|
new FauxRequest( $query )
|
|
|
|
|
);
|
2017-02-28 20:52:17 +00:00
|
|
|
if ( $this->contentOverrideCallbacks ) {
|
|
|
|
|
$this->rlClientContext = new DerivativeResourceLoaderContext( $this->rlClientContext );
|
|
|
|
|
$this->rlClientContext->setContentOverrideCallback( function ( Title $title ) {
|
|
|
|
|
foreach ( $this->contentOverrideCallbacks as $callback ) {
|
2018-08-02 18:26:55 +00:00
|
|
|
$content = $callback( $title );
|
2017-02-28 20:52:17 +00:00
|
|
|
if ( $content !== null ) {
|
2018-08-20 00:14:46 +00:00
|
|
|
$text = ContentHandler::getContentText( $content );
|
|
|
|
|
if ( strpos( $text, '</script>' ) !== false ) {
|
|
|
|
|
// Proactively replace this so that we can display a message
|
|
|
|
|
// to the user, instead of letting it go to Html::inlineScript(),
|
|
|
|
|
// where it would be considered a server-side issue.
|
|
|
|
|
$titleFormatted = $title->getPrefixedText();
|
|
|
|
|
$content = new JavaScriptContent(
|
|
|
|
|
Xml::encodeJsCall( 'mw.log.error', [
|
|
|
|
|
"Cannot preview $titleFormatted due to script-closing tag."
|
|
|
|
|
] )
|
|
|
|
|
);
|
|
|
|
|
}
|
2017-02-28 20:52:17 +00:00
|
|
|
return $content;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
} );
|
|
|
|
|
}
|
resourceloader: Move queue formatting out of OutputPage
HTML formatting of the queue was distributed over several OutputPage methods.
Each method demanding a snippet of HTML by calling makeResourceLoaderLink()
with a limited amount of information. As such, makeResourceLoaderLink() was
unable to provide the client with the proper state information.
Centralising it also allows it to better reduce duplication in HTML output
and maintain a more accurate state.
Problems fixed by centralising:
1. The 'user' module is special (due to per-user 'version' and 'user' params).
It is manually requested via script-src. To avoid a separate (and wrong)
request from something that requires it, we set state=loading directly.
However, because the module is in the bottom, the old HTML formatter could
only put state=loading in the bottom also. This sometimes caused a wrong
request to be fired for modules=user if something in the top queue
triggered a requirement for it.
2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose
of allowing dependencies on style modules without risking duplicate loading
on pages where the styles are loaded already. This didn't work, because the
state information about page-style modules is output near the stylesheet,
which is after the script tag with mw.loader.load(). That runs first, and
mw.loader would still make a duplicate request before it learns the state.
Changes:
* Document reasons for style/script tag order in getHeadHtml (per 09537e83).
* Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed
before since a duplicate check in makeResourceLoaderLink() verified the
origin a second time.
* Declare explicit position 'top' on 'user.options' and 'user.tokens' module.
Previously, OutputPage hardcoded them in the top. The new formatter doesn't.
* Remove getHeadScripts().
* Remove getInlineHeadScripts().
* Remove getExternalHeadScripts().
* Remove buildCssLinks().
* Remove getScriptsForBottomQueue().
* Change where Skin::setupSkinUserCss() is called. This methods lets the skin
add modules to the queue. Previously it was called from buildCssLinks(),
via headElement(), via prepareQuickTemplate(), via OutputPage::output().
It's now in OutputPage::output() directly (slightly earlier). This is needed
because prepareQuickTemplate() calls bottomScripts() before headElement().
And bottomScript() would lazy-initialise the queue and lock it before
setupSkinUserCss() is called from headElement().
This makes execution order more predictable instead of being dependent on
the arbitrary order of data extraction in prepareQuickTemplate (which varies
from one skin to another).
* Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early
on so. This avoids wrongful loading and fixes problem 1.
Effective changes in output:
* mw.loader.state() is now before mw.loader.load(). This fixes problem 2.
* mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading".
* mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1.
* The <script async src> tag for 'startup' changed position (slightly).
Previously it was after all inline scripts and stylesheets. It's still after
all inline scripts and after most stylesheets, but before any user styles.
Since the queue is now formatted outside OutputPage, it can't inject the
meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle
of existing output. This shouldn't have any noticable impact.
Bug: T87871
Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
|
|
|
}
|
|
|
|
|
return $this->rlClientContext;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Call this to freeze the module queue and JS config and create a formatter.
|
|
|
|
|
*
|
|
|
|
|
* Depending on the Skin, this may get lazy-initialised in either headElement() or
|
|
|
|
|
* getBottomScripts(). See SkinTemplate::prepareQuickTemplate(). Calling this too early may
|
|
|
|
|
* cause unexpected side-effects since disallowUserJs() may be called at any time to change
|
|
|
|
|
* the module filters retroactively. Skins and extension hooks may also add modules until very
|
|
|
|
|
* late in the request lifecycle.
|
|
|
|
|
*
|
|
|
|
|
* @return ResourceLoaderClientHtml
|
|
|
|
|
*/
|
|
|
|
|
public function getRlClient() {
|
|
|
|
|
if ( !$this->rlClient ) {
|
|
|
|
|
$context = $this->getRlClientContext();
|
2016-08-19 02:04:21 +00:00
|
|
|
$rl = $this->getResourceLoader();
|
resourceloader: Move queue formatting out of OutputPage
HTML formatting of the queue was distributed over several OutputPage methods.
Each method demanding a snippet of HTML by calling makeResourceLoaderLink()
with a limited amount of information. As such, makeResourceLoaderLink() was
unable to provide the client with the proper state information.
Centralising it also allows it to better reduce duplication in HTML output
and maintain a more accurate state.
Problems fixed by centralising:
1. The 'user' module is special (due to per-user 'version' and 'user' params).
It is manually requested via script-src. To avoid a separate (and wrong)
request from something that requires it, we set state=loading directly.
However, because the module is in the bottom, the old HTML formatter could
only put state=loading in the bottom also. This sometimes caused a wrong
request to be fired for modules=user if something in the top queue
triggered a requirement for it.
2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose
of allowing dependencies on style modules without risking duplicate loading
on pages where the styles are loaded already. This didn't work, because the
state information about page-style modules is output near the stylesheet,
which is after the script tag with mw.loader.load(). That runs first, and
mw.loader would still make a duplicate request before it learns the state.
Changes:
* Document reasons for style/script tag order in getHeadHtml (per 09537e83).
* Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed
before since a duplicate check in makeResourceLoaderLink() verified the
origin a second time.
* Declare explicit position 'top' on 'user.options' and 'user.tokens' module.
Previously, OutputPage hardcoded them in the top. The new formatter doesn't.
* Remove getHeadScripts().
* Remove getInlineHeadScripts().
* Remove getExternalHeadScripts().
* Remove buildCssLinks().
* Remove getScriptsForBottomQueue().
* Change where Skin::setupSkinUserCss() is called. This methods lets the skin
add modules to the queue. Previously it was called from buildCssLinks(),
via headElement(), via prepareQuickTemplate(), via OutputPage::output().
It's now in OutputPage::output() directly (slightly earlier). This is needed
because prepareQuickTemplate() calls bottomScripts() before headElement().
And bottomScript() would lazy-initialise the queue and lock it before
setupSkinUserCss() is called from headElement().
This makes execution order more predictable instead of being dependent on
the arbitrary order of data extraction in prepareQuickTemplate (which varies
from one skin to another).
* Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early
on so. This avoids wrongful loading and fixes problem 1.
Effective changes in output:
* mw.loader.state() is now before mw.loader.load(). This fixes problem 2.
* mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading".
* mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1.
* The <script async src> tag for 'startup' changed position (slightly).
Previously it was after all inline scripts and stylesheets. It's still after
all inline scripts and after most stylesheets, but before any user styles.
Since the queue is now formatted outside OutputPage, it can't inject the
meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle
of existing output. This shouldn't have any noticable impact.
Bug: T87871
Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
|
|
|
$this->addModules( [
|
2017-02-28 20:52:17 +00:00
|
|
|
'user',
|
resourceloader: Move queue formatting out of OutputPage
HTML formatting of the queue was distributed over several OutputPage methods.
Each method demanding a snippet of HTML by calling makeResourceLoaderLink()
with a limited amount of information. As such, makeResourceLoaderLink() was
unable to provide the client with the proper state information.
Centralising it also allows it to better reduce duplication in HTML output
and maintain a more accurate state.
Problems fixed by centralising:
1. The 'user' module is special (due to per-user 'version' and 'user' params).
It is manually requested via script-src. To avoid a separate (and wrong)
request from something that requires it, we set state=loading directly.
However, because the module is in the bottom, the old HTML formatter could
only put state=loading in the bottom also. This sometimes caused a wrong
request to be fired for modules=user if something in the top queue
triggered a requirement for it.
2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose
of allowing dependencies on style modules without risking duplicate loading
on pages where the styles are loaded already. This didn't work, because the
state information about page-style modules is output near the stylesheet,
which is after the script tag with mw.loader.load(). That runs first, and
mw.loader would still make a duplicate request before it learns the state.
Changes:
* Document reasons for style/script tag order in getHeadHtml (per 09537e83).
* Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed
before since a duplicate check in makeResourceLoaderLink() verified the
origin a second time.
* Declare explicit position 'top' on 'user.options' and 'user.tokens' module.
Previously, OutputPage hardcoded them in the top. The new formatter doesn't.
* Remove getHeadScripts().
* Remove getInlineHeadScripts().
* Remove getExternalHeadScripts().
* Remove buildCssLinks().
* Remove getScriptsForBottomQueue().
* Change where Skin::setupSkinUserCss() is called. This methods lets the skin
add modules to the queue. Previously it was called from buildCssLinks(),
via headElement(), via prepareQuickTemplate(), via OutputPage::output().
It's now in OutputPage::output() directly (slightly earlier). This is needed
because prepareQuickTemplate() calls bottomScripts() before headElement().
And bottomScript() would lazy-initialise the queue and lock it before
setupSkinUserCss() is called from headElement().
This makes execution order more predictable instead of being dependent on
the arbitrary order of data extraction in prepareQuickTemplate (which varies
from one skin to another).
* Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early
on so. This avoids wrongful loading and fixes problem 1.
Effective changes in output:
* mw.loader.state() is now before mw.loader.load(). This fixes problem 2.
* mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading".
* mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1.
* The <script async src> tag for 'startup' changed position (slightly).
Previously it was after all inline scripts and stylesheets. It's still after
all inline scripts and after most stylesheets, but before any user styles.
Since the queue is now formatted outside OutputPage, it can't inject the
meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle
of existing output. This shouldn't have any noticable impact.
Bug: T87871
Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
|
|
|
'user.options',
|
|
|
|
|
] );
|
2016-08-19 02:04:21 +00:00
|
|
|
$this->addModuleStyles( [
|
|
|
|
|
'site.styles',
|
|
|
|
|
'noscript',
|
|
|
|
|
'user.styles',
|
|
|
|
|
] );
|
2016-08-30 23:36:28 +00:00
|
|
|
$this->getSkin()->setupSkinUserCss( $this );
|
2016-08-19 02:04:21 +00:00
|
|
|
|
|
|
|
|
// Prepare exempt modules for buildExemptModules()
|
|
|
|
|
$exemptGroups = [ 'site' => [], 'noscript' => [], 'private' => [], 'user' => [] ];
|
|
|
|
|
$exemptStates = [];
|
2016-09-08 08:38:26 +00:00
|
|
|
$moduleStyles = $this->getModuleStyles( /*filter*/ true );
|
|
|
|
|
|
2016-10-20 21:43:57 +00:00
|
|
|
// Preload getTitleInfo for isKnownEmpty calls below and in ResourceLoaderClientHtml
|
|
|
|
|
// Separate user-specific batch for improved cache-hit ratio.
|
|
|
|
|
$userBatch = [ 'user.styles', 'user' ];
|
|
|
|
|
$siteBatch = array_diff( $moduleStyles, $userBatch );
|
|
|
|
|
$dbr = wfGetDB( DB_REPLICA );
|
|
|
|
|
ResourceLoaderWikiModule::preloadTitleInfo( $context, $dbr, $siteBatch );
|
|
|
|
|
ResourceLoaderWikiModule::preloadTitleInfo( $context, $dbr, $userBatch );
|
2016-09-08 08:38:26 +00:00
|
|
|
|
|
|
|
|
// Filter out modules handled by buildExemptModules()
|
|
|
|
|
$moduleStyles = array_filter( $moduleStyles,
|
2016-08-19 02:04:21 +00:00
|
|
|
function ( $name ) use ( $rl, $context, &$exemptGroups, &$exemptStates ) {
|
|
|
|
|
$module = $rl->getModule( $name );
|
|
|
|
|
if ( $module ) {
|
2016-09-08 08:38:26 +00:00
|
|
|
$group = $module->getGroup();
|
2016-08-19 02:04:21 +00:00
|
|
|
if ( isset( $exemptGroups[$group] ) ) {
|
|
|
|
|
$exemptStates[$name] = 'ready';
|
|
|
|
|
if ( !$module->isKnownEmpty( $context ) ) {
|
|
|
|
|
// E.g. Don't output empty <styles>
|
|
|
|
|
$exemptGroups[$group][] = $name;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
$this->rlExemptStyleModules = $exemptGroups;
|
|
|
|
|
|
2018-03-27 21:28:03 +00:00
|
|
|
$rlClient = new ResourceLoaderClientHtml( $context, [
|
|
|
|
|
'target' => $this->getTarget(),
|
2019-10-28 05:01:17 +00:00
|
|
|
'nonce' => $this->CSP->getNonce(),
|
2018-03-28 00:57:06 +00:00
|
|
|
// When 'safemode', disallowUserJs(), or reduceAllowedModules() is used
|
|
|
|
|
// to only restrict modules to ORIGIN_CORE (ie. disallow ORIGIN_USER), the list of
|
|
|
|
|
// modules enqueud for loading on this page is filtered to just those.
|
|
|
|
|
// However, to make sure we also apply the restriction to dynamic dependencies and
|
|
|
|
|
// lazy-loaded modules at run-time on the client-side, pass 'safemode' down to the
|
|
|
|
|
// StartupModule so that the client-side registry will not contain any restricted
|
|
|
|
|
// modules either. (T152169, T185303)
|
|
|
|
|
'safemode' => ( $this->getAllowedModules( ResourceLoaderModule::TYPE_COMBINED )
|
|
|
|
|
<= ResourceLoaderModule::ORIGIN_CORE_INDIVIDUAL
|
|
|
|
|
) ? '1' : null,
|
2018-03-27 21:28:03 +00:00
|
|
|
] );
|
resourceloader: Move queue formatting out of OutputPage
HTML formatting of the queue was distributed over several OutputPage methods.
Each method demanding a snippet of HTML by calling makeResourceLoaderLink()
with a limited amount of information. As such, makeResourceLoaderLink() was
unable to provide the client with the proper state information.
Centralising it also allows it to better reduce duplication in HTML output
and maintain a more accurate state.
Problems fixed by centralising:
1. The 'user' module is special (due to per-user 'version' and 'user' params).
It is manually requested via script-src. To avoid a separate (and wrong)
request from something that requires it, we set state=loading directly.
However, because the module is in the bottom, the old HTML formatter could
only put state=loading in the bottom also. This sometimes caused a wrong
request to be fired for modules=user if something in the top queue
triggered a requirement for it.
2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose
of allowing dependencies on style modules without risking duplicate loading
on pages where the styles are loaded already. This didn't work, because the
state information about page-style modules is output near the stylesheet,
which is after the script tag with mw.loader.load(). That runs first, and
mw.loader would still make a duplicate request before it learns the state.
Changes:
* Document reasons for style/script tag order in getHeadHtml (per 09537e83).
* Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed
before since a duplicate check in makeResourceLoaderLink() verified the
origin a second time.
* Declare explicit position 'top' on 'user.options' and 'user.tokens' module.
Previously, OutputPage hardcoded them in the top. The new formatter doesn't.
* Remove getHeadScripts().
* Remove getInlineHeadScripts().
* Remove getExternalHeadScripts().
* Remove buildCssLinks().
* Remove getScriptsForBottomQueue().
* Change where Skin::setupSkinUserCss() is called. This methods lets the skin
add modules to the queue. Previously it was called from buildCssLinks(),
via headElement(), via prepareQuickTemplate(), via OutputPage::output().
It's now in OutputPage::output() directly (slightly earlier). This is needed
because prepareQuickTemplate() calls bottomScripts() before headElement().
And bottomScript() would lazy-initialise the queue and lock it before
setupSkinUserCss() is called from headElement().
This makes execution order more predictable instead of being dependent on
the arbitrary order of data extraction in prepareQuickTemplate (which varies
from one skin to another).
* Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early
on so. This avoids wrongful loading and fixes problem 1.
Effective changes in output:
* mw.loader.state() is now before mw.loader.load(). This fixes problem 2.
* mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading".
* mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1.
* The <script async src> tag for 'startup' changed position (slightly).
Previously it was after all inline scripts and stylesheets. It's still after
all inline scripts and after most stylesheets, but before any user styles.
Since the queue is now formatted outside OutputPage, it can't inject the
meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle
of existing output. This shouldn't have any noticable impact.
Bug: T87871
Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
|
|
|
$rlClient->setConfig( $this->getJSVars() );
|
|
|
|
|
$rlClient->setModules( $this->getModules( /*filter*/ true ) );
|
2016-08-19 02:04:21 +00:00
|
|
|
$rlClient->setModuleStyles( $moduleStyles );
|
|
|
|
|
$rlClient->setExemptStates( $exemptStates );
|
resourceloader: Move queue formatting out of OutputPage
HTML formatting of the queue was distributed over several OutputPage methods.
Each method demanding a snippet of HTML by calling makeResourceLoaderLink()
with a limited amount of information. As such, makeResourceLoaderLink() was
unable to provide the client with the proper state information.
Centralising it also allows it to better reduce duplication in HTML output
and maintain a more accurate state.
Problems fixed by centralising:
1. The 'user' module is special (due to per-user 'version' and 'user' params).
It is manually requested via script-src. To avoid a separate (and wrong)
request from something that requires it, we set state=loading directly.
However, because the module is in the bottom, the old HTML formatter could
only put state=loading in the bottom also. This sometimes caused a wrong
request to be fired for modules=user if something in the top queue
triggered a requirement for it.
2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose
of allowing dependencies on style modules without risking duplicate loading
on pages where the styles are loaded already. This didn't work, because the
state information about page-style modules is output near the stylesheet,
which is after the script tag with mw.loader.load(). That runs first, and
mw.loader would still make a duplicate request before it learns the state.
Changes:
* Document reasons for style/script tag order in getHeadHtml (per 09537e83).
* Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed
before since a duplicate check in makeResourceLoaderLink() verified the
origin a second time.
* Declare explicit position 'top' on 'user.options' and 'user.tokens' module.
Previously, OutputPage hardcoded them in the top. The new formatter doesn't.
* Remove getHeadScripts().
* Remove getInlineHeadScripts().
* Remove getExternalHeadScripts().
* Remove buildCssLinks().
* Remove getScriptsForBottomQueue().
* Change where Skin::setupSkinUserCss() is called. This methods lets the skin
add modules to the queue. Previously it was called from buildCssLinks(),
via headElement(), via prepareQuickTemplate(), via OutputPage::output().
It's now in OutputPage::output() directly (slightly earlier). This is needed
because prepareQuickTemplate() calls bottomScripts() before headElement().
And bottomScript() would lazy-initialise the queue and lock it before
setupSkinUserCss() is called from headElement().
This makes execution order more predictable instead of being dependent on
the arbitrary order of data extraction in prepareQuickTemplate (which varies
from one skin to another).
* Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early
on so. This avoids wrongful loading and fixes problem 1.
Effective changes in output:
* mw.loader.state() is now before mw.loader.load(). This fixes problem 2.
* mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading".
* mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1.
* The <script async src> tag for 'startup' changed position (slightly).
Previously it was after all inline scripts and stylesheets. It's still after
all inline scripts and after most stylesheets, but before any user styles.
Since the queue is now formatted outside OutputPage, it can't inject the
meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle
of existing output. This shouldn't have any noticable impact.
Bug: T87871
Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
|
|
|
$this->rlClient = $rlClient;
|
|
|
|
|
}
|
|
|
|
|
return $this->rlClient;
|
|
|
|
|
}
|
|
|
|
|
|
2004-09-02 23:28:24 +00:00
|
|
|
/**
|
2014-04-08 15:29:17 +00:00
|
|
|
* @param Skin $sk The given Skin
|
|
|
|
|
* @param bool $includeStyle Unused
|
|
|
|
|
* @return string The doctype, opening "<html>", and head element.
|
2004-09-02 23:28:24 +00:00
|
|
|
*/
|
2009-09-22 16:52:40 +00:00
|
|
|
public function headElement( Skin $sk, $includeStyle = true ) {
|
OutputPage: Load html5shiv without indirection of load.php
This library was introduced in 3a30e03645f, to make sure Grade C
stylesheets that apply to HTML5 elements that older browsers might
not know yet, work as expected in old IE.
It originally committed a minified version and loaded it directly
as an HTML script tag in a conditional comment. This had minimal
impact on anything else, and was easy to maintain.
In 68237fb1a74, this was changed to commit the unminified version
instead because Debian maintainers don't like packaging software
that contain minified files, despite being a simple file,
unmodified from the original (upstream publishes it in minified
form, with license header), published under a compatible free
license, and embedded in a license-compliant manner. We then
registered it as an unused ResourceLoader module, to be minified
on-the-fly at run-time.
Support for "server-registered client-unregistered" modules was
removed last week in c554ee8e64e because nothing needed it
anymore (except html5shiv apparently), which resulted in this
module being registered client-side on all page views for all
users (in latest master). This doesn't break anything, but it
is a regression performance-wise.
Restore this by (mostly) going to how it was before: A simple static
file, committed to Git, served as-is. Not related to, served by,
pulled through, nor registered with, ResourceLoader in any way.
Only difference with the original approach is that it is no longer
minified, which means a few more bytes transferred on old IE page
views, which is considered an acceptable compromise.
Bug: T201483
Change-Id: Ib0020b6bd015679b61f63eaa8109ed9b83d8ad15
2019-07-04 15:54:24 +00:00
|
|
|
$config = $this->getConfig();
|
2011-11-21 16:13:21 +00:00
|
|
|
$userdir = $this->getLanguage()->getDir();
|
2018-07-29 12:24:54 +00:00
|
|
|
$sitedir = MediaWikiServices::getInstance()->getContentLanguage()->getDir();
|
2003-04-14 23:10:40 +00:00
|
|
|
|
2016-07-07 19:26:36 +00:00
|
|
|
$pieces = [];
|
2019-08-25 16:24:55 +00:00
|
|
|
$htmlAttribs = Sanitizer::mergeAttributes(
|
resourceloader: Move queue formatting out of OutputPage
HTML formatting of the queue was distributed over several OutputPage methods.
Each method demanding a snippet of HTML by calling makeResourceLoaderLink()
with a limited amount of information. As such, makeResourceLoaderLink() was
unable to provide the client with the proper state information.
Centralising it also allows it to better reduce duplication in HTML output
and maintain a more accurate state.
Problems fixed by centralising:
1. The 'user' module is special (due to per-user 'version' and 'user' params).
It is manually requested via script-src. To avoid a separate (and wrong)
request from something that requires it, we set state=loading directly.
However, because the module is in the bottom, the old HTML formatter could
only put state=loading in the bottom also. This sometimes caused a wrong
request to be fired for modules=user if something in the top queue
triggered a requirement for it.
2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose
of allowing dependencies on style modules without risking duplicate loading
on pages where the styles are loaded already. This didn't work, because the
state information about page-style modules is output near the stylesheet,
which is after the script tag with mw.loader.load(). That runs first, and
mw.loader would still make a duplicate request before it learns the state.
Changes:
* Document reasons for style/script tag order in getHeadHtml (per 09537e83).
* Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed
before since a duplicate check in makeResourceLoaderLink() verified the
origin a second time.
* Declare explicit position 'top' on 'user.options' and 'user.tokens' module.
Previously, OutputPage hardcoded them in the top. The new formatter doesn't.
* Remove getHeadScripts().
* Remove getInlineHeadScripts().
* Remove getExternalHeadScripts().
* Remove buildCssLinks().
* Remove getScriptsForBottomQueue().
* Change where Skin::setupSkinUserCss() is called. This methods lets the skin
add modules to the queue. Previously it was called from buildCssLinks(),
via headElement(), via prepareQuickTemplate(), via OutputPage::output().
It's now in OutputPage::output() directly (slightly earlier). This is needed
because prepareQuickTemplate() calls bottomScripts() before headElement().
And bottomScript() would lazy-initialise the queue and lock it before
setupSkinUserCss() is called from headElement().
This makes execution order more predictable instead of being dependent on
the arbitrary order of data extraction in prepareQuickTemplate (which varies
from one skin to another).
* Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early
on so. This avoids wrongful loading and fixes problem 1.
Effective changes in output:
* mw.loader.state() is now before mw.loader.load(). This fixes problem 2.
* mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading".
* mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1.
* The <script async src> tag for 'startup' changed position (slightly).
Previously it was after all inline scripts and stylesheets. It's still after
all inline scripts and after most stylesheets, but before any user styles.
Since the queue is now formatted outside OutputPage, it can't inject the
meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle
of existing output. This shouldn't have any noticable impact.
Bug: T87871
Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
|
|
|
$this->getRlClient()->getDocumentAttributes(),
|
|
|
|
|
$sk->getHtmlElementAttributes()
|
2019-08-25 16:24:55 +00:00
|
|
|
);
|
|
|
|
|
$pieces[] = Html::htmlHeader( $htmlAttribs );
|
resourceloader: Move queue formatting out of OutputPage
HTML formatting of the queue was distributed over several OutputPage methods.
Each method demanding a snippet of HTML by calling makeResourceLoaderLink()
with a limited amount of information. As such, makeResourceLoaderLink() was
unable to provide the client with the proper state information.
Centralising it also allows it to better reduce duplication in HTML output
and maintain a more accurate state.
Problems fixed by centralising:
1. The 'user' module is special (due to per-user 'version' and 'user' params).
It is manually requested via script-src. To avoid a separate (and wrong)
request from something that requires it, we set state=loading directly.
However, because the module is in the bottom, the old HTML formatter could
only put state=loading in the bottom also. This sometimes caused a wrong
request to be fired for modules=user if something in the top queue
triggered a requirement for it.
2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose
of allowing dependencies on style modules without risking duplicate loading
on pages where the styles are loaded already. This didn't work, because the
state information about page-style modules is output near the stylesheet,
which is after the script tag with mw.loader.load(). That runs first, and
mw.loader would still make a duplicate request before it learns the state.
Changes:
* Document reasons for style/script tag order in getHeadHtml (per 09537e83).
* Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed
before since a duplicate check in makeResourceLoaderLink() verified the
origin a second time.
* Declare explicit position 'top' on 'user.options' and 'user.tokens' module.
Previously, OutputPage hardcoded them in the top. The new formatter doesn't.
* Remove getHeadScripts().
* Remove getInlineHeadScripts().
* Remove getExternalHeadScripts().
* Remove buildCssLinks().
* Remove getScriptsForBottomQueue().
* Change where Skin::setupSkinUserCss() is called. This methods lets the skin
add modules to the queue. Previously it was called from buildCssLinks(),
via headElement(), via prepareQuickTemplate(), via OutputPage::output().
It's now in OutputPage::output() directly (slightly earlier). This is needed
because prepareQuickTemplate() calls bottomScripts() before headElement().
And bottomScript() would lazy-initialise the queue and lock it before
setupSkinUserCss() is called from headElement().
This makes execution order more predictable instead of being dependent on
the arbitrary order of data extraction in prepareQuickTemplate (which varies
from one skin to another).
* Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early
on so. This avoids wrongful loading and fixes problem 1.
Effective changes in output:
* mw.loader.state() is now before mw.loader.load(). This fixes problem 2.
* mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading".
* mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1.
* The <script async src> tag for 'startup' changed position (slightly).
Previously it was after all inline scripts and stylesheets. It's still after
all inline scripts and after most stylesheets, but before any user styles.
Since the queue is now formatted outside OutputPage, it can't inject the
meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle
of existing output. This shouldn't have any noticable impact.
Bug: T87871
Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
|
|
|
$pieces[] = Html::openElement( 'head' );
|
2004-07-08 14:53:54 +00:00
|
|
|
|
2010-01-06 19:59:42 +00:00
|
|
|
if ( $this->getHTMLTitle() == '' ) {
|
2013-11-12 08:36:00 +00:00
|
|
|
$this->setHTMLTitle( $this->msg( 'pagetitle', $this->getPageTitle() )->inContentLanguage() );
|
2003-04-14 23:10:40 +00:00
|
|
|
}
|
2005-02-07 14:11:59 +00:00
|
|
|
|
OutputPage: Load html5shiv without indirection of load.php
This library was introduced in 3a30e03645f, to make sure Grade C
stylesheets that apply to HTML5 elements that older browsers might
not know yet, work as expected in old IE.
It originally committed a minified version and loaded it directly
as an HTML script tag in a conditional comment. This had minimal
impact on anything else, and was easy to maintain.
In 68237fb1a74, this was changed to commit the unminified version
instead because Debian maintainers don't like packaging software
that contain minified files, despite being a simple file,
unmodified from the original (upstream publishes it in minified
form, with license header), published under a compatible free
license, and embedded in a license-compliant manner. We then
registered it as an unused ResourceLoader module, to be minified
on-the-fly at run-time.
Support for "server-registered client-unregistered" modules was
removed last week in c554ee8e64e because nothing needed it
anymore (except html5shiv apparently), which resulted in this
module being registered client-side on all page views for all
users (in latest master). This doesn't break anything, but it
is a regression performance-wise.
Restore this by (mostly) going to how it was before: A simple static
file, committed to Git, served as-is. Not related to, served by,
pulled through, nor registered with, ResourceLoader in any way.
Only difference with the original approach is that it is no longer
minified, which means a few more bytes transferred on old IE page
views, which is considered an acceptable compromise.
Bug: T201483
Change-Id: Ib0020b6bd015679b61f63eaa8109ed9b83d8ad15
2019-07-04 15:54:24 +00:00
|
|
|
if ( !Html::isXmlMimeType( $config->get( 'MimeType' ) ) ) {
|
2013-05-11 09:31:30 +00:00
|
|
|
// Add <meta charset="UTF-8">
|
|
|
|
|
// This should be before <title> since it defines the charset used by
|
|
|
|
|
// text including the text inside <title>.
|
|
|
|
|
// The spec recommends defining XHTML5's charset using the XML declaration
|
|
|
|
|
// instead of meta.
|
|
|
|
|
// Our XML declaration is output by Html::htmlHeader.
|
2016-10-13 05:34:26 +00:00
|
|
|
// https://html.spec.whatwg.org/multipage/semantics.html#attr-meta-http-equiv-content-type
|
|
|
|
|
// https://html.spec.whatwg.org/multipage/semantics.html#charset
|
2016-07-07 19:26:36 +00:00
|
|
|
$pieces[] = Html::element( 'meta', [ 'charset' => 'UTF-8' ] );
|
2013-05-11 09:31:30 +00:00
|
|
|
}
|
|
|
|
|
|
2016-07-07 19:26:36 +00:00
|
|
|
$pieces[] = Html::element( 'title', null, $this->getHTMLTitle() );
|
2019-08-25 16:24:55 +00:00
|
|
|
$pieces[] = $this->getRlClient()->getHeadHtml( $htmlAttribs['class'] ?? null );
|
resourceloader: Move queue formatting out of OutputPage
HTML formatting of the queue was distributed over several OutputPage methods.
Each method demanding a snippet of HTML by calling makeResourceLoaderLink()
with a limited amount of information. As such, makeResourceLoaderLink() was
unable to provide the client with the proper state information.
Centralising it also allows it to better reduce duplication in HTML output
and maintain a more accurate state.
Problems fixed by centralising:
1. The 'user' module is special (due to per-user 'version' and 'user' params).
It is manually requested via script-src. To avoid a separate (and wrong)
request from something that requires it, we set state=loading directly.
However, because the module is in the bottom, the old HTML formatter could
only put state=loading in the bottom also. This sometimes caused a wrong
request to be fired for modules=user if something in the top queue
triggered a requirement for it.
2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose
of allowing dependencies on style modules without risking duplicate loading
on pages where the styles are loaded already. This didn't work, because the
state information about page-style modules is output near the stylesheet,
which is after the script tag with mw.loader.load(). That runs first, and
mw.loader would still make a duplicate request before it learns the state.
Changes:
* Document reasons for style/script tag order in getHeadHtml (per 09537e83).
* Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed
before since a duplicate check in makeResourceLoaderLink() verified the
origin a second time.
* Declare explicit position 'top' on 'user.options' and 'user.tokens' module.
Previously, OutputPage hardcoded them in the top. The new formatter doesn't.
* Remove getHeadScripts().
* Remove getInlineHeadScripts().
* Remove getExternalHeadScripts().
* Remove buildCssLinks().
* Remove getScriptsForBottomQueue().
* Change where Skin::setupSkinUserCss() is called. This methods lets the skin
add modules to the queue. Previously it was called from buildCssLinks(),
via headElement(), via prepareQuickTemplate(), via OutputPage::output().
It's now in OutputPage::output() directly (slightly earlier). This is needed
because prepareQuickTemplate() calls bottomScripts() before headElement().
And bottomScript() would lazy-initialise the queue and lock it before
setupSkinUserCss() is called from headElement().
This makes execution order more predictable instead of being dependent on
the arbitrary order of data extraction in prepareQuickTemplate (which varies
from one skin to another).
* Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early
on so. This avoids wrongful loading and fixes problem 1.
Effective changes in output:
* mw.loader.state() is now before mw.loader.load(). This fixes problem 2.
* mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading".
* mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1.
* The <script async src> tag for 'startup' changed position (slightly).
Previously it was after all inline scripts and stylesheets. It's still after
all inline scripts and after most stylesheets, but before any user styles.
Since the queue is now formatted outside OutputPage, it can't inject the
meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle
of existing output. This shouldn't have any noticable impact.
Bug: T87871
Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
|
|
|
$pieces[] = $this->buildExemptModules();
|
|
|
|
|
$pieces = array_merge( $pieces, array_values( $this->getHeadLinksArray() ) );
|
|
|
|
|
$pieces = array_merge( $pieces, array_values( $this->mHeadItems ) );
|
2017-08-03 15:35:10 +00:00
|
|
|
|
OutputPage: Load html5shiv without indirection of load.php
This library was introduced in 3a30e03645f, to make sure Grade C
stylesheets that apply to HTML5 elements that older browsers might
not know yet, work as expected in old IE.
It originally committed a minified version and loaded it directly
as an HTML script tag in a conditional comment. This had minimal
impact on anything else, and was easy to maintain.
In 68237fb1a74, this was changed to commit the unminified version
instead because Debian maintainers don't like packaging software
that contain minified files, despite being a simple file,
unmodified from the original (upstream publishes it in minified
form, with license header), published under a compatible free
license, and embedded in a license-compliant manner. We then
registered it as an unused ResourceLoader module, to be minified
on-the-fly at run-time.
Support for "server-registered client-unregistered" modules was
removed last week in c554ee8e64e because nothing needed it
anymore (except html5shiv apparently), which resulted in this
module being registered client-side on all page views for all
users (in latest master). This doesn't break anything, but it
is a regression performance-wise.
Restore this by (mostly) going to how it was before: A simple static
file, committed to Git, served as-is. Not related to, served by,
pulled through, nor registered with, ResourceLoader in any way.
Only difference with the original approach is that it is no longer
minified, which means a few more bytes transferred on old IE page
views, which is considered an acceptable compromise.
Bug: T201483
Change-Id: Ib0020b6bd015679b61f63eaa8109ed9b83d8ad15
2019-07-04 15:54:24 +00:00
|
|
|
// This library is intended to run on older browsers that MediaWiki no longer
|
|
|
|
|
// supports as Grade A. For these Grade C browsers, we provide an experience
|
2019-08-03 21:45:48 +00:00
|
|
|
// using only HTML and CSS. But, where standards-compliant browsers are able to
|
|
|
|
|
// style unknown HTML elements without issue, old IE ignores these styles.
|
OutputPage: Load html5shiv without indirection of load.php
This library was introduced in 3a30e03645f, to make sure Grade C
stylesheets that apply to HTML5 elements that older browsers might
not know yet, work as expected in old IE.
It originally committed a minified version and loaded it directly
as an HTML script tag in a conditional comment. This had minimal
impact on anything else, and was easy to maintain.
In 68237fb1a74, this was changed to commit the unminified version
instead because Debian maintainers don't like packaging software
that contain minified files, despite being a simple file,
unmodified from the original (upstream publishes it in minified
form, with license header), published under a compatible free
license, and embedded in a license-compliant manner. We then
registered it as an unused ResourceLoader module, to be minified
on-the-fly at run-time.
Support for "server-registered client-unregistered" modules was
removed last week in c554ee8e64e because nothing needed it
anymore (except html5shiv apparently), which resulted in this
module being registered client-side on all page views for all
users (in latest master). This doesn't break anything, but it
is a regression performance-wise.
Restore this by (mostly) going to how it was before: A simple static
file, committed to Git, served as-is. Not related to, served by,
pulled through, nor registered with, ResourceLoader in any way.
Only difference with the original approach is that it is no longer
minified, which means a few more bytes transferred on old IE page
views, which is considered an acceptable compromise.
Bug: T201483
Change-Id: Ib0020b6bd015679b61f63eaa8109ed9b83d8ad15
2019-07-04 15:54:24 +00:00
|
|
|
// The html5shiv library fixes that.
|
2017-08-03 15:35:10 +00:00
|
|
|
// Use an IE conditional comment to serve the script only to old IE
|
OutputPage: Load html5shiv without indirection of load.php
This library was introduced in 3a30e03645f, to make sure Grade C
stylesheets that apply to HTML5 elements that older browsers might
not know yet, work as expected in old IE.
It originally committed a minified version and loaded it directly
as an HTML script tag in a conditional comment. This had minimal
impact on anything else, and was easy to maintain.
In 68237fb1a74, this was changed to commit the unminified version
instead because Debian maintainers don't like packaging software
that contain minified files, despite being a simple file,
unmodified from the original (upstream publishes it in minified
form, with license header), published under a compatible free
license, and embedded in a license-compliant manner. We then
registered it as an unused ResourceLoader module, to be minified
on-the-fly at run-time.
Support for "server-registered client-unregistered" modules was
removed last week in c554ee8e64e because nothing needed it
anymore (except html5shiv apparently), which resulted in this
module being registered client-side on all page views for all
users (in latest master). This doesn't break anything, but it
is a regression performance-wise.
Restore this by (mostly) going to how it was before: A simple static
file, committed to Git, served as-is. Not related to, served by,
pulled through, nor registered with, ResourceLoader in any way.
Only difference with the original approach is that it is no longer
minified, which means a few more bytes transferred on old IE page
views, which is considered an acceptable compromise.
Bug: T201483
Change-Id: Ib0020b6bd015679b61f63eaa8109ed9b83d8ad15
2019-07-04 15:54:24 +00:00
|
|
|
$shivUrl = $config->get( 'ResourceBasePath' ) . '/resources/lib/html5shiv/html5shiv.js';
|
2017-08-03 15:35:10 +00:00
|
|
|
$pieces[] = '<!--[if lt IE 9]>' .
|
2019-10-28 05:01:17 +00:00
|
|
|
Html::linkedScript( $shivUrl, $this->CSP->getNonce() ) .
|
2017-08-03 15:35:10 +00:00
|
|
|
'<![endif]-->';
|
|
|
|
|
|
resourceloader: Move queue formatting out of OutputPage
HTML formatting of the queue was distributed over several OutputPage methods.
Each method demanding a snippet of HTML by calling makeResourceLoaderLink()
with a limited amount of information. As such, makeResourceLoaderLink() was
unable to provide the client with the proper state information.
Centralising it also allows it to better reduce duplication in HTML output
and maintain a more accurate state.
Problems fixed by centralising:
1. The 'user' module is special (due to per-user 'version' and 'user' params).
It is manually requested via script-src. To avoid a separate (and wrong)
request from something that requires it, we set state=loading directly.
However, because the module is in the bottom, the old HTML formatter could
only put state=loading in the bottom also. This sometimes caused a wrong
request to be fired for modules=user if something in the top queue
triggered a requirement for it.
2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose
of allowing dependencies on style modules without risking duplicate loading
on pages where the styles are loaded already. This didn't work, because the
state information about page-style modules is output near the stylesheet,
which is after the script tag with mw.loader.load(). That runs first, and
mw.loader would still make a duplicate request before it learns the state.
Changes:
* Document reasons for style/script tag order in getHeadHtml (per 09537e83).
* Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed
before since a duplicate check in makeResourceLoaderLink() verified the
origin a second time.
* Declare explicit position 'top' on 'user.options' and 'user.tokens' module.
Previously, OutputPage hardcoded them in the top. The new formatter doesn't.
* Remove getHeadScripts().
* Remove getInlineHeadScripts().
* Remove getExternalHeadScripts().
* Remove buildCssLinks().
* Remove getScriptsForBottomQueue().
* Change where Skin::setupSkinUserCss() is called. This methods lets the skin
add modules to the queue. Previously it was called from buildCssLinks(),
via headElement(), via prepareQuickTemplate(), via OutputPage::output().
It's now in OutputPage::output() directly (slightly earlier). This is needed
because prepareQuickTemplate() calls bottomScripts() before headElement().
And bottomScript() would lazy-initialise the queue and lock it before
setupSkinUserCss() is called from headElement().
This makes execution order more predictable instead of being dependent on
the arbitrary order of data extraction in prepareQuickTemplate (which varies
from one skin to another).
* Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early
on so. This avoids wrongful loading and fixes problem 1.
Effective changes in output:
* mw.loader.state() is now before mw.loader.load(). This fixes problem 2.
* mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading".
* mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1.
* The <script async src> tag for 'startup' changed position (slightly).
Previously it was after all inline scripts and stylesheets. It's still after
all inline scripts and after most stylesheets, but before any user styles.
Since the queue is now formatted outside OutputPage, it can't inject the
meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle
of existing output. This shouldn't have any noticable impact.
Bug: T87871
Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
|
|
|
$pieces[] = Html::closeElement( 'head' );
|
2010-01-15 01:16:52 +00:00
|
|
|
|
2017-09-11 07:12:03 +00:00
|
|
|
$bodyClasses = $this->mAdditionalBodyClasses;
|
2013-04-22 13:57:44 +00:00
|
|
|
$bodyClasses[] = 'mediawiki';
|
2010-01-15 01:16:52 +00:00
|
|
|
|
2011-07-06 02:26:06 +00:00
|
|
|
# Classes for LTR/RTL directionality support
|
2013-04-22 13:57:44 +00:00
|
|
|
$bodyClasses[] = $userdir;
|
|
|
|
|
$bodyClasses[] = "sitedir-$sitedir";
|
2010-01-15 01:16:52 +00:00
|
|
|
|
2016-09-07 22:51:48 +00:00
|
|
|
$underline = $this->getUser()->getOption( 'underline' );
|
|
|
|
|
if ( $underline < 2 ) {
|
|
|
|
|
// The following classes can be used here:
|
|
|
|
|
// * mw-underline-always
|
|
|
|
|
// * mw-underline-never
|
|
|
|
|
$bodyClasses[] = 'mw-underline-' . ( $underline ? 'always' : 'never' );
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-21 16:13:21 +00:00
|
|
|
if ( $this->getLanguage()->capitalizeAllNouns() ) {
|
2010-01-15 01:16:52 +00:00
|
|
|
# A <body> class is probably not the best way to do this . . .
|
2013-04-22 13:57:44 +00:00
|
|
|
$bodyClasses[] = 'capitalize-all-nouns';
|
2010-01-15 01:16:52 +00:00
|
|
|
}
|
|
|
|
|
|
Hide marked empty elements by default (stage 1)
We originally imagined rolling out the display of empty elements
simultaneously with the Html5Depurate, but now we have added support for
marking empty elements to Html5Depurate and plan on having some sort of
longer migration period. So, move the relevant CSS to content.css, and
remove the concept of CSS dependant on tidy driver.
Add a body class which will allow the effect to be toggled in a gadget or
extension. Actual toggling in the CSS will be in the stage 2 patch, to be
deployed after the varnish cache and parser cache have expired.
I originally imagined that there would be a gadget that overrides the
rule with an !important selector, but that method does not allow you to
recover the original display property, which is often overridden by the
style attribute or site CSS to be "inline".
Also, in RaggettWrapper, switch to the new class mw-empty-elt, following
Html5Depurate, instead of mw-empty-li. The old class will be removed in
the stage 2 patch.
Change-Id: Ic0f432c43a006629ca5a1a7c2dda3552ceb4dc4f
2016-07-13 02:01:59 +00:00
|
|
|
// Parser feature migration class
|
|
|
|
|
// The idea is that this will eventually be removed, after the wikitext
|
|
|
|
|
// which requires it is cleaned up.
|
|
|
|
|
$bodyClasses[] = 'mw-hide-empty-elt';
|
|
|
|
|
|
2013-04-22 13:57:44 +00:00
|
|
|
$bodyClasses[] = $sk->getPageClasses( $this->getTitle() );
|
|
|
|
|
$bodyClasses[] = 'skin-' . Sanitizer::escapeClass( $sk->getSkinName() );
|
2014-05-11 01:54:15 +00:00
|
|
|
$bodyClasses[] =
|
|
|
|
|
'action-' . Sanitizer::escapeClass( Action::getActionName( $this->getContext() ) );
|
2013-04-22 13:57:44 +00:00
|
|
|
|
2016-02-17 09:09:32 +00:00
|
|
|
$bodyAttrs = [];
|
2013-04-22 13:57:44 +00:00
|
|
|
// While the implode() is not strictly needed, it's used for backwards compatibility
|
|
|
|
|
// (this used to be built as a string and hooks likely still expect that).
|
|
|
|
|
$bodyAttrs['class'] = implode( ' ', $bodyClasses );
|
|
|
|
|
|
|
|
|
|
// Allow skins and extensions to add body attributes they need
|
|
|
|
|
$sk->addToBodyAttributes( $this, $bodyAttrs );
|
2016-02-17 09:09:32 +00:00
|
|
|
Hooks::run( 'OutputPageBodyAttributes', [ $this, $sk, &$bodyAttrs ] );
|
2010-10-03 14:12:41 +00:00
|
|
|
|
2016-07-07 19:26:36 +00:00
|
|
|
$pieces[] = Html::openElement( 'body', $bodyAttrs );
|
2010-01-15 01:16:52 +00:00
|
|
|
|
resourceloader: Move queue formatting out of OutputPage
HTML formatting of the queue was distributed over several OutputPage methods.
Each method demanding a snippet of HTML by calling makeResourceLoaderLink()
with a limited amount of information. As such, makeResourceLoaderLink() was
unable to provide the client with the proper state information.
Centralising it also allows it to better reduce duplication in HTML output
and maintain a more accurate state.
Problems fixed by centralising:
1. The 'user' module is special (due to per-user 'version' and 'user' params).
It is manually requested via script-src. To avoid a separate (and wrong)
request from something that requires it, we set state=loading directly.
However, because the module is in the bottom, the old HTML formatter could
only put state=loading in the bottom also. This sometimes caused a wrong
request to be fired for modules=user if something in the top queue
triggered a requirement for it.
2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose
of allowing dependencies on style modules without risking duplicate loading
on pages where the styles are loaded already. This didn't work, because the
state information about page-style modules is output near the stylesheet,
which is after the script tag with mw.loader.load(). That runs first, and
mw.loader would still make a duplicate request before it learns the state.
Changes:
* Document reasons for style/script tag order in getHeadHtml (per 09537e83).
* Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed
before since a duplicate check in makeResourceLoaderLink() verified the
origin a second time.
* Declare explicit position 'top' on 'user.options' and 'user.tokens' module.
Previously, OutputPage hardcoded them in the top. The new formatter doesn't.
* Remove getHeadScripts().
* Remove getInlineHeadScripts().
* Remove getExternalHeadScripts().
* Remove buildCssLinks().
* Remove getScriptsForBottomQueue().
* Change where Skin::setupSkinUserCss() is called. This methods lets the skin
add modules to the queue. Previously it was called from buildCssLinks(),
via headElement(), via prepareQuickTemplate(), via OutputPage::output().
It's now in OutputPage::output() directly (slightly earlier). This is needed
because prepareQuickTemplate() calls bottomScripts() before headElement().
And bottomScript() would lazy-initialise the queue and lock it before
setupSkinUserCss() is called from headElement().
This makes execution order more predictable instead of being dependent on
the arbitrary order of data extraction in prepareQuickTemplate (which varies
from one skin to another).
* Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early
on so. This avoids wrongful loading and fixes problem 1.
Effective changes in output:
* mw.loader.state() is now before mw.loader.load(). This fixes problem 2.
* mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading".
* mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1.
* The <script async src> tag for 'startup' changed position (slightly).
Previously it was after all inline scripts and stylesheets. It's still after
all inline scripts and after most stylesheets, but before any user styles.
Since the queue is now formatted outside OutputPage, it can't inject the
meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle
of existing output. This shouldn't have any noticable impact.
Bug: T87871
Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
|
|
|
return self::combineWrappedStrings( $pieces );
|
2004-04-09 04:53:52 +00:00
|
|
|
}
|
2010-10-02 13:45:35 +00:00
|
|
|
|
* Made Resources.php return a pure-data array instead of an ugly mix of data and code. This allows the class code to be lazy-loaded with the autoloader, for a performance advantage especially on non-APC installs. And using the convention where if the class is omitted, ResourceLoaderFileModule is assumed, the registration code becomes shorter and simpler.
* Modified ResourceLoader to lazy-initialise module objects, for a further performance advantage.
* Deleted ResourceLoader::getModules(), provided getModuleNames() instead. Although the startup module needs this functionality, it's slow to generate, so to avoid misuse, it's better to provide a foolproof fast interface and let the startup module do the slow thing itself.
* Modified ResourceLoader::register() to optionally accept an info array instead of an object.
* Added $wgResourceModules, allowing extensions to efficiently define their own resource loader modules. The trouble with hooks is that they contain code, and code is slow. We've been through all this before with i18n. Hooks are useful as a performance tool only if you call them very rarely.
* Moved ResourceLoader settings to their own section in DefaultSettings.php
* Added options to ResourceLoaderFileModule equivalent to the $localBasePath and $remoteBasePath parameters, to allow it to be instantiated via the new array style. Also added remoteExtPath, which allows modules to be registered before $wgExtensionAssetsPath is known.
* Added OutputPage::getResourceLoader(), mostly for debugging.
* The time saving at the moment is about 5ms per request with no extensions, which is significant already with 6 load.php requests for a cold cache page view. This is a much more scalable interface; the relative saving will grow as more extensions are added which use this interface, especially for non-APC installs.
Although the interface is backwards compatible, extension updates will follow in a subsequent commit.
2010-11-19 10:41:06 +00:00
|
|
|
/**
|
|
|
|
|
* Get a ResourceLoader object associated with this OutputPage
|
2011-02-21 17:12:33 +00:00
|
|
|
*
|
|
|
|
|
* @return ResourceLoader
|
* Made Resources.php return a pure-data array instead of an ugly mix of data and code. This allows the class code to be lazy-loaded with the autoloader, for a performance advantage especially on non-APC installs. And using the convention where if the class is omitted, ResourceLoaderFileModule is assumed, the registration code becomes shorter and simpler.
* Modified ResourceLoader to lazy-initialise module objects, for a further performance advantage.
* Deleted ResourceLoader::getModules(), provided getModuleNames() instead. Although the startup module needs this functionality, it's slow to generate, so to avoid misuse, it's better to provide a foolproof fast interface and let the startup module do the slow thing itself.
* Modified ResourceLoader::register() to optionally accept an info array instead of an object.
* Added $wgResourceModules, allowing extensions to efficiently define their own resource loader modules. The trouble with hooks is that they contain code, and code is slow. We've been through all this before with i18n. Hooks are useful as a performance tool only if you call them very rarely.
* Moved ResourceLoader settings to their own section in DefaultSettings.php
* Added options to ResourceLoaderFileModule equivalent to the $localBasePath and $remoteBasePath parameters, to allow it to be instantiated via the new array style. Also added remoteExtPath, which allows modules to be registered before $wgExtensionAssetsPath is known.
* Added OutputPage::getResourceLoader(), mostly for debugging.
* The time saving at the moment is about 5ms per request with no extensions, which is significant already with 6 load.php requests for a cold cache page view. This is a much more scalable interface; the relative saving will grow as more extensions are added which use this interface, especially for non-APC installs.
Although the interface is backwards compatible, extension updates will follow in a subsequent commit.
2010-11-19 10:41:06 +00:00
|
|
|
*/
|
|
|
|
|
public function getResourceLoader() {
|
2020-01-09 23:48:34 +00:00
|
|
|
if ( $this->mResourceLoader === null ) {
|
2019-02-16 23:16:09 +00:00
|
|
|
// Lazy-initialise as needed
|
|
|
|
|
$this->mResourceLoader = MediaWikiServices::getInstance()->getResourceLoader();
|
* Made Resources.php return a pure-data array instead of an ugly mix of data and code. This allows the class code to be lazy-loaded with the autoloader, for a performance advantage especially on non-APC installs. And using the convention where if the class is omitted, ResourceLoaderFileModule is assumed, the registration code becomes shorter and simpler.
* Modified ResourceLoader to lazy-initialise module objects, for a further performance advantage.
* Deleted ResourceLoader::getModules(), provided getModuleNames() instead. Although the startup module needs this functionality, it's slow to generate, so to avoid misuse, it's better to provide a foolproof fast interface and let the startup module do the slow thing itself.
* Modified ResourceLoader::register() to optionally accept an info array instead of an object.
* Added $wgResourceModules, allowing extensions to efficiently define their own resource loader modules. The trouble with hooks is that they contain code, and code is slow. We've been through all this before with i18n. Hooks are useful as a performance tool only if you call them very rarely.
* Moved ResourceLoader settings to their own section in DefaultSettings.php
* Added options to ResourceLoaderFileModule equivalent to the $localBasePath and $remoteBasePath parameters, to allow it to be instantiated via the new array style. Also added remoteExtPath, which allows modules to be registered before $wgExtensionAssetsPath is known.
* Added OutputPage::getResourceLoader(), mostly for debugging.
* The time saving at the moment is about 5ms per request with no extensions, which is significant already with 6 load.php requests for a cold cache page view. This is a much more scalable interface; the relative saving will grow as more extensions are added which use this interface, especially for non-APC installs.
Although the interface is backwards compatible, extension updates will follow in a subsequent commit.
2010-11-19 10:41:06 +00:00
|
|
|
}
|
|
|
|
|
return $this->mResourceLoader;
|
2011-04-23 19:28:35 +00:00
|
|
|
}
|
* Made Resources.php return a pure-data array instead of an ugly mix of data and code. This allows the class code to be lazy-loaded with the autoloader, for a performance advantage especially on non-APC installs. And using the convention where if the class is omitted, ResourceLoaderFileModule is assumed, the registration code becomes shorter and simpler.
* Modified ResourceLoader to lazy-initialise module objects, for a further performance advantage.
* Deleted ResourceLoader::getModules(), provided getModuleNames() instead. Although the startup module needs this functionality, it's slow to generate, so to avoid misuse, it's better to provide a foolproof fast interface and let the startup module do the slow thing itself.
* Modified ResourceLoader::register() to optionally accept an info array instead of an object.
* Added $wgResourceModules, allowing extensions to efficiently define their own resource loader modules. The trouble with hooks is that they contain code, and code is slow. We've been through all this before with i18n. Hooks are useful as a performance tool only if you call them very rarely.
* Moved ResourceLoader settings to their own section in DefaultSettings.php
* Added options to ResourceLoaderFileModule equivalent to the $localBasePath and $remoteBasePath parameters, to allow it to be instantiated via the new array style. Also added remoteExtPath, which allows modules to be registered before $wgExtensionAssetsPath is known.
* Added OutputPage::getResourceLoader(), mostly for debugging.
* The time saving at the moment is about 5ms per request with no extensions, which is significant already with 6 load.php requests for a cold cache page view. This is a much more scalable interface; the relative saving will grow as more extensions are added which use this interface, especially for non-APC installs.
Although the interface is backwards compatible, extension updates will follow in a subsequent commit.
2010-11-19 10:41:06 +00:00
|
|
|
|
2010-11-06 18:52:32 +00:00
|
|
|
/**
|
resourceloader: Move queue formatting out of OutputPage
HTML formatting of the queue was distributed over several OutputPage methods.
Each method demanding a snippet of HTML by calling makeResourceLoaderLink()
with a limited amount of information. As such, makeResourceLoaderLink() was
unable to provide the client with the proper state information.
Centralising it also allows it to better reduce duplication in HTML output
and maintain a more accurate state.
Problems fixed by centralising:
1. The 'user' module is special (due to per-user 'version' and 'user' params).
It is manually requested via script-src. To avoid a separate (and wrong)
request from something that requires it, we set state=loading directly.
However, because the module is in the bottom, the old HTML formatter could
only put state=loading in the bottom also. This sometimes caused a wrong
request to be fired for modules=user if something in the top queue
triggered a requirement for it.
2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose
of allowing dependencies on style modules without risking duplicate loading
on pages where the styles are loaded already. This didn't work, because the
state information about page-style modules is output near the stylesheet,
which is after the script tag with mw.loader.load(). That runs first, and
mw.loader would still make a duplicate request before it learns the state.
Changes:
* Document reasons for style/script tag order in getHeadHtml (per 09537e83).
* Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed
before since a duplicate check in makeResourceLoaderLink() verified the
origin a second time.
* Declare explicit position 'top' on 'user.options' and 'user.tokens' module.
Previously, OutputPage hardcoded them in the top. The new formatter doesn't.
* Remove getHeadScripts().
* Remove getInlineHeadScripts().
* Remove getExternalHeadScripts().
* Remove buildCssLinks().
* Remove getScriptsForBottomQueue().
* Change where Skin::setupSkinUserCss() is called. This methods lets the skin
add modules to the queue. Previously it was called from buildCssLinks(),
via headElement(), via prepareQuickTemplate(), via OutputPage::output().
It's now in OutputPage::output() directly (slightly earlier). This is needed
because prepareQuickTemplate() calls bottomScripts() before headElement().
And bottomScript() would lazy-initialise the queue and lock it before
setupSkinUserCss() is called from headElement().
This makes execution order more predictable instead of being dependent on
the arbitrary order of data extraction in prepareQuickTemplate (which varies
from one skin to another).
* Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early
on so. This avoids wrongful loading and fixes problem 1.
Effective changes in output:
* mw.loader.state() is now before mw.loader.load(). This fixes problem 2.
* mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading".
* mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1.
* The <script async src> tag for 'startup' changed position (slightly).
Previously it was after all inline scripts and stylesheets. It's still after
all inline scripts and after most stylesheets, but before any user styles.
Since the queue is now formatted outside OutputPage, it can't inject the
meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle
of existing output. This shouldn't have any noticable impact.
Bug: T87871
Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
|
|
|
* Explicily load or embed modules on a page.
|
2015-07-28 02:46:00 +00:00
|
|
|
*
|
2013-11-14 16:54:19 +00:00
|
|
|
* @param array|string $modules One or more module names
|
2013-03-11 17:15:01 +00:00
|
|
|
* @param string $only ResourceLoaderModule TYPE_ class constant
|
2015-07-28 02:46:00 +00:00
|
|
|
* @param array $extraQuery [optional] Array with extra query parameters for the request
|
resourceloader: Move queue formatting out of OutputPage
HTML formatting of the queue was distributed over several OutputPage methods.
Each method demanding a snippet of HTML by calling makeResourceLoaderLink()
with a limited amount of information. As such, makeResourceLoaderLink() was
unable to provide the client with the proper state information.
Centralising it also allows it to better reduce duplication in HTML output
and maintain a more accurate state.
Problems fixed by centralising:
1. The 'user' module is special (due to per-user 'version' and 'user' params).
It is manually requested via script-src. To avoid a separate (and wrong)
request from something that requires it, we set state=loading directly.
However, because the module is in the bottom, the old HTML formatter could
only put state=loading in the bottom also. This sometimes caused a wrong
request to be fired for modules=user if something in the top queue
triggered a requirement for it.
2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose
of allowing dependencies on style modules without risking duplicate loading
on pages where the styles are loaded already. This didn't work, because the
state information about page-style modules is output near the stylesheet,
which is after the script tag with mw.loader.load(). That runs first, and
mw.loader would still make a duplicate request before it learns the state.
Changes:
* Document reasons for style/script tag order in getHeadHtml (per 09537e83).
* Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed
before since a duplicate check in makeResourceLoaderLink() verified the
origin a second time.
* Declare explicit position 'top' on 'user.options' and 'user.tokens' module.
Previously, OutputPage hardcoded them in the top. The new formatter doesn't.
* Remove getHeadScripts().
* Remove getInlineHeadScripts().
* Remove getExternalHeadScripts().
* Remove buildCssLinks().
* Remove getScriptsForBottomQueue().
* Change where Skin::setupSkinUserCss() is called. This methods lets the skin
add modules to the queue. Previously it was called from buildCssLinks(),
via headElement(), via prepareQuickTemplate(), via OutputPage::output().
It's now in OutputPage::output() directly (slightly earlier). This is needed
because prepareQuickTemplate() calls bottomScripts() before headElement().
And bottomScript() would lazy-initialise the queue and lock it before
setupSkinUserCss() is called from headElement().
This makes execution order more predictable instead of being dependent on
the arbitrary order of data extraction in prepareQuickTemplate (which varies
from one skin to another).
* Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early
on so. This avoids wrongful loading and fixes problem 1.
Effective changes in output:
* mw.loader.state() is now before mw.loader.load(). This fixes problem 2.
* mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading".
* mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1.
* The <script async src> tag for 'startup' changed position (slightly).
Previously it was after all inline scripts and stylesheets. It's still after
all inline scripts and after most stylesheets, but before any user styles.
Since the queue is now formatted outside OutputPage, it can't inject the
meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle
of existing output. This shouldn't have any noticable impact.
Bug: T87871
Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
|
|
|
* @return string|WrappedStringList HTML
|
2015-07-28 02:46:00 +00:00
|
|
|
*/
|
2016-02-17 09:09:32 +00:00
|
|
|
public function makeResourceLoaderLink( $modules, $only, array $extraQuery = [] ) {
|
2016-08-16 22:25:08 +00:00
|
|
|
// Apply 'target' and 'origin' filters
|
|
|
|
|
$modules = $this->filterModules( (array)$modules, null, $only );
|
|
|
|
|
|
resourceloader: Move queue formatting out of OutputPage
HTML formatting of the queue was distributed over several OutputPage methods.
Each method demanding a snippet of HTML by calling makeResourceLoaderLink()
with a limited amount of information. As such, makeResourceLoaderLink() was
unable to provide the client with the proper state information.
Centralising it also allows it to better reduce duplication in HTML output
and maintain a more accurate state.
Problems fixed by centralising:
1. The 'user' module is special (due to per-user 'version' and 'user' params).
It is manually requested via script-src. To avoid a separate (and wrong)
request from something that requires it, we set state=loading directly.
However, because the module is in the bottom, the old HTML formatter could
only put state=loading in the bottom also. This sometimes caused a wrong
request to be fired for modules=user if something in the top queue
triggered a requirement for it.
2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose
of allowing dependencies on style modules without risking duplicate loading
on pages where the styles are loaded already. This didn't work, because the
state information about page-style modules is output near the stylesheet,
which is after the script tag with mw.loader.load(). That runs first, and
mw.loader would still make a duplicate request before it learns the state.
Changes:
* Document reasons for style/script tag order in getHeadHtml (per 09537e83).
* Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed
before since a duplicate check in makeResourceLoaderLink() verified the
origin a second time.
* Declare explicit position 'top' on 'user.options' and 'user.tokens' module.
Previously, OutputPage hardcoded them in the top. The new formatter doesn't.
* Remove getHeadScripts().
* Remove getInlineHeadScripts().
* Remove getExternalHeadScripts().
* Remove buildCssLinks().
* Remove getScriptsForBottomQueue().
* Change where Skin::setupSkinUserCss() is called. This methods lets the skin
add modules to the queue. Previously it was called from buildCssLinks(),
via headElement(), via prepareQuickTemplate(), via OutputPage::output().
It's now in OutputPage::output() directly (slightly earlier). This is needed
because prepareQuickTemplate() calls bottomScripts() before headElement().
And bottomScript() would lazy-initialise the queue and lock it before
setupSkinUserCss() is called from headElement().
This makes execution order more predictable instead of being dependent on
the arbitrary order of data extraction in prepareQuickTemplate (which varies
from one skin to another).
* Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early
on so. This avoids wrongful loading and fixes problem 1.
Effective changes in output:
* mw.loader.state() is now before mw.loader.load(). This fixes problem 2.
* mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading".
* mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1.
* The <script async src> tag for 'startup' changed position (slightly).
Previously it was after all inline scripts and stylesheets. It's still after
all inline scripts and after most stylesheets, but before any user styles.
Since the queue is now formatted outside OutputPage, it can't inject the
meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle
of existing output. This shouldn't have any noticable impact.
Bug: T87871
Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
|
|
|
return ResourceLoaderClientHtml::makeLoad(
|
|
|
|
|
$this->getRlClientContext(),
|
2016-08-16 22:25:08 +00:00
|
|
|
$modules,
|
resourceloader: Move queue formatting out of OutputPage
HTML formatting of the queue was distributed over several OutputPage methods.
Each method demanding a snippet of HTML by calling makeResourceLoaderLink()
with a limited amount of information. As such, makeResourceLoaderLink() was
unable to provide the client with the proper state information.
Centralising it also allows it to better reduce duplication in HTML output
and maintain a more accurate state.
Problems fixed by centralising:
1. The 'user' module is special (due to per-user 'version' and 'user' params).
It is manually requested via script-src. To avoid a separate (and wrong)
request from something that requires it, we set state=loading directly.
However, because the module is in the bottom, the old HTML formatter could
only put state=loading in the bottom also. This sometimes caused a wrong
request to be fired for modules=user if something in the top queue
triggered a requirement for it.
2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose
of allowing dependencies on style modules without risking duplicate loading
on pages where the styles are loaded already. This didn't work, because the
state information about page-style modules is output near the stylesheet,
which is after the script tag with mw.loader.load(). That runs first, and
mw.loader would still make a duplicate request before it learns the state.
Changes:
* Document reasons for style/script tag order in getHeadHtml (per 09537e83).
* Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed
before since a duplicate check in makeResourceLoaderLink() verified the
origin a second time.
* Declare explicit position 'top' on 'user.options' and 'user.tokens' module.
Previously, OutputPage hardcoded them in the top. The new formatter doesn't.
* Remove getHeadScripts().
* Remove getInlineHeadScripts().
* Remove getExternalHeadScripts().
* Remove buildCssLinks().
* Remove getScriptsForBottomQueue().
* Change where Skin::setupSkinUserCss() is called. This methods lets the skin
add modules to the queue. Previously it was called from buildCssLinks(),
via headElement(), via prepareQuickTemplate(), via OutputPage::output().
It's now in OutputPage::output() directly (slightly earlier). This is needed
because prepareQuickTemplate() calls bottomScripts() before headElement().
And bottomScript() would lazy-initialise the queue and lock it before
setupSkinUserCss() is called from headElement().
This makes execution order more predictable instead of being dependent on
the arbitrary order of data extraction in prepareQuickTemplate (which varies
from one skin to another).
* Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early
on so. This avoids wrongful loading and fixes problem 1.
Effective changes in output:
* mw.loader.state() is now before mw.loader.load(). This fixes problem 2.
* mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading".
* mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1.
* The <script async src> tag for 'startup' changed position (slightly).
Previously it was after all inline scripts and stylesheets. It's still after
all inline scripts and after most stylesheets, but before any user styles.
Since the queue is now formatted outside OutputPage, it can't inject the
meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle
of existing output. This shouldn't have any noticable impact.
Bug: T87871
Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
|
|
|
$only,
|
Initial support for Content Security Policy, disabled by default
The primary goal here is a defense in depth measure to
stop an attacker who found a bug in the parser allowing
them to insert malicious attributes.
This wouldn't stop someone who could insert a full
script tag (since at current it can't distinguish between
malicious and legit user js). It also would not prevent
DOM-based or reflected XSS for anons, as the nonce value
is guessable for anons when receiving a response cached
by varnish. However, the limited protection of just stopping
stored XSS where the attacker only has control of attributes,
is still a big win in my opinion. (But it wouldn't prevent
someone who has that type of xss from abusing things like
data-ooui attribute).
This will likely break many gadgets. Its expected that any
sort of rollout on Wikimedia will be done very slowly, with
lots of testing and the report-only option to begin with.
This is behind feature flags that are off by default, so
merging this patch should not cause any change in default
behaviour.
This may break some extensions (The most obvious one
is charinsert (See fe648d41005), but will probably need
some testing in report-only mode to see if anything else breaks)
This uses the unsafe-eval option of CSP, in order to
support RL's local storage thingy. For better security,
we may want to remove some of the sillier uses of eval
(e.g. jquery.ui.datepicker.js).
For more info, see spec: https://www.w3.org/TR/CSP2/
Additionally see:
https://www.mediawiki.org/wiki/Requests_for_comment/Content-Security-Policy
Bug: T135963
Change-Id: I80f6f469ba4c0b608385483457df96ccb7429ae5
2016-02-29 04:13:10 +00:00
|
|
|
$extraQuery,
|
2019-10-28 05:01:17 +00:00
|
|
|
$this->CSP->getNonce()
|
resourceloader: Move queue formatting out of OutputPage
HTML formatting of the queue was distributed over several OutputPage methods.
Each method demanding a snippet of HTML by calling makeResourceLoaderLink()
with a limited amount of information. As such, makeResourceLoaderLink() was
unable to provide the client with the proper state information.
Centralising it also allows it to better reduce duplication in HTML output
and maintain a more accurate state.
Problems fixed by centralising:
1. The 'user' module is special (due to per-user 'version' and 'user' params).
It is manually requested via script-src. To avoid a separate (and wrong)
request from something that requires it, we set state=loading directly.
However, because the module is in the bottom, the old HTML formatter could
only put state=loading in the bottom also. This sometimes caused a wrong
request to be fired for modules=user if something in the top queue
triggered a requirement for it.
2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose
of allowing dependencies on style modules without risking duplicate loading
on pages where the styles are loaded already. This didn't work, because the
state information about page-style modules is output near the stylesheet,
which is after the script tag with mw.loader.load(). That runs first, and
mw.loader would still make a duplicate request before it learns the state.
Changes:
* Document reasons for style/script tag order in getHeadHtml (per 09537e83).
* Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed
before since a duplicate check in makeResourceLoaderLink() verified the
origin a second time.
* Declare explicit position 'top' on 'user.options' and 'user.tokens' module.
Previously, OutputPage hardcoded them in the top. The new formatter doesn't.
* Remove getHeadScripts().
* Remove getInlineHeadScripts().
* Remove getExternalHeadScripts().
* Remove buildCssLinks().
* Remove getScriptsForBottomQueue().
* Change where Skin::setupSkinUserCss() is called. This methods lets the skin
add modules to the queue. Previously it was called from buildCssLinks(),
via headElement(), via prepareQuickTemplate(), via OutputPage::output().
It's now in OutputPage::output() directly (slightly earlier). This is needed
because prepareQuickTemplate() calls bottomScripts() before headElement().
And bottomScript() would lazy-initialise the queue and lock it before
setupSkinUserCss() is called from headElement().
This makes execution order more predictable instead of being dependent on
the arbitrary order of data extraction in prepareQuickTemplate (which varies
from one skin to another).
* Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early
on so. This avoids wrongful loading and fixes problem 1.
Effective changes in output:
* mw.loader.state() is now before mw.loader.load(). This fixes problem 2.
* mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading".
* mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1.
* The <script async src> tag for 'startup' changed position (slightly).
Previously it was after all inline scripts and stylesheets. It's still after
all inline scripts and after most stylesheets, but before any user styles.
Since the queue is now formatted outside OutputPage, it can't inject the
meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle
of existing output. This shouldn't have any noticable impact.
Bug: T87871
Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
|
|
|
);
|
2010-09-04 04:00:09 +00:00
|
|
|
}
|
2010-10-02 13:45:35 +00:00
|
|
|
|
2013-11-14 16:54:19 +00:00
|
|
|
/**
|
resourceloader: Move queue formatting out of OutputPage
HTML formatting of the queue was distributed over several OutputPage methods.
Each method demanding a snippet of HTML by calling makeResourceLoaderLink()
with a limited amount of information. As such, makeResourceLoaderLink() was
unable to provide the client with the proper state information.
Centralising it also allows it to better reduce duplication in HTML output
and maintain a more accurate state.
Problems fixed by centralising:
1. The 'user' module is special (due to per-user 'version' and 'user' params).
It is manually requested via script-src. To avoid a separate (and wrong)
request from something that requires it, we set state=loading directly.
However, because the module is in the bottom, the old HTML formatter could
only put state=loading in the bottom also. This sometimes caused a wrong
request to be fired for modules=user if something in the top queue
triggered a requirement for it.
2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose
of allowing dependencies on style modules without risking duplicate loading
on pages where the styles are loaded already. This didn't work, because the
state information about page-style modules is output near the stylesheet,
which is after the script tag with mw.loader.load(). That runs first, and
mw.loader would still make a duplicate request before it learns the state.
Changes:
* Document reasons for style/script tag order in getHeadHtml (per 09537e83).
* Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed
before since a duplicate check in makeResourceLoaderLink() verified the
origin a second time.
* Declare explicit position 'top' on 'user.options' and 'user.tokens' module.
Previously, OutputPage hardcoded them in the top. The new formatter doesn't.
* Remove getHeadScripts().
* Remove getInlineHeadScripts().
* Remove getExternalHeadScripts().
* Remove buildCssLinks().
* Remove getScriptsForBottomQueue().
* Change where Skin::setupSkinUserCss() is called. This methods lets the skin
add modules to the queue. Previously it was called from buildCssLinks(),
via headElement(), via prepareQuickTemplate(), via OutputPage::output().
It's now in OutputPage::output() directly (slightly earlier). This is needed
because prepareQuickTemplate() calls bottomScripts() before headElement().
And bottomScript() would lazy-initialise the queue and lock it before
setupSkinUserCss() is called from headElement().
This makes execution order more predictable instead of being dependent on
the arbitrary order of data extraction in prepareQuickTemplate (which varies
from one skin to another).
* Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early
on so. This avoids wrongful loading and fixes problem 1.
Effective changes in output:
* mw.loader.state() is now before mw.loader.load(). This fixes problem 2.
* mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading".
* mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1.
* The <script async src> tag for 'startup' changed position (slightly).
Previously it was after all inline scripts and stylesheets. It's still after
all inline scripts and after most stylesheets, but before any user styles.
Since the queue is now formatted outside OutputPage, it can't inject the
meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle
of existing output. This shouldn't have any noticable impact.
Bug: T87871
Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
|
|
|
* Combine WrappedString chunks and filter out empty ones
|
|
|
|
|
*
|
|
|
|
|
* @param array $chunks
|
2016-07-07 19:26:36 +00:00
|
|
|
* @return string|WrappedStringList HTML
|
2013-11-14 16:54:19 +00:00
|
|
|
*/
|
resourceloader: Move queue formatting out of OutputPage
HTML formatting of the queue was distributed over several OutputPage methods.
Each method demanding a snippet of HTML by calling makeResourceLoaderLink()
with a limited amount of information. As such, makeResourceLoaderLink() was
unable to provide the client with the proper state information.
Centralising it also allows it to better reduce duplication in HTML output
and maintain a more accurate state.
Problems fixed by centralising:
1. The 'user' module is special (due to per-user 'version' and 'user' params).
It is manually requested via script-src. To avoid a separate (and wrong)
request from something that requires it, we set state=loading directly.
However, because the module is in the bottom, the old HTML formatter could
only put state=loading in the bottom also. This sometimes caused a wrong
request to be fired for modules=user if something in the top queue
triggered a requirement for it.
2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose
of allowing dependencies on style modules without risking duplicate loading
on pages where the styles are loaded already. This didn't work, because the
state information about page-style modules is output near the stylesheet,
which is after the script tag with mw.loader.load(). That runs first, and
mw.loader would still make a duplicate request before it learns the state.
Changes:
* Document reasons for style/script tag order in getHeadHtml (per 09537e83).
* Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed
before since a duplicate check in makeResourceLoaderLink() verified the
origin a second time.
* Declare explicit position 'top' on 'user.options' and 'user.tokens' module.
Previously, OutputPage hardcoded them in the top. The new formatter doesn't.
* Remove getHeadScripts().
* Remove getInlineHeadScripts().
* Remove getExternalHeadScripts().
* Remove buildCssLinks().
* Remove getScriptsForBottomQueue().
* Change where Skin::setupSkinUserCss() is called. This methods lets the skin
add modules to the queue. Previously it was called from buildCssLinks(),
via headElement(), via prepareQuickTemplate(), via OutputPage::output().
It's now in OutputPage::output() directly (slightly earlier). This is needed
because prepareQuickTemplate() calls bottomScripts() before headElement().
And bottomScript() would lazy-initialise the queue and lock it before
setupSkinUserCss() is called from headElement().
This makes execution order more predictable instead of being dependent on
the arbitrary order of data extraction in prepareQuickTemplate (which varies
from one skin to another).
* Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early
on so. This avoids wrongful loading and fixes problem 1.
Effective changes in output:
* mw.loader.state() is now before mw.loader.load(). This fixes problem 2.
* mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading".
* mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1.
* The <script async src> tag for 'startup' changed position (slightly).
Previously it was after all inline scripts and stylesheets. It's still after
all inline scripts and after most stylesheets, but before any user styles.
Since the queue is now formatted outside OutputPage, it can't inject the
meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle
of existing output. This shouldn't have any noticable impact.
Bug: T87871
Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
|
|
|
protected static function combineWrappedStrings( array $chunks ) {
|
2015-07-31 00:13:04 +00:00
|
|
|
// Filter out empty values
|
resourceloader: Move queue formatting out of OutputPage
HTML formatting of the queue was distributed over several OutputPage methods.
Each method demanding a snippet of HTML by calling makeResourceLoaderLink()
with a limited amount of information. As such, makeResourceLoaderLink() was
unable to provide the client with the proper state information.
Centralising it also allows it to better reduce duplication in HTML output
and maintain a more accurate state.
Problems fixed by centralising:
1. The 'user' module is special (due to per-user 'version' and 'user' params).
It is manually requested via script-src. To avoid a separate (and wrong)
request from something that requires it, we set state=loading directly.
However, because the module is in the bottom, the old HTML formatter could
only put state=loading in the bottom also. This sometimes caused a wrong
request to be fired for modules=user if something in the top queue
triggered a requirement for it.
2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose
of allowing dependencies on style modules without risking duplicate loading
on pages where the styles are loaded already. This didn't work, because the
state information about page-style modules is output near the stylesheet,
which is after the script tag with mw.loader.load(). That runs first, and
mw.loader would still make a duplicate request before it learns the state.
Changes:
* Document reasons for style/script tag order in getHeadHtml (per 09537e83).
* Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed
before since a duplicate check in makeResourceLoaderLink() verified the
origin a second time.
* Declare explicit position 'top' on 'user.options' and 'user.tokens' module.
Previously, OutputPage hardcoded them in the top. The new formatter doesn't.
* Remove getHeadScripts().
* Remove getInlineHeadScripts().
* Remove getExternalHeadScripts().
* Remove buildCssLinks().
* Remove getScriptsForBottomQueue().
* Change where Skin::setupSkinUserCss() is called. This methods lets the skin
add modules to the queue. Previously it was called from buildCssLinks(),
via headElement(), via prepareQuickTemplate(), via OutputPage::output().
It's now in OutputPage::output() directly (slightly earlier). This is needed
because prepareQuickTemplate() calls bottomScripts() before headElement().
And bottomScript() would lazy-initialise the queue and lock it before
setupSkinUserCss() is called from headElement().
This makes execution order more predictable instead of being dependent on
the arbitrary order of data extraction in prepareQuickTemplate (which varies
from one skin to another).
* Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early
on so. This avoids wrongful loading and fixes problem 1.
Effective changes in output:
* mw.loader.state() is now before mw.loader.load(). This fixes problem 2.
* mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading".
* mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1.
* The <script async src> tag for 'startup' changed position (slightly).
Previously it was after all inline scripts and stylesheets. It's still after
all inline scripts and after most stylesheets, but before any user styles.
Since the queue is now formatted outside OutputPage, it can't inject the
meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle
of existing output. This shouldn't have any noticable impact.
Bug: T87871
Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
|
|
|
$chunks = array_filter( $chunks, 'strlen' );
|
|
|
|
|
return WrappedString::join( "\n", $chunks );
|
2013-11-14 16:54:19 +00:00
|
|
|
}
|
|
|
|
|
|
2015-08-13 21:30:31 +00:00
|
|
|
/**
|
2018-02-12 21:06:05 +00:00
|
|
|
* JS stuff to put at the bottom of the `<body>`.
|
|
|
|
|
* These are legacy scripts ($this->mScripts), and user JS.
|
2015-08-13 21:30:31 +00:00
|
|
|
*
|
2016-07-07 19:26:36 +00:00
|
|
|
* @return string|WrappedStringList HTML
|
2015-08-13 21:30:31 +00:00
|
|
|
*/
|
resourceloader: Move queue formatting out of OutputPage
HTML formatting of the queue was distributed over several OutputPage methods.
Each method demanding a snippet of HTML by calling makeResourceLoaderLink()
with a limited amount of information. As such, makeResourceLoaderLink() was
unable to provide the client with the proper state information.
Centralising it also allows it to better reduce duplication in HTML output
and maintain a more accurate state.
Problems fixed by centralising:
1. The 'user' module is special (due to per-user 'version' and 'user' params).
It is manually requested via script-src. To avoid a separate (and wrong)
request from something that requires it, we set state=loading directly.
However, because the module is in the bottom, the old HTML formatter could
only put state=loading in the bottom also. This sometimes caused a wrong
request to be fired for modules=user if something in the top queue
triggered a requirement for it.
2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose
of allowing dependencies on style modules without risking duplicate loading
on pages where the styles are loaded already. This didn't work, because the
state information about page-style modules is output near the stylesheet,
which is after the script tag with mw.loader.load(). That runs first, and
mw.loader would still make a duplicate request before it learns the state.
Changes:
* Document reasons for style/script tag order in getHeadHtml (per 09537e83).
* Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed
before since a duplicate check in makeResourceLoaderLink() verified the
origin a second time.
* Declare explicit position 'top' on 'user.options' and 'user.tokens' module.
Previously, OutputPage hardcoded them in the top. The new formatter doesn't.
* Remove getHeadScripts().
* Remove getInlineHeadScripts().
* Remove getExternalHeadScripts().
* Remove buildCssLinks().
* Remove getScriptsForBottomQueue().
* Change where Skin::setupSkinUserCss() is called. This methods lets the skin
add modules to the queue. Previously it was called from buildCssLinks(),
via headElement(), via prepareQuickTemplate(), via OutputPage::output().
It's now in OutputPage::output() directly (slightly earlier). This is needed
because prepareQuickTemplate() calls bottomScripts() before headElement().
And bottomScript() would lazy-initialise the queue and lock it before
setupSkinUserCss() is called from headElement().
This makes execution order more predictable instead of being dependent on
the arbitrary order of data extraction in prepareQuickTemplate (which varies
from one skin to another).
* Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early
on so. This avoids wrongful loading and fixes problem 1.
Effective changes in output:
* mw.loader.state() is now before mw.loader.load(). This fixes problem 2.
* mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading".
* mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1.
* The <script async src> tag for 'startup' changed position (slightly).
Previously it was after all inline scripts and stylesheets. It's still after
all inline scripts and after most stylesheets, but before any user styles.
Since the queue is now formatted outside OutputPage, it can't inject the
meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle
of existing output. This shouldn't have any noticable impact.
Bug: T87871
Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
|
|
|
public function getBottomScripts() {
|
|
|
|
|
$chunks = [];
|
|
|
|
|
$chunks[] = $this->getRlClient()->getBodyHtml();
|
|
|
|
|
|
|
|
|
|
// Legacy non-ResourceLoader scripts
|
|
|
|
|
$chunks[] = $this->mScripts;
|
|
|
|
|
|
2016-11-10 20:29:27 +00:00
|
|
|
if ( $this->limitReportJSData ) {
|
|
|
|
|
$chunks[] = ResourceLoader::makeInlineScript(
|
|
|
|
|
ResourceLoader::makeConfigSetScript(
|
|
|
|
|
[ 'wgPageParseReport' => $this->limitReportJSData ]
|
Initial support for Content Security Policy, disabled by default
The primary goal here is a defense in depth measure to
stop an attacker who found a bug in the parser allowing
them to insert malicious attributes.
This wouldn't stop someone who could insert a full
script tag (since at current it can't distinguish between
malicious and legit user js). It also would not prevent
DOM-based or reflected XSS for anons, as the nonce value
is guessable for anons when receiving a response cached
by varnish. However, the limited protection of just stopping
stored XSS where the attacker only has control of attributes,
is still a big win in my opinion. (But it wouldn't prevent
someone who has that type of xss from abusing things like
data-ooui attribute).
This will likely break many gadgets. Its expected that any
sort of rollout on Wikimedia will be done very slowly, with
lots of testing and the report-only option to begin with.
This is behind feature flags that are off by default, so
merging this patch should not cause any change in default
behaviour.
This may break some extensions (The most obvious one
is charinsert (See fe648d41005), but will probably need
some testing in report-only mode to see if anything else breaks)
This uses the unsafe-eval option of CSP, in order to
support RL's local storage thingy. For better security,
we may want to remove some of the sillier uses of eval
(e.g. jquery.ui.datepicker.js).
For more info, see spec: https://www.w3.org/TR/CSP2/
Additionally see:
https://www.mediawiki.org/wiki/Requests_for_comment/Content-Security-Policy
Bug: T135963
Change-Id: I80f6f469ba4c0b608385483457df96ccb7429ae5
2016-02-29 04:13:10 +00:00
|
|
|
),
|
2019-10-28 05:01:17 +00:00
|
|
|
$this->CSP->getNonce()
|
2016-11-10 20:29:27 +00:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
resourceloader: Move queue formatting out of OutputPage
HTML formatting of the queue was distributed over several OutputPage methods.
Each method demanding a snippet of HTML by calling makeResourceLoaderLink()
with a limited amount of information. As such, makeResourceLoaderLink() was
unable to provide the client with the proper state information.
Centralising it also allows it to better reduce duplication in HTML output
and maintain a more accurate state.
Problems fixed by centralising:
1. The 'user' module is special (due to per-user 'version' and 'user' params).
It is manually requested via script-src. To avoid a separate (and wrong)
request from something that requires it, we set state=loading directly.
However, because the module is in the bottom, the old HTML formatter could
only put state=loading in the bottom also. This sometimes caused a wrong
request to be fired for modules=user if something in the top queue
triggered a requirement for it.
2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose
of allowing dependencies on style modules without risking duplicate loading
on pages where the styles are loaded already. This didn't work, because the
state information about page-style modules is output near the stylesheet,
which is after the script tag with mw.loader.load(). That runs first, and
mw.loader would still make a duplicate request before it learns the state.
Changes:
* Document reasons for style/script tag order in getHeadHtml (per 09537e83).
* Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed
before since a duplicate check in makeResourceLoaderLink() verified the
origin a second time.
* Declare explicit position 'top' on 'user.options' and 'user.tokens' module.
Previously, OutputPage hardcoded them in the top. The new formatter doesn't.
* Remove getHeadScripts().
* Remove getInlineHeadScripts().
* Remove getExternalHeadScripts().
* Remove buildCssLinks().
* Remove getScriptsForBottomQueue().
* Change where Skin::setupSkinUserCss() is called. This methods lets the skin
add modules to the queue. Previously it was called from buildCssLinks(),
via headElement(), via prepareQuickTemplate(), via OutputPage::output().
It's now in OutputPage::output() directly (slightly earlier). This is needed
because prepareQuickTemplate() calls bottomScripts() before headElement().
And bottomScript() would lazy-initialise the queue and lock it before
setupSkinUserCss() is called from headElement().
This makes execution order more predictable instead of being dependent on
the arbitrary order of data extraction in prepareQuickTemplate (which varies
from one skin to another).
* Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early
on so. This avoids wrongful loading and fixes problem 1.
Effective changes in output:
* mw.loader.state() is now before mw.loader.load(). This fixes problem 2.
* mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading".
* mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1.
* The <script async src> tag for 'startup' changed position (slightly).
Previously it was after all inline scripts and stylesheets. It's still after
all inline scripts and after most stylesheets, but before any user styles.
Since the queue is now formatted outside OutputPage, it can't inject the
meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle
of existing output. This shouldn't have any noticable impact.
Bug: T87871
Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
|
|
|
return self::combineWrappedStrings( $chunks );
|
ResourceLoader: Add an experimental option to move the main module loading queue (the bottom queue) from the bottom of the <body> up into the <head> , while still being loaded asynchronously. This makes them load earlier, which should make the page load faster. This is the product of a long discussion on bug 27488
* Added a "blocking" state to mw.loader . When loading scripts while the document is not ready, the loader will use document.write() if blocking is true, and append to the <body> or the <head> if blocking is false. If the document is ready, the loader will always append to the <body>
* Enable blocking mode while loading the top queue, and disable it after. This ensures that modules in the top queue are still loaded in a blocking way as they were before
* If $wgResourceLoaderExperimentalAsyncLoading is true, the bottom queue is also loaded in the head, but with blocking mode disabled. Otherwise, it's loaded at the bottom of the <body> as before
* scripts-only and messages-only requests need special treatment:
** in the top queue, they can continue to use <script src="..."> tags because they are blocking
** if the bottom queue is at the bottom of the <body> (experimental async loading disabled), they can continue to use <script src="..."> tags as before
** if the bottom queue is in the <head> (experimental async loading enabled), they cannot use <script src="..."> tags, because those would block. Instead, call mw.loader.load() on the load.php URL
2012-01-05 23:32:41 +00:00
|
|
|
}
|
|
|
|
|
|
2014-01-19 15:39:46 +00:00
|
|
|
/**
|
|
|
|
|
* Get the javascript config vars to include on this page
|
|
|
|
|
*
|
2014-04-10 18:50:10 +00:00
|
|
|
* @return array Array of javascript config vars
|
2014-01-19 15:39:46 +00:00
|
|
|
* @since 1.23
|
|
|
|
|
*/
|
|
|
|
|
public function getJsConfigVars() {
|
|
|
|
|
return $this->mJsConfigVars;
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-16 18:25:44 +00:00
|
|
|
/**
|
2014-04-10 18:50:10 +00:00
|
|
|
* Add one or more variables to be set in mw.config in JavaScript
|
2011-04-23 19:28:35 +00:00
|
|
|
*
|
2014-04-10 18:50:10 +00:00
|
|
|
* @param string|array $keys Key or array of key/value pairs
|
2018-06-26 21:14:43 +00:00
|
|
|
* @param mixed|null $value [optional] Value of the configuration variable
|
2011-09-28 22:08:08 +00:00
|
|
|
*/
|
2011-12-31 21:25:00 +00:00
|
|
|
public function addJsConfigVars( $keys, $value = null ) {
|
2011-09-28 22:08:08 +00:00
|
|
|
if ( is_array( $keys ) ) {
|
|
|
|
|
foreach ( $keys as $key => $value ) {
|
|
|
|
|
$this->mJsConfigVars[$key] = $value;
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->mJsConfigVars[$keys] = $value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get an array containing the variables to be set in mw.config in JavaScript.
|
|
|
|
|
*
|
2013-02-23 10:42:05 +00:00
|
|
|
* Do not add things here which can be evaluated in ResourceLoaderStartUpModule
|
2011-10-03 14:59:56 +00:00
|
|
|
* - in other words, page-independent/site-wide variables (without state).
|
2011-09-28 22:08:08 +00:00
|
|
|
* You will only be adding bloat to the html page and causing page caches to
|
2011-02-16 18:25:44 +00:00
|
|
|
* have to be purged on configuration changes.
|
2011-11-29 21:04:20 +00:00
|
|
|
* @return array
|
2011-02-16 18:25:44 +00:00
|
|
|
*/
|
2014-12-02 21:48:21 +00:00
|
|
|
public function getJSVars() {
|
2013-09-26 05:03:16 +00:00
|
|
|
$curRevisionId = 0;
|
|
|
|
|
$articleId = 0;
|
2017-02-20 22:44:19 +00:00
|
|
|
$canonicalSpecialPageName = false; # T23115
|
2018-08-18 04:02:39 +00:00
|
|
|
$services = MediaWikiServices::getInstance();
|
2012-01-29 20:35:32 +00:00
|
|
|
|
2011-02-16 18:25:44 +00:00
|
|
|
$title = $this->getTitle();
|
|
|
|
|
$ns = $title->getNamespace();
|
2019-08-26 14:08:10 +00:00
|
|
|
$nsInfo = $services->getNamespaceInfo();
|
2018-08-05 17:58:51 +00:00
|
|
|
$canonicalNamespace = $nsInfo->exists( $ns )
|
|
|
|
|
? $nsInfo->getCanonicalName( $ns )
|
2014-05-11 01:54:15 +00:00
|
|
|
: $title->getNsText();
|
2012-01-29 20:35:32 +00:00
|
|
|
|
2013-10-29 20:49:17 +00:00
|
|
|
$sk = $this->getSkin();
|
2012-03-13 17:57:54 +00:00
|
|
|
// Get the relevant title so that AJAX features can use the correct page name
|
2017-02-20 22:44:19 +00:00
|
|
|
// when making API requests from certain special pages (T36972).
|
2013-10-29 20:49:17 +00:00
|
|
|
$relevantTitle = $sk->getRelevantTitle();
|
|
|
|
|
$relevantUser = $sk->getRelevantUser();
|
2012-03-13 17:57:54 +00:00
|
|
|
|
2011-02-25 11:48:14 +00:00
|
|
|
if ( $ns == NS_SPECIAL ) {
|
2014-05-11 01:54:15 +00:00
|
|
|
list( $canonicalSpecialPageName, /*...*/ ) =
|
2018-08-18 04:02:39 +00:00
|
|
|
$services->getSpecialPageFactory()->
|
2018-08-12 09:08:58 +00:00
|
|
|
resolveAlias( $title->getDBkey() );
|
2012-01-29 20:35:32 +00:00
|
|
|
} elseif ( $this->canUseWikiPage() ) {
|
|
|
|
|
$wikiPage = $this->getWikiPage();
|
2013-09-26 05:03:16 +00:00
|
|
|
$curRevisionId = $wikiPage->getLatest();
|
|
|
|
|
$articleId = $wikiPage->getId();
|
2011-02-25 11:48:14 +00:00
|
|
|
}
|
2011-02-16 18:25:44 +00:00
|
|
|
|
2016-03-08 23:44:18 +00:00
|
|
|
$lang = $title->getPageViewLanguage();
|
2011-11-28 20:53:26 +00:00
|
|
|
|
|
|
|
|
// Pre-process information
|
|
|
|
|
$separatorTransTable = $lang->separatorTransformTable();
|
2018-06-11 09:16:48 +00:00
|
|
|
$separatorTransTable = $separatorTransTable ?: [];
|
2016-02-17 09:09:32 +00:00
|
|
|
$compactSeparatorTransTable = [
|
2011-11-28 20:53:26 +00:00
|
|
|
implode( "\t", array_keys( $separatorTransTable ) ),
|
|
|
|
|
implode( "\t", $separatorTransTable ),
|
2016-02-17 09:09:32 +00:00
|
|
|
];
|
2011-11-28 20:53:26 +00:00
|
|
|
$digitTransTable = $lang->digitTransformTable();
|
2018-06-11 09:16:48 +00:00
|
|
|
$digitTransTable = $digitTransTable ?: [];
|
2016-02-17 09:09:32 +00:00
|
|
|
$compactDigitTransTable = [
|
2011-11-28 20:53:26 +00:00
|
|
|
implode( "\t", array_keys( $digitTransTable ) ),
|
|
|
|
|
implode( "\t", $digitTransTable ),
|
2016-02-17 09:09:32 +00:00
|
|
|
];
|
2011-11-28 20:53:26 +00:00
|
|
|
|
2013-01-22 22:44:22 +00:00
|
|
|
$user = $this->getUser();
|
|
|
|
|
|
2019-10-12 00:18:43 +00:00
|
|
|
// Internal variables for MediaWiki core
|
2016-02-17 09:09:32 +00:00
|
|
|
$vars = [
|
2019-10-12 00:18:43 +00:00
|
|
|
// @internal For mediawiki.page.startup
|
|
|
|
|
'wgBreakFrames' => $this->getFrameOptions() == 'DENY',
|
|
|
|
|
|
|
|
|
|
// @internal For jquery.tablesorter
|
|
|
|
|
'wgSeparatorTransformTable' => $compactSeparatorTransTable,
|
|
|
|
|
'wgDigitTransformTable' => $compactDigitTransTable,
|
|
|
|
|
'wgDefaultDateFormat' => $lang->getDefaultDateFormat(),
|
|
|
|
|
'wgMonthNames' => $lang->getMonthNamesArray(),
|
|
|
|
|
|
|
|
|
|
// @internal For debugging purposes
|
|
|
|
|
'wgRequestId' => WebRequest::getRequestId(),
|
|
|
|
|
|
|
|
|
|
// @internal For mw.loader
|
|
|
|
|
'wgCSPNonce' => $this->CSP->getNonce(),
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
// Start of supported and stable config vars (for use by extensions/gadgets).
|
|
|
|
|
$vars += [
|
2013-09-26 05:03:16 +00:00
|
|
|
'wgCanonicalNamespace' => $canonicalNamespace,
|
|
|
|
|
'wgCanonicalSpecialPageName' => $canonicalSpecialPageName,
|
2011-02-16 18:25:44 +00:00
|
|
|
'wgNamespaceNumber' => $title->getNamespace(),
|
2013-03-09 20:14:22 +00:00
|
|
|
'wgPageName' => $title->getPrefixedDBkey(),
|
2011-02-16 18:25:44 +00:00
|
|
|
'wgTitle' => $title->getText(),
|
2013-09-26 05:03:16 +00:00
|
|
|
'wgCurRevisionId' => $curRevisionId,
|
2013-08-26 18:52:23 +00:00
|
|
|
'wgRevisionId' => (int)$this->getRevisionId(),
|
2013-09-26 05:03:16 +00:00
|
|
|
'wgArticleId' => $articleId,
|
2011-02-16 18:25:44 +00:00
|
|
|
'wgIsArticle' => $this->isArticle(),
|
2013-07-23 19:11:17 +00:00
|
|
|
'wgIsRedirect' => $title->isRedirect(),
|
2012-01-21 06:57:34 +00:00
|
|
|
'wgAction' => Action::getActionName( $this->getContext() ),
|
2013-01-22 22:44:22 +00:00
|
|
|
'wgUserName' => $user->isAnon() ? null : $user->getName(),
|
|
|
|
|
'wgUserGroups' => $user->getEffectiveGroups(),
|
2011-02-16 18:25:44 +00:00
|
|
|
'wgCategories' => $this->getCategories(),
|
2011-11-28 20:53:26 +00:00
|
|
|
'wgPageContentLanguage' => $lang->getCode(),
|
2013-05-07 19:59:40 +00:00
|
|
|
'wgPageContentModel' => $title->getContentModel(),
|
2013-03-09 20:14:22 +00:00
|
|
|
'wgRelevantPageName' => $relevantTitle->getPrefixedDBkey(),
|
2016-03-19 00:08:06 +00:00
|
|
|
'wgRelevantArticleId' => $relevantTitle->getArticleID(),
|
2016-02-17 09:09:32 +00:00
|
|
|
];
|
2013-01-22 22:44:22 +00:00
|
|
|
if ( $user->isLoggedIn() ) {
|
|
|
|
|
$vars['wgUserId'] = $user->getId();
|
|
|
|
|
$vars['wgUserEditCount'] = $user->getEditCount();
|
2016-09-23 23:59:56 +00:00
|
|
|
$userReg = $user->getRegistration();
|
2019-09-15 14:40:42 +00:00
|
|
|
$vars['wgUserRegistration'] = $userReg ? (int)wfTimestamp( TS_UNIX, $userReg ) * 1000 : null;
|
2013-05-05 01:08:58 +00:00
|
|
|
// Get the revision ID of the oldest new message on the user's talk
|
|
|
|
|
// page. This can be used for constructing new message alerts on
|
|
|
|
|
// the client side.
|
2020-03-13 22:26:16 +00:00
|
|
|
$userNewMsgRevId = $user->getNewMessageRevisionId();
|
|
|
|
|
// Only occupy precious space in the <head> when it is non-null (T53640)
|
|
|
|
|
// mw.config.get returns null by default.
|
|
|
|
|
if ( $userNewMsgRevId ) {
|
2020-03-19 03:12:48 +00:00
|
|
|
$vars['wgUserNewMsgRevisionId'] = $userNewMsgRevId;
|
2020-03-13 22:26:16 +00:00
|
|
|
}
|
2013-01-22 22:44:22 +00:00
|
|
|
}
|
2018-08-18 04:02:39 +00:00
|
|
|
$contLang = $services->getContentLanguage();
|
2018-07-29 12:24:54 +00:00
|
|
|
if ( $contLang->hasVariants() ) {
|
|
|
|
|
$vars['wgUserVariant'] = $contLang->getPreferredVariant();
|
2012-10-26 15:42:13 +00:00
|
|
|
}
|
2013-05-23 04:31:11 +00:00
|
|
|
// Same test as SkinTemplate
|
2019-08-23 23:53:15 +00:00
|
|
|
$vars['wgIsProbablyEditable'] = $this->userCanEditOrCreate( $user, $title );
|
|
|
|
|
$vars['wgRelevantPageIsProbablyEditable'] = $relevantTitle &&
|
|
|
|
|
$this->userCanEditOrCreate( $user, $relevantTitle );
|
2011-02-16 18:25:44 +00:00
|
|
|
foreach ( $title->getRestrictionTypes() as $type ) {
|
2017-12-27 02:04:45 +00:00
|
|
|
// Following keys are set in $vars:
|
|
|
|
|
// wgRestrictionCreate, wgRestrictionEdit, wgRestrictionMove, wgRestrictionUpload
|
2011-02-16 18:25:44 +00:00
|
|
|
$vars['wgRestriction' . ucfirst( $type )] = $title->getRestrictions( $type );
|
|
|
|
|
}
|
2011-06-14 23:29:45 +00:00
|
|
|
if ( $title->isMainPage() ) {
|
2011-06-15 01:11:16 +00:00
|
|
|
$vars['wgIsMainPage'] = true;
|
2011-11-30 12:43:10 +00:00
|
|
|
}
|
2019-10-12 00:18:43 +00:00
|
|
|
if ( $relevantUser ) {
|
|
|
|
|
$vars['wgRelevantUserName'] = $relevantUser->getName();
|
|
|
|
|
}
|
|
|
|
|
// End of stable config vars
|
2014-05-11 01:54:15 +00:00
|
|
|
|
2011-11-30 12:43:10 +00:00
|
|
|
if ( $this->mRedirectedFrom ) {
|
2019-10-12 00:18:43 +00:00
|
|
|
// @internal For skin JS
|
2013-03-09 20:14:22 +00:00
|
|
|
$vars['wgRedirectedFrom'] = $this->mRedirectedFrom->getPrefixedDBkey();
|
2011-06-14 23:29:45 +00:00
|
|
|
}
|
2014-05-11 01:54:15 +00:00
|
|
|
|
2011-09-28 22:08:08 +00:00
|
|
|
// 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).
|
|
|
|
|
// Alternatively, you may want to use OutputPage->addJsConfigVars() instead.
|
2016-02-17 09:09:32 +00:00
|
|
|
Hooks::run( 'MakeGlobalVariablesScript', [ &$vars, $this ] );
|
2011-04-23 19:28:35 +00:00
|
|
|
|
2011-09-28 22:08:08 +00:00
|
|
|
// Merge in variables from addJsConfigVars last
|
2014-01-19 15:39:46 +00:00
|
|
|
return array_merge( $vars, $this->getJsConfigVars() );
|
2011-02-16 18:25:44 +00:00
|
|
|
}
|
|
|
|
|
|
2010-01-29 21:32:45 +00:00
|
|
|
/**
|
2011-07-22 10:45:07 +00:00
|
|
|
* To make it harder for someone to slip a user a fake
|
2017-02-28 20:52:17 +00:00
|
|
|
* JavaScript or CSS preview, a random token
|
2011-07-22 10:45:07 +00:00
|
|
|
* is associated with the login session. If it's not
|
|
|
|
|
* passed back with the preview request, we won't render
|
|
|
|
|
* the code.
|
|
|
|
|
*
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
public function userCanPreview() {
|
2015-10-08 06:04:59 +00:00
|
|
|
$request = $this->getRequest();
|
2015-11-15 01:28:25 +00:00
|
|
|
if (
|
|
|
|
|
$request->getVal( 'action' ) !== 'submit' ||
|
|
|
|
|
!$request->wasPosted()
|
|
|
|
|
) {
|
2015-10-08 06:04:59 +00:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$user = $this->getUser();
|
2016-08-19 20:53:52 +00:00
|
|
|
|
2016-08-23 07:12:35 +00:00
|
|
|
if ( !$user->isLoggedIn() ) {
|
2016-08-19 20:53:52 +00:00
|
|
|
// Anons have predictable edit tokens
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2015-10-08 06:04:59 +00:00
|
|
|
if ( !$user->matchEditToken( $request->getVal( 'wpEditToken' ) ) ) {
|
2011-07-22 10:45:07 +00:00
|
|
|
return false;
|
|
|
|
|
}
|
2015-10-08 06:04:59 +00:00
|
|
|
|
|
|
|
|
$title = $this->getTitle();
|
2020-02-20 09:45:13 +00:00
|
|
|
$errors = MediaWikiServices::getInstance()->getPermissionManager()
|
|
|
|
|
->getPermissionErrors( 'edit', $user, $title );
|
2015-10-08 06:04:59 +00:00
|
|
|
if ( count( $errors ) !== 0 ) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
2011-07-22 10:45:07 +00:00
|
|
|
}
|
|
|
|
|
|
2019-08-23 23:53:15 +00:00
|
|
|
/**
|
|
|
|
|
* @param User $user
|
|
|
|
|
* @param LinkTarget $title
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
private function userCanEditOrCreate(
|
|
|
|
|
User $user,
|
|
|
|
|
LinkTarget $title
|
|
|
|
|
) {
|
|
|
|
|
$pm = MediaWikiServices::getInstance()->getPermissionManager();
|
|
|
|
|
return $pm->quickUserCan( 'edit', $user, $title )
|
|
|
|
|
&& ( $this->getTitle()->exists() ||
|
|
|
|
|
$pm->quickUserCan( 'create', $user, $title ) );
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-22 10:45:07 +00:00
|
|
|
/**
|
2014-07-24 17:42:24 +00:00
|
|
|
* @return array Array in format "link name or number => 'link html'".
|
2010-01-29 21:32:45 +00:00
|
|
|
*/
|
2013-05-11 09:31:30 +00:00
|
|
|
public function getHeadLinksArray() {
|
2016-02-17 09:09:32 +00:00
|
|
|
$tags = [];
|
2014-08-23 08:52:41 +00:00
|
|
|
$config = $this->getConfig();
|
2009-07-07 21:49:45 +00:00
|
|
|
|
2012-12-31 01:11:43 +00:00
|
|
|
$canonicalUrl = $this->mCanonicalUrl;
|
|
|
|
|
|
2016-02-17 09:09:32 +00:00
|
|
|
$tags['meta-generator'] = Html::element( 'meta', [
|
2011-04-02 18:38:42 +00:00
|
|
|
'name' => 'generator',
|
2020-02-25 01:33:18 +00:00
|
|
|
'content' => 'MediaWiki ' . MW_VERSION,
|
2016-02-17 09:09:32 +00:00
|
|
|
] );
|
here it is ... the upload-api, script-server, js2 (javascript phase2) branch merge 1st attempt.
Here is a short overview of changes and associated default configuration variables (most everything is off by default) also see ~soon to be updated~: http://www.mediawiki.org/wiki/Media_Projects_Overview
= Upload Improvements =
==Upload API ==
* Based on the early work of Bryan Tong and others it adds the upload option to the api.
* We rewrite Special:Upload page to include use the new refactoring
* Added in token checks in both the SpecialUpload.php page so avoids DOS / xss copy-by-url JavaScript based cross site POST file submissions
== Copy by URL==
$wgAllowCopyUploads = false;
* http class rewrite includes a new http background download see: includes/HttpFunctions.php
* spins off a php process that calls: maintenance/http_session_download.php
* pushes updates to the session and gives the user a progress bar on http copy uploads from other server progress (using js2 upload interface) (if not using the js2 upload interface it does the request in-place but the download is limited to the php ini timeout time)
== Firefogg ==
* Firefogg enables resumable upload by chunks
* progress indicators and conditional invokation (js2 system)
* and of-course client side transcoding.
= Script Server =
$wgEnableScriptLoader = false;
* off by default if $wgEnableScriptLoader is turned on script files are grouped, gziped, cached etc.
for more info see: http://www.mediawiki.org/wiki/Extension:ScriptLoader
* Includes some early skin js include fixes (skin/script system still lots of love)
* Includes a "javascript class autoloader" this is packaged into mwEmbed so that the mwEmbed library can work in stand alone mode (while retaining localization and script serving) (one such application is the make page for firefogg.org : http://www.firefogg.org/make/index.html )
* The file that contains the autojavascript loading classes is: js2/php/jsAutoloadLocalClasses.php
* One can use this auto class loading dependency system with extensions and add-ons but I need to better document that.
= js2 system / mwEmbed=
$wgEnableJS2system = false
* includes initial rewrite towards more jquery based javascript code
* especially for the Special:Upload page.
* Also the edit page include support for the "add-media-wizard"
* includes dependency loader for javascript that optionally takes advantage of the script-loader
* remote embedding of javascript interfaces (like embedding video, or commons media searching)
* $wgDebugJavaScript = false; .. .this variable lets you always get "always fresh javascript". When used with the script-loader it does not minify the script-loader output.
= mwEmbed =
* Will commit a separate patch to oggHandler that conditionally outputs <video tag> to use the new javascript video player.
** mv_embed player includes: play-head, volume control, remote embedding, oggz-chop support across plugins.
* add-media-wizard adds easy inserts of media to pages (with import)
== jQuery==
* we include a base install of jQuery, jQuery ui and some plugins.
* all the javascript classes are in the scriptloader so its easy to load any set of jquery ui components that you may need using the script-server. You get a callback so you can then execute js with dependencies loaded.
== other stuff ==
there is a bit more code in js2 that pertains to sequence editing, timed text display and basic image editing. We include a base import of pixastic-lib & pixastic-editor... will work with the pixastic developer to try and ensure upstream compatibility on our usage of the library for in-browser photo and sequence manipulation.
2009-07-14 23:52:14 +00:00
|
|
|
|
2015-01-20 23:41:41 +00:00
|
|
|
if ( $config->get( 'ReferrerPolicy' ) !== false ) {
|
2017-12-23 17:35:59 +00:00
|
|
|
// Per https://w3c.github.io/webappsec-referrer-policy/#unknown-policy-values
|
|
|
|
|
// fallbacks should come before the primary value so we need to reverse the array.
|
|
|
|
|
foreach ( array_reverse( (array)$config->get( 'ReferrerPolicy' ) ) as $i => $policy ) {
|
|
|
|
|
$tags["meta-referrer-$i"] = Html::element( 'meta', [
|
|
|
|
|
'name' => 'referrer',
|
|
|
|
|
'content' => $policy,
|
|
|
|
|
] );
|
|
|
|
|
}
|
2015-01-20 23:41:41 +00:00
|
|
|
}
|
|
|
|
|
|
2008-07-23 19:05:43 +00:00
|
|
|
$p = "{$this->mIndexPolicy},{$this->mFollowPolicy}";
|
2013-04-17 14:52:47 +00:00
|
|
|
if ( $p !== 'index,follow' ) {
|
2008-07-02 22:52:22 +00:00
|
|
|
// http://www.robotstxt.org/wc/meta-user.html
|
|
|
|
|
// Only show if it's different from the default robots policy
|
2016-02-17 09:09:32 +00:00
|
|
|
$tags['meta-robots'] = Html::element( 'meta', [
|
2011-04-02 18:38:42 +00:00
|
|
|
'name' => 'robots',
|
|
|
|
|
'content' => $p,
|
2016-02-17 09:09:32 +00:00
|
|
|
] );
|
2008-07-02 22:52:22 +00:00
|
|
|
}
|
|
|
|
|
|
2003-04-14 23:10:40 +00:00
|
|
|
foreach ( $this->mMetatags as $tag ) {
|
2017-01-24 12:01:47 +00:00
|
|
|
if ( strncasecmp( $tag[0], 'http:', 5 ) === 0 ) {
|
2012-02-20 22:48:43 +00:00
|
|
|
$a = 'http-equiv';
|
|
|
|
|
$tag[0] = substr( $tag[0], 5 );
|
2017-01-24 12:01:47 +00:00
|
|
|
} elseif ( strncasecmp( $tag[0], 'og:', 3 ) === 0 ) {
|
|
|
|
|
$a = 'property';
|
2012-02-20 22:48:43 +00:00
|
|
|
} else {
|
|
|
|
|
$a = 'name';
|
2003-04-14 23:10:40 +00:00
|
|
|
}
|
2012-04-24 17:25:06 +00:00
|
|
|
$tagName = "meta-{$tag[0]}";
|
|
|
|
|
if ( isset( $tags[$tagName] ) ) {
|
|
|
|
|
$tagName .= $tag[1];
|
|
|
|
|
}
|
|
|
|
|
$tags[$tagName] = Html::element( 'meta',
|
2016-02-17 09:09:32 +00:00
|
|
|
[
|
2008-07-02 22:52:22 +00:00
|
|
|
$a => $tag[0],
|
2010-05-22 12:18:22 +00:00
|
|
|
'content' => $tag[1]
|
2016-02-17 09:09:32 +00:00
|
|
|
]
|
2010-05-22 12:18:22 +00:00
|
|
|
);
|
2003-04-14 23:10:40 +00:00
|
|
|
}
|
2011-04-02 18:38:42 +00:00
|
|
|
|
2003-04-14 23:10:40 +00:00
|
|
|
foreach ( $this->mLinktags as $tag ) {
|
2009-09-18 20:10:25 +00:00
|
|
|
$tags[] = Html::element( 'link', $tag );
|
2003-04-14 23:10:40 +00:00
|
|
|
}
|
2008-04-14 07:45:50 +00:00
|
|
|
|
2011-04-02 18:38:42 +00:00
|
|
|
# Universal edit button
|
2014-08-23 08:52:41 +00:00
|
|
|
if ( $config->get( 'UniversalEditButton' ) && $this->isArticleRelated() ) {
|
2019-08-23 23:53:15 +00:00
|
|
|
if ( $this->userCanEditOrCreate( $this->getUser(), $this->getTitle() ) ) {
|
2011-04-02 18:38:42 +00:00
|
|
|
// Original UniversalEditButton
|
2011-10-23 08:13:52 +00:00
|
|
|
$msg = $this->msg( 'edit' )->text();
|
2016-02-17 09:09:32 +00:00
|
|
|
$tags['universal-edit-button'] = Html::element( 'link', [
|
2011-04-02 18:38:42 +00:00
|
|
|
'rel' => 'alternate',
|
|
|
|
|
'type' => 'application/x-wiki',
|
|
|
|
|
'title' => $msg,
|
2014-05-18 20:54:46 +00:00
|
|
|
'href' => $this->getTitle()->getEditURL(),
|
2016-02-17 09:09:32 +00:00
|
|
|
] );
|
2011-04-02 18:38:42 +00:00
|
|
|
// Alternate edit link
|
2016-02-17 09:09:32 +00:00
|
|
|
$tags['alternative-edit'] = Html::element( 'link', [
|
2011-04-02 18:38:42 +00:00
|
|
|
'rel' => 'edit',
|
|
|
|
|
'title' => $msg,
|
2014-05-18 20:54:46 +00:00
|
|
|
'href' => $this->getTitle()->getEditURL(),
|
2016-02-17 09:09:32 +00:00
|
|
|
] );
|
2011-04-02 18:38:42 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Generally the order of the favicon and apple-touch-icon links
|
|
|
|
|
# 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.
|
2014-08-23 08:52:41 +00:00
|
|
|
if ( $config->get( 'AppleTouchIcon' ) !== false ) {
|
2016-02-17 09:09:32 +00:00
|
|
|
$tags['apple-touch-icon'] = Html::element( 'link', [
|
2014-05-11 01:54:15 +00:00
|
|
|
'rel' => 'apple-touch-icon',
|
2014-08-23 08:52:41 +00:00
|
|
|
'href' => $config->get( 'AppleTouchIcon' )
|
2016-02-17 09:09:32 +00:00
|
|
|
] );
|
2011-04-02 18:38:42 +00:00
|
|
|
}
|
|
|
|
|
|
2014-08-23 08:52:41 +00:00
|
|
|
if ( $config->get( 'Favicon' ) !== false ) {
|
2016-02-17 09:09:32 +00:00
|
|
|
$tags['favicon'] = Html::element( 'link', [
|
2014-05-11 01:54:15 +00:00
|
|
|
'rel' => 'shortcut icon',
|
2014-08-23 08:52:41 +00:00
|
|
|
'href' => $config->get( 'Favicon' )
|
2016-02-17 09:09:32 +00:00
|
|
|
] );
|
2011-04-02 18:38:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# OpenSearch description link
|
2016-02-17 09:09:32 +00:00
|
|
|
$tags['opensearch'] = Html::element( 'link', [
|
2011-04-02 18:38:42 +00:00
|
|
|
'rel' => 'search',
|
|
|
|
|
'type' => 'application/opensearchdescription+xml',
|
|
|
|
|
'href' => wfScript( 'opensearch_desc' ),
|
2011-10-23 08:13:52 +00:00
|
|
|
'title' => $this->msg( 'opensearch-desc' )->inContentLanguage()->text(),
|
2016-02-17 09:09:32 +00:00
|
|
|
] );
|
2011-04-02 18:38:42 +00:00
|
|
|
|
2017-11-20 23:50:22 +00:00
|
|
|
# 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
|
|
|
|
|
# blogging extension, etc)
|
|
|
|
|
$tags['rsd'] = Html::element( 'link', [
|
|
|
|
|
'rel' => 'EditURI',
|
|
|
|
|
'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' ),
|
|
|
|
|
[ 'action' => 'rsd' ] ),
|
|
|
|
|
PROTO_RELATIVE
|
|
|
|
|
),
|
|
|
|
|
] );
|
2011-04-02 18:38:42 +00:00
|
|
|
|
|
|
|
|
# Language variants
|
2014-08-15 08:08:46 +00:00
|
|
|
if ( !$config->get( 'DisableLangConversion' ) ) {
|
2011-11-05 20:25:00 +00:00
|
|
|
$lang = $this->getTitle()->getPageLanguage();
|
|
|
|
|
if ( $lang->hasVariants() ) {
|
2014-08-15 08:08:46 +00:00
|
|
|
$variants = $lang->getVariants();
|
2015-07-23 10:53:31 +00:00
|
|
|
foreach ( $variants as $variant ) {
|
2016-02-17 09:09:32 +00:00
|
|
|
$tags["variant-$variant"] = Html::element( 'link', [
|
2014-08-15 08:08:46 +00:00
|
|
|
'rel' => 'alternate',
|
2017-04-06 15:17:19 +00:00
|
|
|
'hreflang' => LanguageCode::bcp47( $variant ),
|
2015-09-27 07:58:17 +00:00
|
|
|
'href' => $this->getTitle()->getLocalURL(
|
2016-02-17 09:09:32 +00:00
|
|
|
[ 'variant' => $variant ] )
|
|
|
|
|
]
|
2014-08-15 08:08:46 +00:00
|
|
|
);
|
2011-04-02 18:38:42 +00:00
|
|
|
}
|
2015-07-07 10:32:03 +00:00
|
|
|
# x-default link per https://support.google.com/webmasters/answer/189077?hl=en
|
2016-02-17 09:09:32 +00:00
|
|
|
$tags["variant-x-default"] = Html::element( 'link', [
|
2015-07-07 10:32:03 +00:00
|
|
|
'rel' => 'alternate',
|
|
|
|
|
'hreflang' => 'x-default',
|
2016-02-17 09:09:32 +00:00
|
|
|
'href' => $this->getTitle()->getLocalURL() ] );
|
2011-04-02 18:38:42 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Copyright
|
2015-06-08 13:39:06 +00:00
|
|
|
if ( $this->copyrightUrl !== null ) {
|
|
|
|
|
$copyright = $this->copyrightUrl;
|
|
|
|
|
} else {
|
|
|
|
|
$copyright = '';
|
|
|
|
|
if ( $config->get( 'RightsPage' ) ) {
|
|
|
|
|
$copy = Title::newFromText( $config->get( 'RightsPage' ) );
|
2011-04-02 18:38:42 +00:00
|
|
|
|
2015-06-08 13:39:06 +00:00
|
|
|
if ( $copy ) {
|
|
|
|
|
$copyright = $copy->getLocalURL();
|
|
|
|
|
}
|
2011-04-02 18:38:42 +00:00
|
|
|
}
|
|
|
|
|
|
2015-06-08 13:39:06 +00:00
|
|
|
if ( !$copyright && $config->get( 'RightsUrl' ) ) {
|
|
|
|
|
$copyright = $config->get( 'RightsUrl' );
|
|
|
|
|
}
|
2011-04-02 18:38:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( $copyright ) {
|
2016-02-17 09:09:32 +00:00
|
|
|
$tags['copyright'] = Html::element( 'link', [
|
2017-06-07 12:11:08 +00:00
|
|
|
'rel' => 'license',
|
2016-02-17 09:09:32 +00:00
|
|
|
'href' => $copyright ]
|
2011-04-02 18:38:42 +00:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Feeds
|
2014-08-23 08:52:41 +00:00
|
|
|
if ( $config->get( 'Feed' ) ) {
|
2016-02-17 09:09:32 +00:00
|
|
|
$feedLinks = [];
|
2015-12-18 19:09:34 +00:00
|
|
|
|
2013-04-17 14:52:47 +00:00
|
|
|
foreach ( $this->getSyndicationLinks() as $format => $link ) {
|
2011-05-26 16:11:47 +00:00
|
|
|
# Use the page name for the title. In principle, this could
|
|
|
|
|
# lead to issues with having the same name for different feeds
|
|
|
|
|
# corresponding to the same page, but we can't avoid that at
|
|
|
|
|
# this low a level.
|
2008-04-14 07:45:50 +00:00
|
|
|
|
2015-12-18 19:09:34 +00:00
|
|
|
$feedLinks[] = $this->feedLink(
|
2008-02-22 12:33:51 +00:00
|
|
|
$format,
|
|
|
|
|
$link,
|
2010-01-13 21:29:47 +00:00
|
|
|
# Used messages: 'page-rss-feed' and 'page-atom-feed' (for an easier grep)
|
2015-09-27 07:58:17 +00:00
|
|
|
$this->msg(
|
|
|
|
|
"page-{$format}-feed", $this->getTitle()->getPrefixedText()
|
|
|
|
|
)->text()
|
2010-05-22 12:18:22 +00:00
|
|
|
);
|
2008-02-22 12:33:51 +00:00
|
|
|
}
|
2008-04-14 07:45:50 +00:00
|
|
|
|
here it is ... the upload-api, script-server, js2 (javascript phase2) branch merge 1st attempt.
Here is a short overview of changes and associated default configuration variables (most everything is off by default) also see ~soon to be updated~: http://www.mediawiki.org/wiki/Media_Projects_Overview
= Upload Improvements =
==Upload API ==
* Based on the early work of Bryan Tong and others it adds the upload option to the api.
* We rewrite Special:Upload page to include use the new refactoring
* Added in token checks in both the SpecialUpload.php page so avoids DOS / xss copy-by-url JavaScript based cross site POST file submissions
== Copy by URL==
$wgAllowCopyUploads = false;
* http class rewrite includes a new http background download see: includes/HttpFunctions.php
* spins off a php process that calls: maintenance/http_session_download.php
* pushes updates to the session and gives the user a progress bar on http copy uploads from other server progress (using js2 upload interface) (if not using the js2 upload interface it does the request in-place but the download is limited to the php ini timeout time)
== Firefogg ==
* Firefogg enables resumable upload by chunks
* progress indicators and conditional invokation (js2 system)
* and of-course client side transcoding.
= Script Server =
$wgEnableScriptLoader = false;
* off by default if $wgEnableScriptLoader is turned on script files are grouped, gziped, cached etc.
for more info see: http://www.mediawiki.org/wiki/Extension:ScriptLoader
* Includes some early skin js include fixes (skin/script system still lots of love)
* Includes a "javascript class autoloader" this is packaged into mwEmbed so that the mwEmbed library can work in stand alone mode (while retaining localization and script serving) (one such application is the make page for firefogg.org : http://www.firefogg.org/make/index.html )
* The file that contains the autojavascript loading classes is: js2/php/jsAutoloadLocalClasses.php
* One can use this auto class loading dependency system with extensions and add-ons but I need to better document that.
= js2 system / mwEmbed=
$wgEnableJS2system = false
* includes initial rewrite towards more jquery based javascript code
* especially for the Special:Upload page.
* Also the edit page include support for the "add-media-wizard"
* includes dependency loader for javascript that optionally takes advantage of the script-loader
* remote embedding of javascript interfaces (like embedding video, or commons media searching)
* $wgDebugJavaScript = false; .. .this variable lets you always get "always fresh javascript". When used with the script-loader it does not minify the script-loader output.
= mwEmbed =
* Will commit a separate patch to oggHandler that conditionally outputs <video tag> to use the new javascript video player.
** mv_embed player includes: play-head, volume control, remote embedding, oggz-chop support across plugins.
* add-media-wizard adds easy inserts of media to pages (with import)
== jQuery==
* we include a base install of jQuery, jQuery ui and some plugins.
* all the javascript classes are in the scriptloader so its easy to load any set of jquery ui components that you may need using the script-server. You get a callback so you can then execute js with dependencies loaded.
== other stuff ==
there is a bit more code in js2 that pertains to sequence editing, timed text display and basic image editing. We include a base import of pixastic-lib & pixastic-editor... will work with the pixastic developer to try and ensure upstream compatibility on our usage of the library for in-browser photo and sequence manipulation.
2009-07-14 23:52:14 +00:00
|
|
|
# Recent changes feed should appear on every page (except recentchanges,
|
|
|
|
|
# that would be redundant). Put it after the per-page feed to avoid
|
|
|
|
|
# changing existing behavior. It's still available, probably via a
|
2008-08-08 01:41:14 +00:00
|
|
|
# menu in your browser. Some sites might have a different feed they'd
|
|
|
|
|
# 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.
|
2014-08-23 08:52:41 +00:00
|
|
|
$sitename = $config->get( 'Sitename' );
|
2018-01-28 02:21:51 +00:00
|
|
|
$overrideSiteFeed = $config->get( 'OverrideSiteFeed' );
|
|
|
|
|
if ( $overrideSiteFeed ) {
|
|
|
|
|
foreach ( $overrideSiteFeed as $type => $feedUrl ) {
|
2011-09-07 19:14:06 +00:00
|
|
|
// Note, this->feedLink escapes the url.
|
2015-12-18 19:09:34 +00:00
|
|
|
$feedLinks[] = $this->feedLink(
|
2008-08-08 01:41:14 +00:00
|
|
|
$type,
|
2011-09-07 19:14:06 +00:00
|
|
|
$feedUrl,
|
2014-08-23 08:52:41 +00:00
|
|
|
$this->msg( "site-{$type}-feed", $sitename )->text()
|
2010-05-22 12:18:22 +00:00
|
|
|
);
|
2008-08-08 01:41:14 +00:00
|
|
|
}
|
2011-11-05 20:25:00 +00:00
|
|
|
} elseif ( !$this->getTitle()->isSpecial( 'Recentchanges' ) ) {
|
2011-12-20 09:12:20 +00:00
|
|
|
$rctitle = SpecialPage::getTitleFor( 'Recentchanges' );
|
2018-01-28 02:21:51 +00:00
|
|
|
foreach ( $this->getAdvertisedFeedTypes() as $format ) {
|
2015-12-18 19:09:34 +00:00
|
|
|
$feedLinks[] = $this->feedLink(
|
2008-09-16 03:58:18 +00:00
|
|
|
$format,
|
2016-02-17 09:09:32 +00:00
|
|
|
$rctitle->getLocalURL( [ 'feed' => $format ] ),
|
2014-05-11 01:54:15 +00:00
|
|
|
# For grep: 'site-rss-feed', 'site-atom-feed'
|
2014-08-23 08:52:41 +00:00
|
|
|
$this->msg( "site-{$format}-feed", $sitename )->text()
|
2010-05-22 12:18:22 +00:00
|
|
|
);
|
2008-09-16 03:58:18 +00:00
|
|
|
}
|
2008-06-12 00:51:04 +00:00
|
|
|
}
|
2015-12-18 19:09:34 +00:00
|
|
|
|
|
|
|
|
# Allow extensions to change the list pf feeds. This hook is primarily for changing,
|
|
|
|
|
# manipulating or removing existing feed tags. If you want to add new feeds, you should
|
|
|
|
|
# use OutputPage::addFeedLink() instead.
|
2016-02-17 09:09:32 +00:00
|
|
|
Hooks::run( 'AfterBuildFeedLinks', [ &$feedLinks ] );
|
2015-12-18 19:09:34 +00:00
|
|
|
|
|
|
|
|
$tags += $feedLinks;
|
2004-03-19 08:05:36 +00:00
|
|
|
}
|
2012-12-31 01:11:43 +00:00
|
|
|
|
|
|
|
|
# Canonical URL
|
2014-08-23 08:52:41 +00:00
|
|
|
if ( $config->get( 'EnableCanonicalServerLink' ) ) {
|
2012-12-31 01:11:43 +00:00
|
|
|
if ( $canonicalUrl !== false ) {
|
|
|
|
|
$canonicalUrl = wfExpandUrl( $canonicalUrl, PROTO_CANONICAL );
|
2019-03-29 20:12:24 +00:00
|
|
|
} elseif ( $this->isArticleRelated() ) {
|
|
|
|
|
// This affects all requests where "setArticleRelated" is true. This is
|
|
|
|
|
// typically all requests that show content (query title, curid, oldid, diff),
|
|
|
|
|
// and all wikipage actions (edit, delete, purge, info, history etc.).
|
|
|
|
|
// It does not apply to File pages and Special pages.
|
|
|
|
|
// 'history' and 'info' actions address page metadata rather than the page
|
|
|
|
|
// content itself, so they may not be canonicalized to the view page url.
|
|
|
|
|
// TODO: this ought to be better encapsulated in the Action class.
|
|
|
|
|
$action = Action::getActionName( $this->getContext() );
|
|
|
|
|
if ( in_array( $action, [ 'history', 'info' ] ) ) {
|
|
|
|
|
$query = "action={$action}";
|
Construct clean canonical URLs for wiki pages, ignoring request URL
Canonical URLs allow webmasters to indicate the preferred URL form for
accessing some content that can be reached via a multitude of URL patterns.
It is usually (but not always) distinct from the request URL, which may
feature things like aliases and session-specific query parameters.
We currently derive canonical URLs from request URLs, which is backwards:
it is the web application, not the client, that ought to know the canonical
way to refer to some content.
This patch ensures MediaWiki derives a clean canonical URL for all wiki
pages from the request context's title object and action.
For some assurance that this is the correct approach, see:
http://googlewebmastercentral.blogspot.com/2009/02/specify-your-canonical.html
This Google blog post identifies Wikia as exemplary in its usage of canonical
URLs. Wikia disregards things like the requested revision ID (oldid=NNN) when
constructing the canonical URL. See, for example:
http://fallout.wikia.com/wiki/Aqua_Pura_delivery_program?oldid=2171222
Wikia goes as far as canonicalizing the action=history to the page view URL.
I think that this is incorrect, because the history and info actions are not
views of the page content, but rather its associated metadata.
This affects all requests where "setArticleRelated" is true. This is typically
all urls that show content (title query, curid, oldid, diff), and all actions
thereof (edit, delete, purge, info, history etc.). It does not apply to
File pages and Special pages.
Bug: T67402
Change-Id: I1549ca056637981a0d751020c634b9fab387f7bc
2015-06-22 04:26:22 +00:00
|
|
|
} else {
|
2019-03-29 20:12:24 +00:00
|
|
|
$query = '';
|
Construct clean canonical URLs for wiki pages, ignoring request URL
Canonical URLs allow webmasters to indicate the preferred URL form for
accessing some content that can be reached via a multitude of URL patterns.
It is usually (but not always) distinct from the request URL, which may
feature things like aliases and session-specific query parameters.
We currently derive canonical URLs from request URLs, which is backwards:
it is the web application, not the client, that ought to know the canonical
way to refer to some content.
This patch ensures MediaWiki derives a clean canonical URL for all wiki
pages from the request context's title object and action.
For some assurance that this is the correct approach, see:
http://googlewebmastercentral.blogspot.com/2009/02/specify-your-canonical.html
This Google blog post identifies Wikia as exemplary in its usage of canonical
URLs. Wikia disregards things like the requested revision ID (oldid=NNN) when
constructing the canonical URL. See, for example:
http://fallout.wikia.com/wiki/Aqua_Pura_delivery_program?oldid=2171222
Wikia goes as far as canonicalizing the action=history to the page view URL.
I think that this is incorrect, because the history and info actions are not
views of the page content, but rather its associated metadata.
This affects all requests where "setArticleRelated" is true. This is typically
all urls that show content (title query, curid, oldid, diff), and all actions
thereof (edit, delete, purge, info, history etc.). It does not apply to
File pages and Special pages.
Bug: T67402
Change-Id: I1549ca056637981a0d751020c634b9fab387f7bc
2015-06-22 04:26:22 +00:00
|
|
|
}
|
2019-03-29 20:12:24 +00:00
|
|
|
$canonicalUrl = $this->getTitle()->getCanonicalURL( $query );
|
|
|
|
|
} else {
|
|
|
|
|
$reqUrl = $this->getRequest()->getRequestURL();
|
|
|
|
|
$canonicalUrl = wfExpandUrl( $reqUrl, PROTO_CANONICAL );
|
2012-12-31 01:11:43 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if ( $canonicalUrl !== false ) {
|
2016-02-17 09:09:32 +00:00
|
|
|
$tags[] = Html::element( 'link', [
|
2012-12-31 01:11:43 +00:00
|
|
|
'rel' => 'canonical',
|
|
|
|
|
'href' => $canonicalUrl
|
2016-02-17 09:09:32 +00:00
|
|
|
] );
|
2012-12-31 01:11:43 +00:00
|
|
|
}
|
|
|
|
|
|
2018-07-14 22:54:40 +00:00
|
|
|
// Allow extensions to add, remove and/or otherwise manipulate these links
|
|
|
|
|
// If you want only to *add* <head> links, please use the addHeadItem()
|
|
|
|
|
// (or addHeadItems() for multiple items) method instead.
|
|
|
|
|
// This hook is provided as a last resort for extensions to modify these
|
|
|
|
|
// links before the output is sent to client.
|
|
|
|
|
Hooks::run( 'OutputPageAfterGetHeadLinksArray', [ &$tags, $this ] );
|
|
|
|
|
|
2012-04-24 17:25:06 +00:00
|
|
|
return $tags;
|
|
|
|
|
}
|
|
|
|
|
|
2008-01-12 00:13:53 +00:00
|
|
|
/**
|
2012-07-10 12:48:06 +00:00
|
|
|
* Generate a "<link rel/>" for a feed.
|
2010-01-29 21:32:45 +00:00
|
|
|
*
|
2014-04-08 15:29:17 +00:00
|
|
|
* @param string $type Feed type
|
2013-03-11 17:15:01 +00:00
|
|
|
* @param string $url URL to the feed
|
2014-04-08 15:29:17 +00:00
|
|
|
* @param string $text Value of the "title" attribute
|
|
|
|
|
* @return string HTML fragment
|
2007-09-24 18:25:56 +00:00
|
|
|
*/
|
|
|
|
|
private function feedLink( $type, $url, $text ) {
|
2016-02-17 09:09:32 +00:00
|
|
|
return Html::element( 'link', [
|
2007-09-24 18:25:56 +00:00
|
|
|
'rel' => 'alternate',
|
|
|
|
|
'type' => "application/$type+xml",
|
|
|
|
|
'title' => $text,
|
2016-02-17 09:09:32 +00:00
|
|
|
'href' => $url ]
|
2010-05-22 12:18:22 +00:00
|
|
|
);
|
2007-09-24 18:25:56 +00:00
|
|
|
}
|
2005-07-01 00:03:31 +00:00
|
|
|
|
2008-08-21 14:09:57 +00:00
|
|
|
/**
|
|
|
|
|
* Add a local or specified stylesheet, with the given media options.
|
2015-10-31 15:16:58 +00:00
|
|
|
* Internal use only. Use OutputPage::addModuleStyles() if possible.
|
2008-08-21 14:09:57 +00:00
|
|
|
*
|
2013-03-11 17:15:01 +00:00
|
|
|
* @param string $style URL to the file
|
2014-07-24 17:42:24 +00:00
|
|
|
* @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
|
2008-08-21 14:09:57 +00:00
|
|
|
*/
|
2010-05-22 12:18:22 +00:00
|
|
|
public function addStyle( $style, $media = '', $condition = '', $dir = '' ) {
|
2016-02-17 09:09:32 +00:00
|
|
|
$options = [];
|
2013-04-17 14:52:47 +00:00
|
|
|
if ( $media ) {
|
2008-08-21 14:09:57 +00:00
|
|
|
$options['media'] = $media;
|
2010-05-22 12:18:22 +00:00
|
|
|
}
|
2013-04-17 14:52:47 +00:00
|
|
|
if ( $condition ) {
|
2008-08-21 14:09:57 +00:00
|
|
|
$options['condition'] = $condition;
|
2010-05-22 12:18:22 +00:00
|
|
|
}
|
2013-04-17 14:52:47 +00:00
|
|
|
if ( $dir ) {
|
2008-08-21 14:09:57 +00:00
|
|
|
$options['dir'] = $dir;
|
2010-05-22 12:18:22 +00:00
|
|
|
}
|
2008-08-21 14:09:57 +00:00
|
|
|
$this->styles[$style] = $options;
|
|
|
|
|
}
|
2009-07-15 00:55:58 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Adds inline CSS styles
|
2015-10-31 15:16:58 +00:00
|
|
|
* Internal use only. Use OutputPage::addModuleStyles() if possible.
|
|
|
|
|
*
|
2014-04-08 15:29:17 +00:00
|
|
|
* @param mixed $style_css Inline CSS
|
2013-03-11 17:15:01 +00:00
|
|
|
* @param string $flip Set to 'flip' to flip the CSS if needed
|
2009-07-15 00:55:58 +00:00
|
|
|
*/
|
2011-09-11 21:07:17 +00:00
|
|
|
public function addInlineStyle( $style_css, $flip = 'noflip' ) {
|
2013-04-17 14:52:47 +00:00
|
|
|
if ( $flip === 'flip' && $this->getLanguage()->isRTL() ) {
|
2011-09-04 21:50:02 +00:00
|
|
|
# If wanted, and the interface is right-to-left, flip the CSS
|
|
|
|
|
$style_css = CSSJanus::transform( $style_css, true, false );
|
|
|
|
|
}
|
2016-02-18 16:33:15 +00:00
|
|
|
$this->mInlineStyles .= Html::inlineStyle( $style_css );
|
here it is ... the upload-api, script-server, js2 (javascript phase2) branch merge 1st attempt.
Here is a short overview of changes and associated default configuration variables (most everything is off by default) also see ~soon to be updated~: http://www.mediawiki.org/wiki/Media_Projects_Overview
= Upload Improvements =
==Upload API ==
* Based on the early work of Bryan Tong and others it adds the upload option to the api.
* We rewrite Special:Upload page to include use the new refactoring
* Added in token checks in both the SpecialUpload.php page so avoids DOS / xss copy-by-url JavaScript based cross site POST file submissions
== Copy by URL==
$wgAllowCopyUploads = false;
* http class rewrite includes a new http background download see: includes/HttpFunctions.php
* spins off a php process that calls: maintenance/http_session_download.php
* pushes updates to the session and gives the user a progress bar on http copy uploads from other server progress (using js2 upload interface) (if not using the js2 upload interface it does the request in-place but the download is limited to the php ini timeout time)
== Firefogg ==
* Firefogg enables resumable upload by chunks
* progress indicators and conditional invokation (js2 system)
* and of-course client side transcoding.
= Script Server =
$wgEnableScriptLoader = false;
* off by default if $wgEnableScriptLoader is turned on script files are grouped, gziped, cached etc.
for more info see: http://www.mediawiki.org/wiki/Extension:ScriptLoader
* Includes some early skin js include fixes (skin/script system still lots of love)
* Includes a "javascript class autoloader" this is packaged into mwEmbed so that the mwEmbed library can work in stand alone mode (while retaining localization and script serving) (one such application is the make page for firefogg.org : http://www.firefogg.org/make/index.html )
* The file that contains the autojavascript loading classes is: js2/php/jsAutoloadLocalClasses.php
* One can use this auto class loading dependency system with extensions and add-ons but I need to better document that.
= js2 system / mwEmbed=
$wgEnableJS2system = false
* includes initial rewrite towards more jquery based javascript code
* especially for the Special:Upload page.
* Also the edit page include support for the "add-media-wizard"
* includes dependency loader for javascript that optionally takes advantage of the script-loader
* remote embedding of javascript interfaces (like embedding video, or commons media searching)
* $wgDebugJavaScript = false; .. .this variable lets you always get "always fresh javascript". When used with the script-loader it does not minify the script-loader output.
= mwEmbed =
* Will commit a separate patch to oggHandler that conditionally outputs <video tag> to use the new javascript video player.
** mv_embed player includes: play-head, volume control, remote embedding, oggz-chop support across plugins.
* add-media-wizard adds easy inserts of media to pages (with import)
== jQuery==
* we include a base install of jQuery, jQuery ui and some plugins.
* all the javascript classes are in the scriptloader so its easy to load any set of jquery ui components that you may need using the script-server. You get a callback so you can then execute js with dependencies loaded.
== other stuff ==
there is a bit more code in js2 that pertains to sequence editing, timed text display and basic image editing. We include a base import of pixastic-lib & pixastic-editor... will work with the pixastic developer to try and ensure upstream compatibility on our usage of the library for in-browser photo and sequence manipulation.
2009-07-14 23:52:14 +00:00
|
|
|
}
|
2009-07-15 00:55:58 +00:00
|
|
|
|
2008-08-21 14:09:57 +00:00
|
|
|
/**
|
resourceloader: Move queue formatting out of OutputPage
HTML formatting of the queue was distributed over several OutputPage methods.
Each method demanding a snippet of HTML by calling makeResourceLoaderLink()
with a limited amount of information. As such, makeResourceLoaderLink() was
unable to provide the client with the proper state information.
Centralising it also allows it to better reduce duplication in HTML output
and maintain a more accurate state.
Problems fixed by centralising:
1. The 'user' module is special (due to per-user 'version' and 'user' params).
It is manually requested via script-src. To avoid a separate (and wrong)
request from something that requires it, we set state=loading directly.
However, because the module is in the bottom, the old HTML formatter could
only put state=loading in the bottom also. This sometimes caused a wrong
request to be fired for modules=user if something in the top queue
triggered a requirement for it.
2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose
of allowing dependencies on style modules without risking duplicate loading
on pages where the styles are loaded already. This didn't work, because the
state information about page-style modules is output near the stylesheet,
which is after the script tag with mw.loader.load(). That runs first, and
mw.loader would still make a duplicate request before it learns the state.
Changes:
* Document reasons for style/script tag order in getHeadHtml (per 09537e83).
* Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed
before since a duplicate check in makeResourceLoaderLink() verified the
origin a second time.
* Declare explicit position 'top' on 'user.options' and 'user.tokens' module.
Previously, OutputPage hardcoded them in the top. The new formatter doesn't.
* Remove getHeadScripts().
* Remove getInlineHeadScripts().
* Remove getExternalHeadScripts().
* Remove buildCssLinks().
* Remove getScriptsForBottomQueue().
* Change where Skin::setupSkinUserCss() is called. This methods lets the skin
add modules to the queue. Previously it was called from buildCssLinks(),
via headElement(), via prepareQuickTemplate(), via OutputPage::output().
It's now in OutputPage::output() directly (slightly earlier). This is needed
because prepareQuickTemplate() calls bottomScripts() before headElement().
And bottomScript() would lazy-initialise the queue and lock it before
setupSkinUserCss() is called from headElement().
This makes execution order more predictable instead of being dependent on
the arbitrary order of data extraction in prepareQuickTemplate (which varies
from one skin to another).
* Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early
on so. This avoids wrongful loading and fixes problem 1.
Effective changes in output:
* mw.loader.state() is now before mw.loader.load(). This fixes problem 2.
* mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading".
* mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1.
* The <script async src> tag for 'startup' changed position (slightly).
Previously it was after all inline scripts and stylesheets. It's still after
all inline scripts and after most stylesheets, but before any user styles.
Since the queue is now formatted outside OutputPage, it can't inject the
meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle
of existing output. This shouldn't have any noticable impact.
Bug: T87871
Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
|
|
|
* Build exempt modules and legacy non-ResourceLoader styles.
|
2011-05-21 19:07:24 +00:00
|
|
|
*
|
2016-07-07 19:26:36 +00:00
|
|
|
* @return string|WrappedStringList HTML
|
2008-08-21 14:09:57 +00:00
|
|
|
*/
|
resourceloader: Move queue formatting out of OutputPage
HTML formatting of the queue was distributed over several OutputPage methods.
Each method demanding a snippet of HTML by calling makeResourceLoaderLink()
with a limited amount of information. As such, makeResourceLoaderLink() was
unable to provide the client with the proper state information.
Centralising it also allows it to better reduce duplication in HTML output
and maintain a more accurate state.
Problems fixed by centralising:
1. The 'user' module is special (due to per-user 'version' and 'user' params).
It is manually requested via script-src. To avoid a separate (and wrong)
request from something that requires it, we set state=loading directly.
However, because the module is in the bottom, the old HTML formatter could
only put state=loading in the bottom also. This sometimes caused a wrong
request to be fired for modules=user if something in the top queue
triggered a requirement for it.
2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose
of allowing dependencies on style modules without risking duplicate loading
on pages where the styles are loaded already. This didn't work, because the
state information about page-style modules is output near the stylesheet,
which is after the script tag with mw.loader.load(). That runs first, and
mw.loader would still make a duplicate request before it learns the state.
Changes:
* Document reasons for style/script tag order in getHeadHtml (per 09537e83).
* Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed
before since a duplicate check in makeResourceLoaderLink() verified the
origin a second time.
* Declare explicit position 'top' on 'user.options' and 'user.tokens' module.
Previously, OutputPage hardcoded them in the top. The new formatter doesn't.
* Remove getHeadScripts().
* Remove getInlineHeadScripts().
* Remove getExternalHeadScripts().
* Remove buildCssLinks().
* Remove getScriptsForBottomQueue().
* Change where Skin::setupSkinUserCss() is called. This methods lets the skin
add modules to the queue. Previously it was called from buildCssLinks(),
via headElement(), via prepareQuickTemplate(), via OutputPage::output().
It's now in OutputPage::output() directly (slightly earlier). This is needed
because prepareQuickTemplate() calls bottomScripts() before headElement().
And bottomScript() would lazy-initialise the queue and lock it before
setupSkinUserCss() is called from headElement().
This makes execution order more predictable instead of being dependent on
the arbitrary order of data extraction in prepareQuickTemplate (which varies
from one skin to another).
* Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early
on so. This avoids wrongful loading and fixes problem 1.
Effective changes in output:
* mw.loader.state() is now before mw.loader.load(). This fixes problem 2.
* mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading".
* mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1.
* The <script async src> tag for 'startup' changed position (slightly).
Previously it was after all inline scripts and stylesheets. It's still after
all inline scripts and after most stylesheets, but before any user styles.
Since the queue is now formatted outside OutputPage, it can't inject the
meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle
of existing output. This shouldn't have any noticable impact.
Bug: T87871
Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
|
|
|
protected function buildExemptModules() {
|
|
|
|
|
$chunks = [];
|
2011-07-22 10:45:07 +00:00
|
|
|
|
resourceloader: Only output ResourceLoaderDynamicStyles when needed
In mediawiki.js, this marker has always been optional, falling back to
appending to <head>. When no stylesheets need to be after the marker
(e.g. no site styles on the wiki, and user is not logged-in), then
there is no need for the marker to exist.
In a previous refactor, I was going to do this and created an
"$append" variable in the function to do what this commit does,
but I forgot to actually use it for anything.
Test Plan:
* Local wiki, with no MediaWiki:Common/{Skinname}.css pages existing.
* When logged-out, before this change, there is a marker, now there is not.
* When creating "MediaWiki:Group-user.css" and logging in, there is still
a marker, and it is still above the <link> for that user styles request
in the <head>.
Bug: T219342
Change-Id: I2e9657f318088860916823efeb96ae4f1532974c
2019-06-26 23:20:33 +00:00
|
|
|
// Requirements:
|
|
|
|
|
// - Within modules provided by the software (core, skin, extensions),
|
|
|
|
|
// styles from skin stylesheets should be overridden by styles
|
|
|
|
|
// from modules dynamically loaded with JavaScript.
|
|
|
|
|
// - Styles from site-specific, private, and user modules should override
|
|
|
|
|
// both of the above.
|
|
|
|
|
//
|
|
|
|
|
// The effective order for stylesheets must thus be:
|
|
|
|
|
// 1. Page style modules, formatted server-side by ResourceLoaderClientHtml.
|
|
|
|
|
// 2. Dynamically-loaded styles, inserted client-side by mw.loader.
|
|
|
|
|
// 3. Styles that are site-specific, private or from the user, formatted
|
|
|
|
|
// server-side by this function.
|
|
|
|
|
//
|
|
|
|
|
// The 'ResourceLoaderDynamicStyles' marker helps JavaScript know where
|
|
|
|
|
// point #2 is.
|
resourceloader: Move queue formatting out of OutputPage
HTML formatting of the queue was distributed over several OutputPage methods.
Each method demanding a snippet of HTML by calling makeResourceLoaderLink()
with a limited amount of information. As such, makeResourceLoaderLink() was
unable to provide the client with the proper state information.
Centralising it also allows it to better reduce duplication in HTML output
and maintain a more accurate state.
Problems fixed by centralising:
1. The 'user' module is special (due to per-user 'version' and 'user' params).
It is manually requested via script-src. To avoid a separate (and wrong)
request from something that requires it, we set state=loading directly.
However, because the module is in the bottom, the old HTML formatter could
only put state=loading in the bottom also. This sometimes caused a wrong
request to be fired for modules=user if something in the top queue
triggered a requirement for it.
2. Since a464d1d4 (T87871) we track states of page-style modules, with purpose
of allowing dependencies on style modules without risking duplicate loading
on pages where the styles are loaded already. This didn't work, because the
state information about page-style modules is output near the stylesheet,
which is after the script tag with mw.loader.load(). That runs first, and
mw.loader would still make a duplicate request before it learns the state.
Changes:
* Document reasons for style/script tag order in getHeadHtml (per 09537e83).
* Pass $type from getModuleStyles() to getAllowedModules(). This wasn't needed
before since a duplicate check in makeResourceLoaderLink() verified the
origin a second time.
* Declare explicit position 'top' on 'user.options' and 'user.tokens' module.
Previously, OutputPage hardcoded them in the top. The new formatter doesn't.
* Remove getHeadScripts().
* Remove getInlineHeadScripts().
* Remove getExternalHeadScripts().
* Remove buildCssLinks().
* Remove getScriptsForBottomQueue().
* Change where Skin::setupSkinUserCss() is called. This methods lets the skin
add modules to the queue. Previously it was called from buildCssLinks(),
via headElement(), via prepareQuickTemplate(), via OutputPage::output().
It's now in OutputPage::output() directly (slightly earlier). This is needed
because prepareQuickTemplate() calls bottomScripts() before headElement().
And bottomScript() would lazy-initialise the queue and lock it before
setupSkinUserCss() is called from headElement().
This makes execution order more predictable instead of being dependent on
the arbitrary order of data extraction in prepareQuickTemplate (which varies
from one skin to another).
* Compute isUserModulePreview() and isKnownEmpty() for the 'user' module early
on so. This avoids wrongful loading and fixes problem 1.
Effective changes in output:
* mw.loader.state() is now before mw.loader.load(). This fixes problem 2.
* mw.loader.state() now sets 'user.options' and 'user.tokens' to "loading".
* mw.loader.state() now sets 'user' (as "loading" or "ready"). Fixes problem 1.
* The <script async src> tag for 'startup' changed position (slightly).
Previously it was after all inline scripts and stylesheets. It's still after
all inline scripts and after most stylesheets, but before any user styles.
Since the queue is now formatted outside OutputPage, it can't inject the
meta-ResourceLoaderDynamicStyles tag and user-stylesheet hack in the middle
of existing output. This shouldn't have any noticable impact.
Bug: T87871
Change-Id: I605b8cd1e1fc009b4662a0edbc54d09dd65ee1df
2016-07-15 14:13:09 +00:00
|
|
|
|
|
|
|
|
// Add legacy styles added through addStyle()/addInlineStyle() here
|
|
|
|
|
$chunks[] = implode( '', $this->buildCssLinksArray() ) . $this->mInlineStyles;
|
|
|
|
|
|
resourceloader: Only output ResourceLoaderDynamicStyles when needed
In mediawiki.js, this marker has always been optional, falling back to
appending to <head>. When no stylesheets need to be after the marker
(e.g. no site styles on the wiki, and user is not logged-in), then
there is no need for the marker to exist.
In a previous refactor, I was going to do this and created an
"$append" variable in the function to do what this commit does,
but I forgot to actually use it for anything.
Test Plan:
* Local wiki, with no MediaWiki:Common/{Skinname}.css pages existing.
* When logged-out, before this change, there is a marker, now there is not.
* When creating "MediaWiki:Group-user.css" and logging in, there is still
a marker, and it is still above the <link> for that user styles request
in the <head>.
Bug: T219342
Change-Id: I2e9657f318088860916823efeb96ae4f1532974c
2019-06-26 23:20:33 +00:00
|
|
|
// Things that go after the ResourceLoaderDynamicStyles marker
|
|
|
|
|
$append = [];
|
2017-05-12 22:26:36 +00:00
|
|
|
$separateReq = [ 'site.styles', 'user.styles' ];
|
2016-08-19 02:04:21 +00:00
|
|
|
foreach ( $this->rlExemptStyleModules as $group => $moduleNames ) {
|
resourceloader: Only output ResourceLoaderDynamicStyles when needed
In mediawiki.js, this marker has always been optional, falling back to
appending to <head>. When no stylesheets need to be after the marker
(e.g. no site styles on the wiki, and user is not logged-in), then
there is no need for the marker to exist.
In a previous refactor, I was going to do this and created an
"$append" variable in the function to do what this commit does,
but I forgot to actually use it for anything.
Test Plan:
* Local wiki, with no MediaWiki:Common/{Skinname}.css pages existing.
* When logged-out, before this change, there is a marker, now there is not.
* When creating "MediaWiki:Group-user.css" and logging in, there is still
a marker, and it is still above the <link> for that user styles request
in the <head>.
Bug: T219342
Change-Id: I2e9657f318088860916823efeb96ae4f1532974c
2019-06-26 23:20:33 +00:00
|
|
|
if ( $moduleNames ) {
|
|
|
|
|
$append[] = $this->makeResourceLoaderLink(
|
|
|
|
|
array_diff( $moduleNames, $separateReq ),
|
2017-05-12 22:26:36 +00:00
|
|
|
ResourceLoaderModule::TYPE_STYLES
|
|
|
|
|
);
|
resourceloader: Only output ResourceLoaderDynamicStyles when needed
In mediawiki.js, this marker has always been optional, falling back to
appending to <head>. When no stylesheets need to be after the marker
(e.g. no site styles on the wiki, and user is not logged-in), then
there is no need for the marker to exist.
In a previous refactor, I was going to do this and created an
"$append" variable in the function to do what this commit does,
but I forgot to actually use it for anything.
Test Plan:
* Local wiki, with no MediaWiki:Common/{Skinname}.css pages existing.
* When logged-out, before this change, there is a marker, now there is not.
* When creating "MediaWiki:Group-user.css" and logging in, there is still
a marker, and it is still above the <link> for that user styles request
in the <head>.
Bug: T219342
Change-Id: I2e9657f318088860916823efeb96ae4f1532974c
2019-06-26 23:20:33 +00:00
|
|
|
|
|
|
|
|
foreach ( array_intersect( $moduleNames, $separateReq ) as $name ) {
|
|
|
|
|
// These require their own dedicated request in order to support "@import"
|
|
|
|
|
// syntax, which is incompatible with concatenation. (T147667, T37562)
|
|
|
|
|
$append[] = $this->makeResourceLoaderLink( $name,
|
|
|
|
|
ResourceLoaderModule::TYPE_STYLES
|
|
|
|
|
);
|
|
|
|
|
}
|
2017-05-12 22:26:36 +00:00
|
|
|
}
|
2011-02-20 17:24:42 +00:00
|
|
|
}
|
resourceloader: Only output ResourceLoaderDynamicStyles when needed
In mediawiki.js, this marker has always been optional, falling back to
appending to <head>. When no stylesheets need to be after the marker
(e.g. no site styles on the wiki, and user is not logged-in), then
there is no need for the marker to exist.
In a previous refactor, I was going to do this and created an
"$append" variable in the function to do what this commit does,
but I forgot to actually use it for anything.
Test Plan:
* Local wiki, with no MediaWiki:Common/{Skinname}.css pages existing.
* When logged-out, before this change, there is a marker, now there is not.
* When creating "MediaWiki:Group-user.css" and logging in, there is still
a marker, and it is still above the <link> for that user styles request
in the <head>.
Bug: T219342
Change-Id: I2e9657f318088860916823efeb96ae4f1532974c
2019-06-26 23:20:33 +00:00
|
|
|
if ( $append ) {
|
|
|
|
|
$chunks[] = Html::element(
|
|
|
|
|
'meta',
|
|
|
|
|
[ 'name' => 'ResourceLoaderDynamicStyles', 'content' => '' ]
|
|
|
|
|
);
|
|
|
|
|
$chunks = array_merge( $chunks, $append );
|
|
|
|
|
}
|
2011-08-31 14:47:08 +00:00
|
|
|
|
resourceloader: Only output ResourceLoaderDynamicStyles when needed
In mediawiki.js, this marker has always been optional, falling back to
appending to <head>. When no stylesheets need to be after the marker
(e.g. no site styles on the wiki, and user is not logged-in), then
there is no need for the marker to exist.
In a previous refactor, I was going to do this and created an
"$append" variable in the function to do what this commit does,
but I forgot to actually use it for anything.
Test Plan:
* Local wiki, with no MediaWiki:Common/{Skinname}.css pages existing.
* When logged-out, before this change, there is a marker, now there is not.
* When creating "MediaWiki:Group-user.css" and logging in, there is still
a marker, and it is still above the <link> for that user styles request
in the <head>.
Bug: T219342
Change-Id: I2e9657f318088860916823efeb96ae4f1532974c
2019-06-26 23:20:33 +00:00
|
|
|
return self::combineWrappedStrings( $chunks );
|
2010-05-24 16:35:23 +00:00
|
|
|
}
|
|
|
|
|
|
2011-09-11 21:07:17 +00:00
|
|
|
/**
|
2014-04-10 18:50:10 +00:00
|
|
|
* @return array
|
2011-09-11 21:07:17 +00:00
|
|
|
*/
|
2010-05-24 16:35:23 +00:00
|
|
|
public function buildCssLinksArray() {
|
2016-02-17 09:09:32 +00:00
|
|
|
$links = [];
|
2011-07-22 10:45:07 +00:00
|
|
|
|
2013-04-17 14:52:47 +00:00
|
|
|
foreach ( $this->styles as $file => $options ) {
|
2008-08-21 14:09:57 +00:00
|
|
|
$link = $this->styleLink( $file, $options );
|
2013-04-17 14:52:47 +00:00
|
|
|
if ( $link ) {
|
2010-05-24 16:35:23 +00:00
|
|
|
$links[$file] = $link;
|
2010-05-22 12:18:22 +00:00
|
|
|
}
|
2008-08-21 14:09:57 +00:00
|
|
|
}
|
2010-05-24 16:35:23 +00:00
|
|
|
return $links;
|
2008-08-21 14:09:57 +00:00
|
|
|
}
|
|
|
|
|
|
2010-01-29 21:32:45 +00:00
|
|
|
/**
|
|
|
|
|
* Generate \<link\> tags for stylesheets
|
|
|
|
|
*
|
2013-03-11 17:15:01 +00:00
|
|
|
* @param string $style URL to the file
|
2014-04-08 15:29:17 +00:00
|
|
|
* @param array $options Option, can contain 'condition', 'dir', 'media' keys
|
|
|
|
|
* @return string HTML fragment
|
2010-01-29 21:32:45 +00:00
|
|
|
*/
|
2014-07-23 02:49:30 +00:00
|
|
|
protected function styleLink( $style, array $options ) {
|
2019-03-29 20:12:24 +00:00
|
|
|
if ( isset( $options['dir'] ) && $this->getLanguage()->getDir() != $options['dir'] ) {
|
|
|
|
|
return '';
|
2008-08-21 14:09:57 +00:00
|
|
|
}
|
|
|
|
|
|
2013-04-17 14:52:47 +00:00
|
|
|
if ( isset( $options['media'] ) ) {
|
2011-01-07 20:22:50 +00:00
|
|
|
$media = self::transformCssMedia( $options['media'] );
|
2020-01-09 23:48:34 +00:00
|
|
|
if ( $media === null ) {
|
2008-08-21 14:09:57 +00:00
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2009-09-07 00:21:55 +00:00
|
|
|
$media = 'all';
|
2008-08-21 14:09:57 +00:00
|
|
|
}
|
|
|
|
|
|
2013-04-17 14:52:47 +00:00
|
|
|
if ( substr( $style, 0, 1 ) == '/' ||
|
2008-08-21 14:09:57 +00:00
|
|
|
substr( $style, 0, 5 ) == 'http:' ||
|
|
|
|
|
substr( $style, 0, 6 ) == 'https:' ) {
|
|
|
|
|
$url = $style;
|
|
|
|
|
} else {
|
2014-08-23 08:52:41 +00:00
|
|
|
$config = $this->getConfig();
|
OutputPage: Remove appending of wgStyleVersion to legacy resources
For addScriptFile(), just remove the appending of wgStyleVersion.
Going forward, anyone still using this, should simply append a query
parameter themselves in a way that is specific to that one url
(instead of relying on a generic global variable). Alternatively, one
could use OutputPage::transformResourcePath if the file is in /w/.
For addStyles(), also remove the appending of wgStyleVersion. Since this
method takes paths relative to /w/skins, we can easily update this to
automatically use transformResourcePath(), so that file-hash based query
parameters are automatically added.
Test Plan:
* Add calls to top of OutputPage::output():
`$this->addStyle( 'Vector/README.md' );`
`$this->addScriptFile( "{$GLOBALS['wgScriptPath']}/composer.json" );`
* Before, they are both inserted as `<link>` (head) and `<script>` (body)
with a query parameter based on wgStyleVersion.
* After, the `<script>` (end of body) has no query.
After, the stylesheet (head) has a SHA1 content hash as query.
Bug: T181318
Change-Id: Ie5ab5066ef7d07279086bde838d7305e9e4eabaf
2018-05-21 21:21:28 +00:00
|
|
|
// Append file hash as query parameter
|
|
|
|
|
$url = self::transformResourcePath(
|
|
|
|
|
$config,
|
|
|
|
|
$config->get( 'StylePath' ) . '/' . $style
|
|
|
|
|
);
|
2008-08-21 14:09:57 +00:00
|
|
|
}
|
|
|
|
|
|
2009-08-11 00:09:24 +00:00
|
|
|
$link = Html::linkedStyle( $url, $media );
|
2008-08-21 14:09:57 +00:00
|
|
|
|
2013-04-17 14:52:47 +00:00
|
|
|
if ( isset( $options['condition'] ) ) {
|
2008-08-21 14:09:57 +00:00
|
|
|
$condition = htmlspecialchars( $options['condition'] );
|
|
|
|
|
$link = "<!--[if $condition]>$link<![endif]-->";
|
|
|
|
|
}
|
|
|
|
|
return $link;
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-22 22:58:03 +00:00
|
|
|
/**
|
|
|
|
|
* Transform path to web-accessible static resource.
|
|
|
|
|
*
|
|
|
|
|
* This is used to add a validation hash as query string.
|
|
|
|
|
* This aids various behaviors:
|
|
|
|
|
*
|
|
|
|
|
* - Put long Cache-Control max-age headers on responses for improved
|
|
|
|
|
* cache performance.
|
|
|
|
|
* - Get the correct version of a file as expected by the current page.
|
|
|
|
|
* - Instantly get the updated version of a file after deployment.
|
|
|
|
|
*
|
|
|
|
|
* Avoid using this for urls included in HTML as otherwise clients may get different
|
|
|
|
|
* versions of a resource when navigating the site depending on when the page was cached.
|
|
|
|
|
* If changes to the url propagate, this is not a problem (e.g. if the url is in
|
|
|
|
|
* an external stylesheet).
|
|
|
|
|
*
|
|
|
|
|
* @since 1.27
|
|
|
|
|
* @param Config $config
|
|
|
|
|
* @param string $path Path-absolute URL to file (from document root, must start with "/")
|
|
|
|
|
* @return string URL
|
|
|
|
|
*/
|
|
|
|
|
public static function transformResourcePath( Config $config, $path ) {
|
|
|
|
|
global $IP;
|
2017-02-10 00:03:06 +00:00
|
|
|
|
|
|
|
|
$localDir = $IP;
|
2016-02-22 03:51:19 +00:00
|
|
|
$remotePathPrefix = $config->get( 'ResourceBasePath' );
|
|
|
|
|
if ( $remotePathPrefix === '' ) {
|
|
|
|
|
// The configured base path is required to be empty string for
|
|
|
|
|
// wikis in the domain root
|
|
|
|
|
$remotePath = '/';
|
|
|
|
|
} else {
|
|
|
|
|
$remotePath = $remotePathPrefix;
|
|
|
|
|
}
|
2017-01-18 06:07:05 +00:00
|
|
|
if ( strpos( $path, $remotePath ) !== 0 || substr( $path, 0, 2 ) === '//' ) {
|
|
|
|
|
// - Path is outside wgResourceBasePath, ignore.
|
|
|
|
|
// - Path is protocol-relative. Fixes T155310. Not supported by RelPath lib.
|
2016-01-22 22:58:03 +00:00
|
|
|
return $path;
|
|
|
|
|
}
|
2017-02-10 00:03:06 +00:00
|
|
|
// For files in resources, extensions/ or skins/, ResourceBasePath is preferred here.
|
|
|
|
|
// For other misc files in $IP, we'll fallback to that as well. There is, however, a fourth
|
|
|
|
|
// supported dir/path pair in the configuration (wgUploadDirectory, wgUploadPath)
|
|
|
|
|
// which is not expected to be in wgResourceBasePath on CDNs. (T155146)
|
|
|
|
|
$uploadPath = $config->get( 'UploadPath' );
|
|
|
|
|
if ( strpos( $path, $uploadPath ) === 0 ) {
|
|
|
|
|
$localDir = $config->get( 'UploadDirectory' );
|
|
|
|
|
$remotePathPrefix = $remotePath = $uploadPath;
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-21 04:33:38 +00:00
|
|
|
$path = RelPath::getRelativePath( $path, $remotePath );
|
2017-02-10 00:03:06 +00:00
|
|
|
return self::transformFilePath( $remotePathPrefix, $localDir, $path );
|
2016-01-22 22:58:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Utility method for transformResourceFilePath().
|
|
|
|
|
*
|
|
|
|
|
* Caller is responsible for ensuring the file exists. Emits a PHP warning otherwise.
|
|
|
|
|
*
|
|
|
|
|
* @since 1.27
|
2017-08-11 16:09:41 +00:00
|
|
|
* @param string $remotePathPrefix URL path prefix that points to $localPath
|
2016-01-22 22:58:03 +00:00
|
|
|
* @param string $localPath File directory exposed at $remotePath
|
|
|
|
|
* @param string $file Path to target file relative to $localPath
|
|
|
|
|
* @return string URL
|
|
|
|
|
*/
|
2016-02-22 03:51:19 +00:00
|
|
|
public static function transformFilePath( $remotePathPrefix, $localPath, $file ) {
|
2016-01-22 22:58:03 +00:00
|
|
|
$hash = md5_file( "$localPath/$file" );
|
|
|
|
|
if ( $hash === false ) {
|
|
|
|
|
wfLogWarning( __METHOD__ . ": Failed to hash $localPath/$file" );
|
|
|
|
|
$hash = '';
|
|
|
|
|
}
|
2016-02-22 03:51:19 +00:00
|
|
|
return "$remotePathPrefix/$file?" . substr( $hash, 0, 5 );
|
2016-01-22 22:58:03 +00:00
|
|
|
}
|
|
|
|
|
|
2010-01-29 21:32:45 +00:00
|
|
|
/**
|
|
|
|
|
* Transform "media" attribute based on request parameters
|
|
|
|
|
*
|
2014-04-08 15:29:17 +00:00
|
|
|
* @param string $media Current value of the "media" attribute
|
2020-03-24 08:59:02 +00:00
|
|
|
* @return string|null Modified value of the "media" attribute, or null to skip
|
2013-01-14 03:26:15 +00:00
|
|
|
* this stylesheet
|
2010-01-29 21:32:45 +00:00
|
|
|
*/
|
2011-01-07 20:22:50 +00:00
|
|
|
public static function transformCssMedia( $media ) {
|
2013-05-11 19:05:43 +00:00
|
|
|
global $wgRequest;
|
2008-08-21 14:09:57 +00:00
|
|
|
|
2016-10-13 05:34:26 +00:00
|
|
|
// https://www.w3.org/TR/css3-mediaqueries/#syntax
|
2013-01-14 03:26:15 +00:00
|
|
|
$screenMediaQueryRegex = '/^(?:only\s+)?screen\b/i';
|
|
|
|
|
|
2008-08-21 14:09:57 +00:00
|
|
|
// Switch in on-screen display for media testing
|
2016-02-17 09:09:32 +00:00
|
|
|
$switches = [
|
2008-08-21 14:09:57 +00:00
|
|
|
'printable' => 'print',
|
|
|
|
|
'handheld' => 'handheld',
|
2016-02-17 09:09:32 +00:00
|
|
|
];
|
2013-04-17 14:52:47 +00:00
|
|
|
foreach ( $switches as $switch => $targetMedia ) {
|
|
|
|
|
if ( $wgRequest->getBool( $switch ) ) {
|
|
|
|
|
if ( $media == $targetMedia ) {
|
2008-08-21 14:09:57 +00:00
|
|
|
$media = '';
|
2013-04-17 14:52:47 +00:00
|
|
|
} elseif ( preg_match( $screenMediaQueryRegex, $media ) === 1 ) {
|
2015-10-14 07:40:50 +00:00
|
|
|
/* 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.
|
|
|
|
|
*/
|
2013-04-17 14:52:47 +00:00
|
|
|
if ( $targetMedia == 'print' || $media == 'screen' ) {
|
2013-01-14 03:26:15 +00:00
|
|
|
return null;
|
|
|
|
|
}
|
2008-08-21 14:09:57 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $media;
|
|
|
|
|
}
|
|
|
|
|
|
2008-02-18 07:25:35 +00:00
|
|
|
/**
|
|
|
|
|
* Add a wikitext-formatted message to the output.
|
|
|
|
|
* This is equivalent to:
|
|
|
|
|
*
|
2012-07-24 01:04:15 +00:00
|
|
|
* $wgOut->addWikiText( wfMessage( ... )->plain() )
|
2019-10-11 18:17:48 +00:00
|
|
|
*
|
|
|
|
|
* @param mixed ...$args
|
2008-02-18 07:25:35 +00:00
|
|
|
*/
|
2019-10-11 18:17:48 +00:00
|
|
|
public function addWikiMsg( ...$args ) {
|
2008-02-18 07:25:35 +00:00
|
|
|
$name = array_shift( $args );
|
|
|
|
|
$this->addWikiMsgArray( $name, $args );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Add a wikitext-formatted message to the output.
|
|
|
|
|
* Like addWikiMsg() except the parameters are taken as an array
|
|
|
|
|
* instead of a variable argument list.
|
|
|
|
|
*
|
2014-04-10 18:50:10 +00:00
|
|
|
* @param string $name
|
|
|
|
|
* @param array $args
|
2008-02-18 07:25:35 +00:00
|
|
|
*/
|
2011-10-23 08:13:52 +00:00
|
|
|
public function addWikiMsgArray( $name, $args ) {
|
2012-03-08 20:06:49 +00:00
|
|
|
$this->addHTML( $this->msg( $name, $args )->parseAsBlock() );
|
2008-02-18 07:25:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2008-04-14 07:45:50 +00:00
|
|
|
* This function takes a number of message/argument specifications, wraps them in
|
2008-02-18 07:25:35 +00:00
|
|
|
* some overall structure, and then parses the result and adds it to the output.
|
|
|
|
|
*
|
2015-01-08 17:09:09 +00:00
|
|
|
* In the $wrap, $1 is replaced with the first message, $2 with the second,
|
|
|
|
|
* and so on. The subsequent arguments may be either
|
|
|
|
|
* 1) strings, in which case they are message names, or
|
|
|
|
|
* 2) arrays, in which case, within each array, the first element is the message
|
|
|
|
|
* name, and subsequent elements are the parameters to that message.
|
2008-02-18 07:25:35 +00:00
|
|
|
*
|
2015-01-08 17:09:09 +00:00
|
|
|
* Don't use this for messages that are not in the user's interface language.
|
2008-04-18 06:31:37 +00:00
|
|
|
*
|
2008-02-18 07:25:35 +00:00
|
|
|
* For example:
|
|
|
|
|
*
|
2019-01-07 01:33:50 +00:00
|
|
|
* $wgOut->wrapWikiMsg( "<div class='error'>\n$1\n</div>", 'some-error' );
|
2008-04-14 07:45:50 +00:00
|
|
|
*
|
2008-02-18 07:25:35 +00:00
|
|
|
* Is equivalent to:
|
|
|
|
|
*
|
2019-01-07 01:33:50 +00:00
|
|
|
* $wgOut->addWikiTextAsInterface( "<div class='error'>\n"
|
|
|
|
|
* . wfMessage( 'some-error' )->plain() . "\n</div>" );
|
2010-01-14 17:14:49 +00:00
|
|
|
*
|
2017-02-20 22:44:19 +00:00
|
|
|
* The newline after the opening div is needed in some wikitext. See T21226.
|
2011-05-21 19:07:24 +00:00
|
|
|
*
|
2014-04-10 18:50:10 +00:00
|
|
|
* @param string $wrap
|
2019-10-11 18:17:48 +00:00
|
|
|
* @param mixed ...$msgSpecs
|
2008-02-18 07:25:35 +00:00
|
|
|
*/
|
2019-10-11 18:17:48 +00:00
|
|
|
public function wrapWikiMsg( $wrap, ...$msgSpecs ) {
|
2008-02-18 07:25:35 +00:00
|
|
|
$s = $wrap;
|
|
|
|
|
foreach ( $msgSpecs as $n => $spec ) {
|
|
|
|
|
if ( is_array( $spec ) ) {
|
|
|
|
|
$args = $spec;
|
|
|
|
|
$name = array_shift( $args );
|
2013-02-03 20:05:24 +00:00
|
|
|
} else {
|
2016-02-17 09:09:32 +00:00
|
|
|
$args = [];
|
2008-02-18 07:25:35 +00:00
|
|
|
$name = $spec;
|
|
|
|
|
}
|
2012-09-03 19:38:59 +00:00
|
|
|
$s = str_replace( '$' . ( $n + 1 ), $this->msg( $name, $args )->plain(), $s );
|
2008-02-18 07:25:35 +00:00
|
|
|
}
|
2018-09-25 15:02:07 +00:00
|
|
|
$this->addWikiTextAsInterface( $s );
|
2008-02-18 07:25:35 +00:00
|
|
|
}
|
2010-01-23 20:11:42 +00:00
|
|
|
|
2013-08-22 22:22:03 +00:00
|
|
|
/**
|
2017-05-20 13:39:40 +00:00
|
|
|
* Whether the output has a table of contents
|
2013-08-22 22:22:03 +00:00
|
|
|
* @return bool
|
|
|
|
|
* @since 1.22
|
|
|
|
|
*/
|
|
|
|
|
public function isTOCEnabled() {
|
|
|
|
|
return $this->mEnableTOC;
|
|
|
|
|
}
|
2014-03-11 20:00:02 +00:00
|
|
|
|
2015-01-05 20:18:57 +00:00
|
|
|
/**
|
2015-07-25 15:32:08 +00:00
|
|
|
* Helper function to setup the PHP implementation of OOUI to use in this request.
|
2015-01-05 20:18:57 +00:00
|
|
|
*
|
2015-07-25 15:32:08 +00:00
|
|
|
* @since 1.26
|
2018-10-21 11:38:39 +00:00
|
|
|
* @param string $skinName The Skin name to determine the correct OOUI theme
|
|
|
|
|
* @param string $dir Language direction
|
2015-01-05 20:18:57 +00:00
|
|
|
*/
|
2017-03-17 01:32:59 +00:00
|
|
|
public static function setupOOUI( $skinName = 'default', $dir = 'ltr' ) {
|
|
|
|
|
$themes = ResourceLoaderOOUIModule::getSkinThemeMap();
|
2017-10-06 22:17:58 +00:00
|
|
|
$theme = $themes[$skinName] ?? $themes['default'];
|
2017-03-17 01:32:59 +00:00
|
|
|
// For example, 'OOUI\WikimediaUITheme'.
|
2015-07-23 16:37:53 +00:00
|
|
|
$themeClass = "OOUI\\{$theme}Theme";
|
|
|
|
|
OOUI\Theme::setSingleton( new $themeClass() );
|
2015-07-25 15:32:08 +00:00
|
|
|
OOUI\Element::setDefaultDir( $dir );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Add ResourceLoader module styles for OOUI and set up the PHP implementation of it for use with
|
|
|
|
|
* MediaWiki and this OutputPage instance.
|
|
|
|
|
*
|
|
|
|
|
* @since 1.25
|
|
|
|
|
*/
|
|
|
|
|
public function enableOOUI() {
|
|
|
|
|
self::setupOOUI(
|
|
|
|
|
strtolower( $this->getSkin()->getSkinName() ),
|
|
|
|
|
$this->getLanguage()->getDir()
|
|
|
|
|
);
|
2016-02-17 09:09:32 +00:00
|
|
|
$this->addModuleStyles( [
|
2016-02-01 22:28:13 +00:00
|
|
|
'oojs-ui-core.styles',
|
2015-04-07 16:02:46 +00:00
|
|
|
'oojs-ui.styles.indicators',
|
2015-07-14 20:30:06 +00:00
|
|
|
'mediawiki.widgets.styles',
|
2018-02-12 19:41:44 +00:00
|
|
|
'oojs-ui-core.icons',
|
2016-02-17 09:09:32 +00:00
|
|
|
] );
|
2015-01-05 20:18:57 +00:00
|
|
|
}
|
2015-06-01 16:58:42 +00:00
|
|
|
|
Initial support for Content Security Policy, disabled by default
The primary goal here is a defense in depth measure to
stop an attacker who found a bug in the parser allowing
them to insert malicious attributes.
This wouldn't stop someone who could insert a full
script tag (since at current it can't distinguish between
malicious and legit user js). It also would not prevent
DOM-based or reflected XSS for anons, as the nonce value
is guessable for anons when receiving a response cached
by varnish. However, the limited protection of just stopping
stored XSS where the attacker only has control of attributes,
is still a big win in my opinion. (But it wouldn't prevent
someone who has that type of xss from abusing things like
data-ooui attribute).
This will likely break many gadgets. Its expected that any
sort of rollout on Wikimedia will be done very slowly, with
lots of testing and the report-only option to begin with.
This is behind feature flags that are off by default, so
merging this patch should not cause any change in default
behaviour.
This may break some extensions (The most obvious one
is charinsert (See fe648d41005), but will probably need
some testing in report-only mode to see if anything else breaks)
This uses the unsafe-eval option of CSP, in order to
support RL's local storage thingy. For better security,
we may want to remove some of the sillier uses of eval
(e.g. jquery.ui.datepicker.js).
For more info, see spec: https://www.w3.org/TR/CSP2/
Additionally see:
https://www.mediawiki.org/wiki/Requests_for_comment/Content-Security-Policy
Bug: T135963
Change-Id: I80f6f469ba4c0b608385483457df96ccb7429ae5
2016-02-29 04:13:10 +00:00
|
|
|
/**
|
|
|
|
|
* Get (and set if not yet set) the CSP nonce.
|
|
|
|
|
*
|
|
|
|
|
* This value needs to be included in any <script> tags on the
|
|
|
|
|
* page.
|
|
|
|
|
*
|
|
|
|
|
* @return string|bool Nonce or false to mean don't output nonce
|
|
|
|
|
* @since 1.32
|
2019-10-28 05:01:17 +00:00
|
|
|
* @deprecated Since 1.35 use getCSP()->getNonce() instead
|
Initial support for Content Security Policy, disabled by default
The primary goal here is a defense in depth measure to
stop an attacker who found a bug in the parser allowing
them to insert malicious attributes.
This wouldn't stop someone who could insert a full
script tag (since at current it can't distinguish between
malicious and legit user js). It also would not prevent
DOM-based or reflected XSS for anons, as the nonce value
is guessable for anons when receiving a response cached
by varnish. However, the limited protection of just stopping
stored XSS where the attacker only has control of attributes,
is still a big win in my opinion. (But it wouldn't prevent
someone who has that type of xss from abusing things like
data-ooui attribute).
This will likely break many gadgets. Its expected that any
sort of rollout on Wikimedia will be done very slowly, with
lots of testing and the report-only option to begin with.
This is behind feature flags that are off by default, so
merging this patch should not cause any change in default
behaviour.
This may break some extensions (The most obvious one
is charinsert (See fe648d41005), but will probably need
some testing in report-only mode to see if anything else breaks)
This uses the unsafe-eval option of CSP, in order to
support RL's local storage thingy. For better security,
we may want to remove some of the sillier uses of eval
(e.g. jquery.ui.datepicker.js).
For more info, see spec: https://www.w3.org/TR/CSP2/
Additionally see:
https://www.mediawiki.org/wiki/Requests_for_comment/Content-Security-Policy
Bug: T135963
Change-Id: I80f6f469ba4c0b608385483457df96ccb7429ae5
2016-02-29 04:13:10 +00:00
|
|
|
*/
|
|
|
|
|
public function getCSPNonce() {
|
2019-10-28 05:01:17 +00:00
|
|
|
return $this->CSP->getNonce();
|
Initial support for Content Security Policy, disabled by default
The primary goal here is a defense in depth measure to
stop an attacker who found a bug in the parser allowing
them to insert malicious attributes.
This wouldn't stop someone who could insert a full
script tag (since at current it can't distinguish between
malicious and legit user js). It also would not prevent
DOM-based or reflected XSS for anons, as the nonce value
is guessable for anons when receiving a response cached
by varnish. However, the limited protection of just stopping
stored XSS where the attacker only has control of attributes,
is still a big win in my opinion. (But it wouldn't prevent
someone who has that type of xss from abusing things like
data-ooui attribute).
This will likely break many gadgets. Its expected that any
sort of rollout on Wikimedia will be done very slowly, with
lots of testing and the report-only option to begin with.
This is behind feature flags that are off by default, so
merging this patch should not cause any change in default
behaviour.
This may break some extensions (The most obvious one
is charinsert (See fe648d41005), but will probably need
some testing in report-only mode to see if anything else breaks)
This uses the unsafe-eval option of CSP, in order to
support RL's local storage thingy. For better security,
we may want to remove some of the sillier uses of eval
(e.g. jquery.ui.datepicker.js).
For more info, see spec: https://www.w3.org/TR/CSP2/
Additionally see:
https://www.mediawiki.org/wiki/Requests_for_comment/Content-Security-Policy
Bug: T135963
Change-Id: I80f6f469ba4c0b608385483457df96ccb7429ae5
2016-02-29 04:13:10 +00:00
|
|
|
}
|
2018-10-03 13:12:37 +00:00
|
|
|
|
2019-10-28 05:01:17 +00:00
|
|
|
/**
|
|
|
|
|
* Get the ContentSecurityPolicy object
|
|
|
|
|
*
|
|
|
|
|
* @since 1.35
|
|
|
|
|
* @return ContentSecurityPolicy
|
|
|
|
|
*/
|
|
|
|
|
public function getCSP() {
|
|
|
|
|
return $this->CSP;
|
|
|
|
|
}
|
2007-08-16 07:05:38 +00:00
|
|
|
}
|