(bug 31363) Expand description URLs for thumbnails to canonical form
[lhc/web/wiklou.git] / includes / OutputPage.php
index 33e4bbd..7041518 100644 (file)
@@ -124,6 +124,7 @@ class OutputPage extends ContextSource {
        // @todo FIXME: Next variables probably comes from the resource loader
        var $mModules = array(), $mModuleScripts = array(), $mModuleStyles = array(), $mModuleMessages = array();
        var $mResourceLoader;
+       var $mJsConfigVars = array();
 
        /** @todo FIXME: Is this still used ?*/
        var $mInlineMsg = array();
@@ -223,7 +224,7 @@ class OutputPage extends ContextSource {
         * 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( IContextSource $context = null ) {
                if ( $context === null ) {
                        # Extensions should use `new RequestContext` instead of `new OutputPage` now.
                        wfDeprecated( __METHOD__ );
@@ -1301,7 +1302,7 @@ class OutputPage extends ContextSource {
         * @return Array (dbKey => array('time' => MW timestamp or null, 'sha1' => sha1 or ''))
         * @since 1.18
         */
-       public function getImageTimeKeys() {
+       public function getFileSearchOptions() {
                return $this->mImageTimeKeys;
        }
 
@@ -1311,10 +1312,11 @@ class OutputPage extends ContextSource {
         *
         * @param $text String
         * @param $linestart Boolean: is this the start of a line?
+        * @param $interface Boolean: is this text in the user interface language?
         */
-       public function addWikiText( $text, $linestart = true ) {
+       public function addWikiText( $text, $linestart = true, $interface = true ) {
                $title = $this->getTitle(); // Work arround E_STRICT
-               $this->addWikiTextTitle( $text, $title, $linestart );
+               $this->addWikiTextTitle( $text, $title, $linestart, /*tidy*/false, $interface );
        }
 
        /**
@@ -1329,7 +1331,7 @@ class OutputPage extends ContextSource {
        }
 
        /**
-        * Add wikitext with a custom Title object and
+        * Add wikitext with a custom Title object and tidy enabled.
         *
         * @param $text String: wikitext
         * @param $title Title object
@@ -1357,8 +1359,10 @@ class OutputPage extends ContextSource {
         * @param $title Title object
         * @param $linestart Boolean: is this the start of a line?
         * @param $tidy Boolean: whether to use tidy
+        * @param $interface Boolean: whether it is an interface message
+        *                                                              (for example disables conversion)
         */
-       public function addWikiTextTitle( $text, &$title, $linestart, $tidy = false ) {
+       public function addWikiTextTitle( $text, &$title, $linestart, $tidy = false, $interface = false ) {
                global $wgParser;
 
                wfProfileIn( __METHOD__ );
@@ -1367,6 +1371,7 @@ class OutputPage extends ContextSource {
 
                $popts = $this->parserOptions();
                $oldTidy = $popts->setTidy( $tidy );
+               $popts->setInterfaceMessage( (bool) $interface );
 
                $parserOutput = $wgParser->parse(
                        $text, $title, $popts,
@@ -1411,7 +1416,7 @@ class OutputPage extends ContextSource {
                        }
                }
                // File versioning...
-               foreach ( (array)$parserOutput->getImageTimeKeys() as $dbk => $data ) {
+               foreach ( (array)$parserOutput->getFileSearchOptions() as $dbk => $data ) {
                        $this->mImageTimeKeys[$dbk] = $data;
                }
 
@@ -1459,7 +1464,8 @@ class OutputPage extends ContextSource {
         * @param $linestart Boolean: is this the start of a line?
         * @param $interface Boolean: use interface language ($wgLang instead of
         *                   $wgContLang) while parsing language sensitive magic
-        *                   words like GRAMMAR and PLURAL
+        *                   words like GRAMMAR and PLURAL. This also disables
+        *                                       LanguageConverter.
         * @param $language  Language object: target language object, will override
         *                   $interface
         * @return String: HTML
@@ -1649,12 +1655,12 @@ class OutputPage extends ContextSource {
         *   /w/index.php?title=Main_page&variant=zh-cn should never be served.
         */
        function addAcceptLanguage() {
-               global $wgContLang;
-               if( !$this->getRequest()->getCheck( 'variant' ) && $wgContLang->hasVariants() ) {
-                       $variants = $wgContLang->getVariants();
+               $lang = $this->getTitle()->getPageLanguage();
+               if( !$this->getRequest()->getCheck( 'variant' ) && $lang->hasVariants() ) {
+                       $variants = $lang->getVariants();
                        $aloption = array();
                        foreach ( $variants as $variant ) {
-                               if( $variant === $wgContLang->getCode() ) {
+                               if( $variant === $lang->getCode() ) {
                                        continue;
                                } else {
                                        $aloption[] = 'string-contains=' . $variant;
@@ -1800,7 +1806,7 @@ class OutputPage extends ContextSource {
         * the object, let's actually output it:
         */
        public function output() {
-               global $wgLanguageCode, $wgDebugRedirects, $wgMimeType;
+               global $wgLanguageCode, $wgDebugRedirects, $wgMimeType, $wgVaryOnXFP;
 
                if( $this->mDoNothing ) {
                        return;
@@ -1812,7 +1818,7 @@ class OutputPage extends ContextSource {
 
                if ( $this->mRedirect != '' ) {
                        # Standards require redirect URLs to be absolute
-                       $this->mRedirect = wfExpandUrl( $this->mRedirect );
+                       $this->mRedirect = wfExpandUrl( $this->mRedirect, PROTO_CURRENT );
                        if( $this->mRedirectCode == '301' || $this->mRedirectCode == '303' ) {
                                if( !$wgDebugRedirects ) {
                                        $message = HttpStatus::getMessage( $this->mRedirectCode );
@@ -1820,6 +1826,9 @@ class OutputPage extends ContextSource {
                                }
                                $this->mLastModified = wfTimestamp( TS_RFC2822 );
                        }
+                       if ( $wgVaryOnXFP ) {
+                               $this->addVaryHeader( 'X-Forwarded-Proto' );
+                       }
                        $this->sendCacheControl();
 
                        $response->header( "Content-Type: text/html; charset=utf-8" );
@@ -1864,7 +1873,7 @@ class OutputPage extends ContextSource {
                        wfRunHooks( 'BeforePageDisplay', array( &$this, &$sk ) );
 
                        wfProfileIn( 'Output-skin' );
-                       $sk->outputPage( $this );
+                       $sk->outputPage();
                        wfProfileOut( 'Output-skin' );
                }
 
@@ -1893,9 +1902,12 @@ class OutputPage extends ContextSource {
        /**
         * Output a standard error page
         *
+        * showErrorPage( 'titlemsg', 'pagetextmsg', array( 'param1', 'param2' ) );
+        * showErrorPage( 'titlemsg', $messageObject );
+        *
         * @param $title String: message key for page title
-        * @param $msg String: message key for page text
-        * @param $params Array: message parameters
+        * @param $msg Mixed: message key (string) for page text, or a Message object
+        * @param $params Array: message parameters; ignored if $msg is a Message object
         */
        public function showErrorPage( $title, $msg, $params = array() ) {
                if ( $this->getTitle() ) {
@@ -1909,7 +1921,11 @@ class OutputPage extends ContextSource {
                $this->mRedirect = '';
                $this->mBodytext = '';
 
-               $this->addWikiMsgArray( $msg, $params );
+               if ( $msg instanceof Message ){
+                       $this->addHTML( $msg->parse() );
+               } else {
+                       $this->addWikiMsgArray( $msg, $params );
+               }
 
                $this->returnToMain();
        }
@@ -2077,12 +2093,15 @@ class OutputPage extends ContextSource {
                if( is_string( $source ) ) {
                        $this->addWikiMsg( 'viewsourcetext' );
 
+                       $pageLang = $this->getTitle()->getPageLanguage();
                        $params = array(
                                'id'   => 'wpTextbox1',
                                'name' => 'wpTextbox1',
                                'cols' => $this->getUser()->getOption( 'cols' ),
                                'rows' => $this->getUser()->getOption( 'rows' ),
-                               'readonly' => 'readonly'
+                               'readonly' => 'readonly',
+                               'lang' => $pageLang->getCode(),
+                               'dir' => $pageLang->getDir(),
                        );
                        $this->addHTML( Html::element( 'textarea', $params, $source ) );
 
@@ -2223,7 +2242,7 @@ $templates
                        $this->addModuleStyles( 'mediawiki.legacy.wikiprintable' );
                }
 
-               $ret = Html::htmlHeader( array( 'lang' => $this->getLang()->getCode(), 'dir' => $userdir ) );
+               $ret = Html::htmlHeader( array( 'lang' => $this->getLang()->getCode(), 'dir' => $userdir, 'class' => 'client-nojs' ) );
 
                if ( $this->getHTMLTitle() == '' ) {
                        $this->setHTMLTitle( wfMsg( 'pagetitle', $this->getPageTitle() ) );
@@ -2277,8 +2296,7 @@ $templates
         * Add the default ResourceLoader modules to this object
         */
        private function addDefaultModules() {
-               global $wgIncludeLegacyJavaScript, $wgUseAjax,
-                       $wgAjaxWatch, $wgEnableMWSuggest, $wgUseAJAXCategories;
+               global $wgIncludeLegacyJavaScript, $wgUseAjax, $wgAjaxWatch, $wgEnableMWSuggest;
 
                // Add base resources
                $this->addModules( array(
@@ -2302,7 +2320,7 @@ $templates
                        }
 
                        if ( $wgEnableMWSuggest && !$this->getUser()->getOption( 'disablesuggest', false ) ) {
-                               $this->addModules( 'mediawiki.page.mwsuggest' );
+                               $this->addModules( 'mediawiki.legacy.mwsuggest' );
                        }
                }
 
@@ -2314,16 +2332,6 @@ $templates
                if ( $this->isArticle() && $this->getUser()->getOption( 'editondblclick' ) ) {
                        $this->addModules( 'mediawiki.action.view.dblClickEdit' );
                }
-
-               if ( $wgUseAJAXCategories ) {
-                       global $wgAJAXCategoriesNamespaces;
-
-                       $title = $this->getTitle();
-
-                       if( empty( $wgAJAXCategoriesNamespaces ) || in_array( $title->getNamespace(), $wgAJAXCategoriesNamespaces ) ) {
-                               $this->addModules( 'mediawiki.page.ajaxCategories.init' );
-                       }
-               }
        }
 
        /**
@@ -2349,21 +2357,6 @@ $templates
        protected function makeResourceLoaderLink( $modules, $only, $useESI = false, array $extraQuery = array() ) {
                global $wgLoadScript, $wgResourceLoaderUseESI,
                        $wgResourceLoaderInlinePrivateModules;
-               $baseQuery = array(
-                       'lang' => $this->getContext()->getLang()->getCode(),
-                       'debug' => ResourceLoader::inDebugMode() ? 'true' : 'false',
-                       'skin' => $this->getSkin()->getSkinName(),
-               ) + $extraQuery;
-               if ( $only !== ResourceLoaderModule::TYPE_COMBINED ) {
-                       $baseQuery['only'] = $only;
-               }
-               // Propagate printable and handheld parameters if present
-               if ( $this->isPrintable() ) {
-                       $baseQuery['printable'] = 1;
-               }
-               if ( $this->getRequest()->getBool( 'handheld' ) ) {
-                       $baseQuery['handheld'] = 1;
-               }
 
                if ( !count( $modules ) ) {
                        return '';
@@ -2409,14 +2402,26 @@ $templates
 
                $links = '';
                foreach ( $groups as $group => $modules ) {
-                       $query = $baseQuery;
                        // Special handling for user-specific groups
+                       $user = null;
                        if ( ( $group === 'user' || $group === 'private' ) && $this->getUser()->isLoggedIn() ) {
-                               $query['user'] = $this->getUser()->getName();
+                               $user = $this->getUser()->getName();
                        }
 
                        // Create a fake request based on the one we are about to make so modules return
                        // correct timestamp and emptiness data
+                       $query = ResourceLoader::makeLoaderQuery(
+                               array(), // modules; not determined yet
+                               $this->getContext()->getLang()->getCode(),
+                               $this->getSkin()->getSkinName(),
+                               $user,
+                               null, // version; not determined yet
+                               ResourceLoader::inDebugMode(),
+                               $only === ResourceLoaderModule::TYPE_COMBINED ? null : $only,
+                               $this->isPrintable(),
+                               $this->getRequest()->getBool( 'handheld' ),
+                               $extraQuery
+                       );
                        $context = new ResourceLoaderContext( $resourceLoader, new FauxRequest( $query ) );
                        // Drop modules that know they're empty
                        foreach ( $modules as $key => $module ) {
@@ -2429,8 +2434,6 @@ $templates
                                continue;
                        }
 
-                       $query['modules'] = ResourceLoader::makePackedModulesString( array_keys( $modules ) );
-
                        // Support inlining of private modules if configured as such
                        if ( $group === 'private' && $wgResourceLoaderInlinePrivateModules ) {
                                if ( $only == ResourceLoaderModule::TYPE_STYLES ) {
@@ -2452,6 +2455,7 @@ $templates
                        // timestamp of these user-changable modules so we can ensure cache misses on change
                        // This should NOT be done for the site group (bug 27564) because anons get that too
                        // and we shouldn't be putting timestamps in Squid-cached HTML
+                       $version = null;
                        if ( $group === 'user' ) {
                                // Get the maximum timestamp
                                $timestamp = 1;
@@ -2459,15 +2463,21 @@ $templates
                                        $timestamp = max( $timestamp, $module->getModifiedTime( $context ) );
                                }
                                // Add a version parameter so cache will break when things change
-                               $query['version'] = wfTimestamp( TS_ISO_8601_BASIC, $timestamp );
+                               $version = wfTimestamp( TS_ISO_8601_BASIC, $timestamp );
                        }
-                       // Make queries uniform in order
-                       ksort( $query );
 
-                       $url = wfAppendQuery( $wgLoadScript, $query );
-                       // Prevent the IE6 extension check from being triggered (bug 28840)
-                       // by appending a character that's invalid in Windows extensions ('*')
-                       $url .= '&*';
+                       $url = ResourceLoader::makeLoaderURL(
+                               array_keys( $modules ),
+                               $this->getContext()->getLang()->getCode(),
+                               $this->getSkin()->getSkinName(),
+                               $user,
+                               $version,
+                               ResourceLoader::inDebugMode(),
+                               $only === ResourceLoaderModule::TYPE_COMBINED ? null : $only,
+                               $this->isPrintable(),
+                               $this->getRequest()->getBool( 'handheld' ),
+                               $extraQuery
+                       );
                        if ( $useESI && $wgResourceLoaderUseESI ) {
                                $esi = Xml::element( 'esi:include', array( 'src' => $url ) );
                                if ( $only == ResourceLoaderModule::TYPE_STYLES ) {
@@ -2591,15 +2601,33 @@ $templates
        }
 
        /**
-        * Get an array containing global JS variables
+        * Add one or more variables to be set in mw.config in JavaScript.
         *
-        * Do not add things here which can be evaluated in
-        * ResourceLoaderStartupScript - in other words, without state.
-        * You will only be adding bloat to the page and causing page caches to
+        * @param $key {String|Array} Key or array of key/value pars.
+        * @param $value {Mixed} Value of the configuration variable.
+        */
+       public function addJsConfigVars( $keys, $value ) {
+               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.
+        *
+        * Do not add things here which can be evaluated in ResourceLoaderStartupScript
+        * - in other words, page-independent/site-wide variables (without state).
+        * You will only be adding bloat to the html page and causing page caches to
         * have to be purged on configuration changes.
         */
        protected function getJSVars() {
-               global $wgUseAjax, $wgEnableMWSuggest, $wgContLang;
+               global $wgUseAjax, $wgEnableMWSuggest;
 
                $title = $this->getTitle();
                $ns = $title->getNamespace();
@@ -2625,9 +2653,10 @@ $templates
                        'wgCategories' => $this->getCategories(),
                        'wgBreakFrames' => $this->getFrameOptions() == 'DENY',
                );
-               if ( $wgContLang->hasVariants() ) {
-                       $vars['wgUserVariant'] = $wgContLang->getPreferredVariant();
-               }
+               $lang = $this->getTitle()->getPageLanguage();
+               if ( $lang->hasVariants() ) {
+                       $vars['wgUserVariant'] = $lang->getPreferredVariant();
+               }
                foreach ( $title->getRestrictionTypes() as $type ) {
                        $vars['wgRestriction' . ucfirst( $type )] = $title->getRestrictions( $type );
                }
@@ -2638,10 +2667,14 @@ $templates
                        $vars['wgIsMainPage'] = true;
                }
 
-               // Allow extensions to add their custom variables to the global JS variables
-               wfRunHooks( 'MakeGlobalVariablesScript', array( &$vars ) );
+               // 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.
+               wfRunHooks( 'MakeGlobalVariablesScript', array( &$vars, &$this ) );
 
-               return $vars;
+               // Merge in variables from addJsConfigVars last
+               return array_merge( $vars, $this->mJsConfigVars );
        }
 
        /**
@@ -2678,7 +2711,7 @@ $templates
                global $wgUniversalEditButton, $wgFavicon, $wgAppleTouchIcon, $wgEnableAPI,
                        $wgSitename, $wgVersion, $wgHtml5, $wgMimeType,
                        $wgFeed, $wgOverrideSiteFeed, $wgAdvertisedFeedTypes,
-                       $wgDisableLangConversion, $wgCanonicalLanguageLinks, $wgContLang,
+                       $wgDisableLangConversion, $wgCanonicalLanguageLinks,
                        $wgRightsPage, $wgRightsUrl;
 
                $tags = array();
@@ -2804,14 +2837,16 @@ $templates
                        ) );
                }
 
+               $lang = $this->getTitle()->getPageLanguage();
+
                # Language variants
                if ( !$wgDisableLangConversion && $wgCanonicalLanguageLinks
-                       && $wgContLang->hasVariants() ) {
+                       && $lang->hasVariants() ) {
 
-                       $urlvar = $wgContLang->getURLVariant();
+                       $urlvar = $lang->getURLVariant();
 
                        if ( !$urlvar ) {
-                               $variants = $wgContLang->getVariants();
+                               $variants = $lang->getVariants();
                                foreach ( $variants as $_v ) {
                                        $tags[] = Html::element( 'link', array(
                                                'rel' => 'alternate',
@@ -2822,8 +2857,8 @@ $templates
                        } else {
                                $tags[] = Html::element( 'link', array(
                                        'rel' => 'canonical',
-                                       'href' => wfExpandUrl( $this->getTitle()->getFullURL(), PROTO_HTTP ) )
-                               );
+                                       'href' => $this->getTitle()->getCanonicalUrl()
+                               ) );
                        }
                }
 
@@ -2876,9 +2911,10 @@ $templates
 
                        if ( $wgOverrideSiteFeed ) {
                                foreach ( $wgOverrideSiteFeed as $type => $feedUrl ) {
+                                       // Note, this->feedLink escapes the url.
                                        $tags[] = $this->feedLink(
                                                $type,
-                                               htmlspecialchars( $feedUrl ),
+                                               $feedUrl,
                                                wfMsg( "site-{$type}-feed", $wgSitename )
                                        );
                                }
@@ -2940,8 +2976,13 @@ $templates
        /**
         * Adds inline CSS styles
         * @param $style_css Mixed: inline CSS
+        * @param $flip String: Set to 'flip' to flip the CSS if needed
         */
-       public function addInlineStyle( $style_css ){
+       public function addInlineStyle( $style_css, $flip = 'noflip' ) {
+               if( $flip === 'flip' && $this->getLang()->isRTL() ) {
+                       # If wanted, and the interface is right-to-left, flip the CSS
+                       $style_css = CSSJanus::transform( $style_css, true, false );
+               }
                $this->mInlineStyles .= Html::inlineStyle( $style_css );
        }
 
@@ -3020,12 +3061,15 @@ $templates
                                        ResourceLoaderModule::TYPE_STYLES
                        );
                }
-               
+
                // Add stuff in $otherTags (previewed user CSS if applicable)
                $ret .= $otherTags;
                return $ret;
        }
 
+       /**
+        * @return Array
+        */
        public function buildCssLinksArray() {
                $links = array();