* Removed getSelectOptions, which did nothing other than send FOR UPDATE on some...
[lhc/web/wiklou.git] / includes / OutputPage.php
index 03fc907..1d1df0e 100644 (file)
@@ -219,7 +219,7 @@ class OutputPage {
         * 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__ );
@@ -390,15 +390,17 @@ class OutputPage {
         * 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;
                        }
@@ -410,12 +412,13 @@ class OutputPage {
         * 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;
        }
 
@@ -434,8 +437,8 @@ class OutputPage {
         * 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' );
        }
 
        /**
@@ -454,8 +457,8 @@ class OutputPage {
         *
         * @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' );
        }
 
        /**
@@ -474,8 +477,8 @@ class OutputPage {
         *
         * @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' );
        }
 
        /**
@@ -1281,6 +1284,7 @@ class OutputPage {
         * Get the templates used on this page
         *
         * @return Array (namespace => dbKey => revId)
+        * @since 1.18
         */
        public function getTemplateIds() {
                return $this->mTemplateIds;
@@ -1290,6 +1294,7 @@ class OutputPage {
         * 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;
@@ -2039,28 +2044,11 @@ class OutputPage {
 
        /**
         * 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 );
        }
 
        /**
@@ -2068,8 +2056,7 @@ class OutputPage {
         */
        public function loginToUse() {
                if( $this->getUser()->isLoggedIn() ) {
-                       $this->permissionRequired( 'read' );
-                       return;
+                       throw new PermissionsError( 'read' );
                }
 
                $this->setPageTitle( wfMsg( 'loginreqtitle' ) );
@@ -2211,6 +2198,43 @@ class OutputPage {
                }
        }
 
+       /**
+        * 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
@@ -2316,8 +2340,7 @@ class OutputPage {
         * @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' );
@@ -2342,6 +2365,7 @@ class OutputPage {
                $ret .= implode( "\n", array(
                        $this->getHeadLinks( $sk, true ),
                        $this->buildCssLinks( $sk ),
+                       $this->getHeadScripts( $sk ),
                        $this->getHeadItems()
                ) );
 
@@ -2365,7 +2389,9 @@ class OutputPage {
                        $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
@@ -2585,36 +2611,68 @@ class OutputPage {
        }
 
        /**
-        * 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;
 
@@ -2641,7 +2699,7 @@ class OutputPage {
                        }
                }
                $scripts .= $this->makeResourceLoaderLink( $sk, $userScripts, ResourceLoaderModule::TYPE_SCRIPTS );
-
+               
                return $scripts;
        }
 
@@ -3130,43 +3188,6 @@ class OutputPage {
                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: