* Instead a new RequestContext should be created and it will implicitly create
* a OutputPage tied to that context.
*/
- function __construct( RequestContext $context=null ) {
+ function __construct( RequestContext $context = null ) {
if ( !isset($context) ) {
# Extensions should use `new RequestContext` instead of `new OutputPage` now.
wfDeprecated( __METHOD__ );
* Filter an array of modules to remove insufficiently trustworthy members, and modules
* which are no longer registered (eg a page is cached before an extension is disabled)
* @param $modules Array
+ * @param $position String if not null, only return modules with this position
* @return Array
*/
- protected function filterModules( $modules, $type = ResourceLoaderModule::TYPE_COMBINED ){
+ protected function filterModules( $modules, $position = null, $type = ResourceLoaderModule::TYPE_COMBINED ){
$resourceLoader = $this->getResourceLoader();
$filteredModules = array();
foreach( $modules as $val ){
$module = $resourceLoader->getModule( $val );
if( $module instanceof ResourceLoaderModule
- && $module->getOrigin() <= $this->getAllowedModules( $type ) )
+ && $module->getOrigin() <= $this->getAllowedModules( $type )
+ && ( is_null( $position ) || $module->getPosition() == $position ) )
{
$filteredModules[] = $val;
}
* Get the list of modules to include on this page
*
* @param $filter Bool whether to filter out insufficiently trustworthy modules
+ * @param $position String if not null, only return modules with this position
* @return Array of module names
*/
- public function getModules( $filter = false, $param = 'mModules' ) {
+ public function getModules( $filter = false, $position = null, $param = 'mModules' ) {
$modules = array_values( array_unique( $this->$param ) );
return $filter
- ? $this->filterModules( $modules )
+ ? $this->filterModules( $modules, $position )
: $modules;
}
* Get the list of module JS to include on this page
* @return array of module names
*/
- public function getModuleScripts( $filter = false ) {
- return $this->getModules( $filter, 'mModuleScripts' );
+ public function getModuleScripts( $filter = false, $position = null ) {
+ return $this->getModules( $filter, $position, 'mModuleScripts' );
}
/**
*
* @return Array of module names
*/
- public function getModuleStyles( $filter = false ) {
- return $this->getModules( $filter, 'mModuleStyles' );
+ public function getModuleStyles( $filter = false, $position = null ) {
+ return $this->getModules( $filter, $position, 'mModuleStyles' );
}
/**
*
* @return Array of module names
*/
- public function getModuleMessages( $filter = false ) {
- return $this->getModules( $filter, 'mModuleMessages' );
+ public function getModuleMessages( $filter = false, $position = null ) {
+ return $this->getModules( $filter, $position, 'mModuleMessages' );
}
/**
* Get the templates used on this page
*
* @return Array (namespace => dbKey => revId)
+ * @since 1.18
*/
public function getTemplateIds() {
return $this->mTemplateIds;
* Get the files used on this page
*
* @return Array (dbKey => array('time' => MW timestamp or null, 'sha1' => sha1 or ''))
+ * @since 1.18
*/
public function getImageTimeKeys() {
return $this->mImageTimeKeys;
/**
* Display an error page noting that a given permission bit is required.
- *
+ * @deprecated since 1.18, just throw the exception directly
* @param $permission String: key required
*/
public function permissionRequired( $permission ) {
- $this->setPageTitle( wfMsg( 'badaccess' ) );
- $this->setHTMLTitle( wfMsg( 'errorpagetitle' ) );
- $this->setRobotPolicy( 'noindex,nofollow' );
- $this->setArticleRelated( false );
- $this->mBodytext = '';
-
- $groups = array_map( array( 'User', 'makeGroupLinkWiki' ),
- User::getGroupsWithPermission( $permission ) );
- if( $groups ) {
- $this->addWikiMsg(
- 'badaccess-groups',
- $this->getContext()->getLang()->commaList( $groups ),
- count( $groups )
- );
- } else {
- $this->addWikiMsg( 'badaccess-group0' );
- }
- $this->returnToMain();
+ throw new PermissionsError( $permission );
}
/**
*/
public function loginToUse() {
if( $this->getUser()->isLoggedIn() ) {
- $this->permissionRequired( 'read' );
- return;
+ throw new PermissionsError( 'read' );
}
$this->setPageTitle( wfMsg( 'loginreqtitle' ) );
}
}
+ /**
+ * Turn off regular page output and return an error reponse
+ * for when rate limiting has triggered.
+ */
+ public function rateLimited() {
+ $this->setPageTitle( wfMsg( 'actionthrottled' ) );
+ $this->setRobotPolicy( 'noindex,follow' );
+ $this->setArticleRelated( false );
+ $this->enableClientCache( false );
+ $this->mRedirect = '';
+ $this->clearHTML();
+ $this->setStatusCode( 503 );
+ $this->addWikiMsg( 'actionthrottledtext' );
+
+ $this->returnToMain( null, $this->getTitle() );
+ }
+
+ /**
+ * Show a warning about slave lag
+ *
+ * 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.
+ *
+ * @param $lag Integer: slave lag
+ */
+ public function showLagWarning( $lag ) {
+ global $wgSlaveLagWarning, $wgSlaveLagCritical;
+ if( $lag >= $wgSlaveLagWarning ) {
+ $message = $lag < $wgSlaveLagCritical
+ ? 'lag-warn-normal'
+ : 'lag-warn-high';
+ $wrap = Html::rawElement( 'div', array( 'class' => "mw-{$message}" ), "\n$1\n" );
+ $this->wrapWikiMsg( "$wrap\n", array( $message, $this->getContext()->getLang()->formatNum( $lag ) ) );
+ }
+ }
+
/**
* Adds JS-based password security checker
* @param $passwordId String ID of input box containing password
* @return String: The doctype, opening <html>, and head element.
*/
public function headElement( Skin $sk, $includeStyle = true ) {
- global $wgOutputEncoding, $wgMimeType;
- global $wgUseTrackbacks, $wgHtml5;
+ global $wgUseTrackbacks;
if ( $sk->commonPrintStylesheet() ) {
$this->addModuleStyles( 'mediawiki.legacy.wikiprintable' );
$ret .= implode( "\n", array(
$this->getHeadLinks( $sk, true ),
$this->buildCssLinks( $sk ),
+ $this->getHeadScripts( $sk ),
$this->getHeadItems()
) );
$this->getUser()->getOption( 'editondblclick' )
)
{
- $bodyAttrs['ondblclick'] = "document.location = '" . Xml::escapeJsString( $this->getTitle()->getEditURL() ) . "'";
+ $editUrl = $this->getTitle()->getLocalUrl( $sk->editUrlOptions() );
+ $bodyAttrs['ondblclick'] = "document.location = '" .
+ Xml::escapeJsString( $editUrl ) . "'";
}
# Class bloat
}
/**
- * Gets the global variables and mScripts; also adds userjs to the end if
- * enabled. Despite the name, these scripts are no longer put in the
- * <head> but at the bottom of the <body>
+ * JS stuff to put in the <head>. This is the startup module, config
+ * vars and modules marked with position 'top'
*
* @param $sk Skin object to use
* @return String: HTML fragment
*/
function getHeadScripts( Skin $sk ) {
- global $wgUseSiteJs, $wgAllowUserJs;
-
// Startup - this will immediately load jquery and mediawiki modules
$scripts = $this->makeResourceLoaderLink( $sk, 'startup', ResourceLoaderModule::TYPE_SCRIPTS, true );
-
- // Script and Messages "only" requests
- $scripts .= $this->makeResourceLoaderLink( $sk, $this->getModuleScripts( true ), ResourceLoaderModule::TYPE_SCRIPTS );
- $scripts .= $this->makeResourceLoaderLink( $sk, $this->getModuleMessages( true ), ResourceLoaderModule::TYPE_MESSAGES );
-
- // Modules requests - let the client calculate dependencies and batch requests as it likes
- $loader = '';
- if ( $this->getModules( true ) ) {
- $loader = Xml::encodeJsCall( 'mw.loader.load', array( $this->getModules( true ) ) ) .
- Xml::encodeJsCall( 'mw.loader.go', array() );
- }
+ // Load config before anything else
$scripts .= Html::inlineScript(
ResourceLoader::makeLoaderConditionalScript(
- ResourceLoader::makeConfigSetScript( $this->getJSVars() ) . $loader
+ ResourceLoader::makeConfigSetScript( $this->getJSVars() )
)
);
+
+ // Script and Messages "only" requests marked for top inclusion
+ // Messages should go first
+ $scripts .= $this->makeResourceLoaderLink( $sk, $this->getModuleMessages( true, 'top' ), ResourceLoaderModule::TYPE_MESSAGES );
+ $scripts .= $this->makeResourceLoaderLink( $sk, $this->getModuleScripts( true, 'top' ), ResourceLoaderModule::TYPE_SCRIPTS );
+ // Modules requests - let the client calculate dependencies and batch requests as it likes
+ // Only load modules that have marked themselves for loading at the top
+ $modules = $this->getModules( true, 'top' );
+ if ( $modules ) {
+ $scripts .= Html::inlineScript(
+ ResourceLoader::makeLoaderConditionalScript(
+ Xml::encodeJsCall( 'mw.loader.load', array( $modules ) ) .
+ Xml::encodeJsCall( 'mw.loader.go', array() )
+ )
+ );
+ }
+
+ return $scripts;
+ }
+
+ /**
+ * JS stuff to put at the bottom of the <body>: modules marked with position 'bottom',
+ * legacy scripts ($this->mScripts), user preferences, site JS and user JS
+ */
+ function getBottomScripts( Skin $sk ) {
+ global $wgUseSiteJs, $wgAllowUserJs;
+
+ // Script and Messages "only" requests marked for bottom inclusion
+ // Messages should go first
+ $scripts = $this->makeResourceLoaderLink( $sk, $this->getModuleMessages( true, 'bottom' ), ResourceLoaderModule::TYPE_MESSAGES );
+ $scripts .= $this->makeResourceLoaderLink( $sk, $this->getModuleScripts( true, 'bottom' ), ResourceLoaderModule::TYPE_SCRIPTS );
+
+ // Modules requests - let the client calculate dependencies and batch requests as it likes
+ // Only load modules that have marked themselves for loading at the bottom
+ $modules = $this->getModules( true, 'bottom' );
+ if ( $modules ) {
+ $scripts .= Html::inlineScript(
+ ResourceLoader::makeLoaderConditionalScript(
+ Xml::encodeJsCall( 'mw.loader.load', array( $modules ) ) .
+ // the go() call is unnecessary if we inserted top modules, but we don't know for sure that we did
+ Xml::encodeJsCall( 'mw.loader.go', array() )
+ )
+ );
+ }
+
// Legacy Scripts
$scripts .= "\n" . $this->mScripts;
}
}
$scripts .= $this->makeResourceLoaderLink( $sk, $userScripts, ResourceLoaderModule::TYPE_SCRIPTS );
-
+
return $scripts;
}
return $media;
}
- /**
- * Turn off regular page output and return an error reponse
- * for when rate limiting has triggered.
- */
- public function rateLimited() {
- $this->setPageTitle( wfMsg( 'actionthrottled' ) );
- $this->setRobotPolicy( 'noindex,follow' );
- $this->setArticleRelated( false );
- $this->enableClientCache( false );
- $this->mRedirect = '';
- $this->clearHTML();
- $this->setStatusCode( 503 );
- $this->addWikiMsg( 'actionthrottledtext' );
-
- $this->returnToMain( null, $this->getTitle() );
- }
-
- /**
- * Show a warning about slave lag
- *
- * 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.
- *
- * @param $lag Integer: slave lag
- */
- public function showLagWarning( $lag ) {
- global $wgSlaveLagWarning, $wgSlaveLagCritical;
- if( $lag >= $wgSlaveLagWarning ) {
- $message = $lag < $wgSlaveLagCritical
- ? 'lag-warn-normal'
- : 'lag-warn-high';
- $wrap = Html::rawElement( 'div', array( 'class' => "mw-{$message}" ), "\n$1\n" );
- $this->wrapWikiMsg( "$wrap\n", array( $message, $this->getContext()->getLang()->formatNum( $lag ) ) );
- }
- }
-
/**
* Add a wikitext-formatted message to the output.
* This is equivalent to: