follow-up r59522, r59523, r59527, r59529, r59530.
[lhc/web/wiklou.git] / includes / OutputPage.php
index 79f1dbd..2f69ab8 100644 (file)
@@ -12,9 +12,7 @@ class OutputPage {
        var $mHTMLtitle = '', $mIsarticle = true, $mPrintable = false;
        var $mSubtitle = '', $mRedirect = '', $mStatusCode;
        var $mLastModified = '', $mETag = false;
-       var $mCategoryLinks = array(), $mLanguageLinks = array();
-
-       var $mScriptLoaderClassList = array();
+       var $mCategoryLinks = array(), $mCategories = array(), $mLanguageLinks = array();
 
        var $mScripts = '', $mLinkColours, $mPageLinkTitle = '', $mHeadItems = array();
        var $mInlineMsg = array();
@@ -28,9 +26,9 @@ class OutputPage {
        var $mContainsOldMagic = 0, $mContainsNewMagic = 0;
        var $mIsArticleRelated = true;
        protected $mParserOptions = null; // lazy initialised, use parserOptions()
-       
+
        var $mFeedLinks = array();
-       
+
        var $mEnableClientCache = true;
        var $mArticleBodyOnly = false;
 
@@ -52,6 +50,8 @@ class OutputPage {
 
        private $mIndexPolicy = 'index';
        private $mFollowPolicy = 'follow';
+       private $mVaryHeader = array( 'Accept-Encoding' => array('list-contains=gzip'),
+                                                                 'Cookie' => null );
 
        /**
         * Constructor
@@ -118,213 +118,21 @@ class OutputPage {
         * @param string $file filename in skins/common or complete on-server path (/foo/bar.js)
         */
        function addScriptFile( $file ) {
-               global $wgStylePath, $wgScript, $wgUser;
-               global $wgEnableScriptLoader, $wgScriptPath;
-
+               global $wgStylePath, $wgStyleVersion, $wgJsMimeType;
                if( substr( $file, 0, 1 ) == '/' ) {
                        $path = $file;
                } else {
-                       $path = "{$wgStylePath}/common/{$file}";
-               }
-
-               // If the class can be determined, use the addScriptClass method
-               $js_class = $this->getJsClassFromPath( $path );
-               if( $js_class ) {
-                       $this->addScriptClass( $js_class );
-                       return true;
-               }
-
-               //do checks for wiki-titles
-               if( strpos( $path, $wgScript ) !== false ) {
-                       $reqPath = str_replace( $wgScript . '?', '', $path );
-                       $reqArgs = explode( '&', $reqPath );
-                       $reqSet = array();
-
-                       foreach( $reqArgs as $arg ) {
-                               list( $key, $var ) = explode( '=', $arg );
-                               $reqSet[$key] = $var;
-                       }
-
-                       if( isset( $reqSet['title'] ) && $reqSet != '' ) {
-                               $jsTitleClass = 'WT:' . $reqSet['title'];
-                               if( $wgEnableScriptLoader ) {
-                                       // Extract any extra parameters (for now just skin)
-                                       $ext_param = ( isset( $reqSet['useskin'] ) && $reqSet['useskin'] != '' )
-                                               ? '|useskin=' . ucfirst( $reqSet['useskin'] ) : '';
-                                       $this->mScriptLoaderClassList[] = $jsTitleClass . $ext_param ;
-                                       return true;
-                               }else{
-                                       $this->addScript( Html::linkedScript(
-                                                               wfAppendQuery( $path, $this->getURIDparam( $jsTitleClass ) )
-                                                       )
-                                               );
-                                       return true;
-                               }
-                       }
-               }
-               // If the script loader could not be used, just add the script to the header
-               $this->addScript( Html::linkedScript( wfAppendQuery( $path, $this->getURIDparam() ) ) );
-       }
-
-       /**
-        * Add the core scripts that are included on every page, for later output into the header
-        *
-        * this includes the conditional sitejs
-        */
-       function addCoreScripts2Top(){
-               global $wgEnableScriptLoader, $wgJSAutoloadLocalClasses, $wgScriptPath, $wgEnableJS2system;
-               global $wgUser, $wgJsMimeType;
-               // @todo We should deprecate wikibits in favor of some mv_embed pieces and jQuery
-
-               if( $wgEnableJS2system ){
-                       $core_classes = array( 'window.jQuery', 'mv_embed', 'wikibits' );
-               } else {
-                       $core_classes = array( 'wikibits' );
+                       $path =  "{$wgStylePath}/common/{$file}";
                }
-
-               //make sure scripts are on top:
-               $postScripts = $this->mScripts;
-               $this->mScripts = '';
-
-               if( $wgEnableScriptLoader ){
-                       //directly add script_loader call
-                       //(separate from other scriptloader calls that may include extensions with conditional js)
-                       $this->mScripts = $this->getScriptLoaderJs( $core_classes );
-               } else {
-                       $so = '';
-                       foreach( $core_classes as $js_class ){
-                               $this->addScriptClass( $js_class );
-                       }
-               }
-               //now re-append any scripts that got added prior to the addCoreScripts2Top call
-               $this->mScripts = $this->mScripts . $postScripts;
-       }
-
-       /**
-        * @param string $js_class Name of the JavaScript class
-        * @return boolean False if the class wasn't found, true on success
-        */
-       function addScriptClass( $js_class ){
-               global $wgDebugJavaScript, $wgJSAutoloadLocalClasses, $wgJSAutoloadClasses,
-                               $wgEnableScriptLoader, $wgStyleVersion, $wgScriptPath, $wgStylePath, $wgEnableJS2system;
-
-               $path = jsScriptLoader::getJsPathFromClass( $js_class );
-               if( $path !== false ){
-                       if( $wgEnableScriptLoader ) {
-                               // Register it with the script loader
-                               if( !in_array( $js_class, $this->mScriptLoaderClassList ) ) {
-                                       $this->mScriptLoaderClassList[] = $js_class;
-                               }
-                       } else {
-                               // Source the script directly
-                               $prefix = "skins/common/";
-                               if( substr( $path, 0, 1 ) == '/' ) {
-                                       // straight path
-                               } elseif( substr( $path, 0, strlen( $prefix ) ) == $prefix ) {
-                                       // Respect $wgStypePath
-                                       $path = "{$wgStylePath}/common/" . substr( $path, strlen( $prefix ) );
-                               } else {
-                                       $path = $wgScriptPath . '/' . $path;
-                               }
-                               $urlAppend = ( $wgDebugJavaScript ) ? time() : $this->getURIDparam( $js_class );
-                               $this->addScript( Html::linkedScript( "$path?$urlAppend" ) );
-
-                               //merge in language text (if js2 is on and we have loadGM function)
-                               if( $wgEnableJS2system ){
-                                       $inlineMsg = jsScriptLoader::getLocalizedMsgsFromClass( $js_class );
-                                       if( $inlineMsg != '' )
-                                               $this->addScript( Html::inlineScript( $inlineMsg ));
-                               }
-                       }
-                       return true;
-               }
-               print "could not find: $js_class\n";
-               wfDebug( __METHOD__ . ' could not find js_class: ' . $js_class );
-               return false; // could not find the class
-       }
-
-       /**
-        * Get the <script> tag which will invoke the script loader
-        * @param $classAry A class array which, if given, overrides $this->mScriptLoaderClassList
-        */
-       function getScriptLoaderJs( $classAry = array() ) {
-               global $wgRequest, $wgDebugJavaScript;
-               // If no class array was provided, use mScriptLoaderClassList
-               if( !count( $classAry ) ) {
-                       $classAry = $this->mScriptLoaderClassList;
-               }
-               $class_list = implode( ',', $classAry );
-
-               $debug_param = ( $wgDebugJavaScript ||
-                                                $wgRequest->getVal( 'debug' ) == 'true' ||
-                                                $wgRequest->getVal( 'debug' ) == '1' )
-                                        ? '&debug=true' : '';
-
-               return Html::linkedScript( wfScript( 'mwScriptLoader' ) .
-                       "?class={$class_list}{$debug_param}&" . $this->getURIDparam( $classAry) ) . "\n";
-       }
-
-       /**
-        * Get the unique request ID parameter for the script-loader request
-        */
-       function getURIDparam( $classAry = array() ) {
-               global $wgDebugJavaScript, $wgStyleVersion, $IP, $wgScriptModifiedCheck;
-               if( $wgDebugJavaScript ) {
-                       return 'urid=' . time();
-               } else {
-                       //support single class_name attr
-                       if( gettype( $classAry) == 'string'  ){
-                               $classAry = array( $classAry );
-                       }
-                       $ftime =  $frev = 0;
-                       foreach( $classAry as $class ) {
-                               if( $wgScriptModifiedCheck ) {
-                                       $js_path =  jsScriptLoader::getJsPathFromClass( $class );
-                                       if( $js_path ) {
-                                               $cur_ftime = filemtime ( $IP ."/". $js_path );
-                                               if( $cur_ftime > $ftime )
-                                                       $ftime = $cur_ftime;
-                                       }
-                               }
-                               // Add the latest revision ID if the class set includes a WT (wiki title)
-                               if( substr($class, 0, 3) == 'WT:'){
-                                       $title_str = substr($class, 3);
-                                       $t = Title::newFromText( $title_str );
-                                       if( $t && $t->exists() ) {
-                                               if( $t->getLatestRevID() > $frev  )
-                                                       $frev = $t->getLatestRevID();
-                                       }
-                               }
-                       }
-                       //build the actual unique request id:
-                       $urid = "urid={$wgStyleVersion}";
-
-                       // Add the file modification time if set
-                       if( $ftime != 0 )
-                               $urid .= "_" . $ftime;
-
-                       //add the wiki rev id if set
-                       if( $frev != 0 )
-                               $urid.= "_" . $frev;
-
-                       return $urid;
-               }
-       }
-
-       /**
-        * Given a script path, get the JS class name, or false if no such path is registered.
-        * @param $path string
-        */
-       function getJsClassFromPath( $path ) {
-               global $wgJSAutoloadClasses, $wgJSAutoloadLocalClasses, $wgScriptPath;
-
-               $scriptLoaderPaths = array_merge( $wgJSAutoloadClasses,  $wgJSAutoloadLocalClasses );
-               foreach( $scriptLoaderPaths as $js_class => $js_path ) {
-                       $js_path = "{$wgScriptPath}/{$js_path}";
-                       if( $path == $js_path )
-                               return $js_class;
-               }
-               return false;
+               $this->addScript( 
+                       Xml::element( 'script', 
+                               array(
+                                       'type' => $wgJsMimeType,
+                                       'src' => "$path?$wgStyleVersion",
+                               ),
+                               '', false
+                       )
+               );
        }
 
        /**
@@ -339,12 +147,7 @@ class OutputPage {
         * Get all registered JS and CSS tags for the header.
         */
        function getScript() {
-               global $wgEnableScriptLoader;
-               if( $wgEnableScriptLoader ){
-                       return $this->mScripts . "\n" . $this->getScriptLoaderJs() . $this->getHeadItems();
-               } else {
-                       return $this->mScripts . $this->getHeadItems();
-               }
+               return $this->mScripts . $this->getHeadItems();
        }
 
        function getHeadItems() {
@@ -543,7 +346,7 @@ class OutputPage {
         * 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.
-         */
+        */
        public function setPageTitle( $name ) {
                global $wgContLang;
                $name = $wgContLang->convert( $name, true );
@@ -589,24 +392,24 @@ class OutputPage {
        public function getOnloadHandler() { return $this->mOnloadHandler; }
        public function disable() { $this->mDoNothing = true; }
        public function isDisabled() { return $this->mDoNothing; }
-       
+
        public function setSyndicated( $show = true ) { $this->mShowFeedLinks = $show; }
-       
+
        public function setFeedAppendQuery( $val ) {
                global $wgFeedClasses;
-               
+
                $this->mFeedLinks = array();
-               
+
                foreach( $wgFeedClasses as $type => $class ) {
                        $query = "feed=$type&".$val;
                        $this->mFeedLinks[$type] = $this->getTitle()->getLocalURL( $query );
                }
        }
-       
+
        public function addFeedLink( $format, $href ) {
                $this->mFeedLinks[$format] = $href;
        }
-       
+
        public function isSyndicated() { return count($this->mFeedLinks); }
 
        public function setArticleRelated( $v ) {
@@ -636,6 +439,10 @@ class OutputPage {
                return $this->mCategoryLinks;
        }
 
+       public function getCategories() {
+               return $this->mCategories;
+       }
+
        /**
         * Add an array of categories, with names in the keys
         */
@@ -685,6 +492,7 @@ class OutputPage {
                                        if ( array_key_exists( $category, $categories ) )
                                                continue;
                                $text = $wgContLang->convertHtml( $title->getText() );
+                               $this->mCategories[] = $title->getText();
                                $this->mCategoryLinks[$type][] = $sk->link( $title, $text );
                        }
                }
@@ -999,19 +807,40 @@ class OutputPage {
                return false;
        }
 
+       public function addVaryHeader( $header, $option = null ) {
+               if ( !array_key_exists( $header, $this->mVaryHeader ) ) {
+                       $this->mVaryHeader[$header] = $option;
+               }
+               elseif( is_array( $option ) ) {
+                       if( is_array( $this->mVaryHeader[$header] ) ) {
+                               $this->mVaryHeader[$header] = array_merge( $this->mVaryHeader[$header], $option );
+                       }
+                       else {
+                               $this->mVaryHeader[$header] = $option;
+                       }
+               }
+               $this->mVaryHeader[$header] = array_unique( $this->mVaryHeader[$header] );
+       }
+
        /** Get a complete X-Vary-Options header */
        public function getXVO() {
                $cvCookies = $this->getCacheVaryCookies();
-               $xvo = 'X-Vary-Options: Accept-Encoding;list-contains=gzip,Cookie;';
-               $first = true;
+               
+               $cookiesOption = array();
                foreach ( $cvCookies as $cookieName ) {
-                       if ( $first ) {
-                               $first = false;
-                       } else {
-                               $xvo .= ';';
-                       }
-                       $xvo .= 'string-contains=' . $cookieName;
+                       $cookiesOption[] = 'string-contains=' . $cookieName;
                }
+               $this->addVaryHeader( 'Cookie', $cookiesOption );
+               
+               $headers = array();
+               foreach( $this->mVaryHeader as $header => $option ) {
+                       $newheader = $header;
+                       if( is_array( $option ) )
+                               $newheader .= ';' . implode( ';', $option );
+                       $headers[] = $newheader;
+               }
+               $xvo = 'X-Vary-Options: ' . implode( ',', $headers );
+               
                return $xvo;
        }
 
@@ -1024,7 +853,7 @@ class OutputPage {
 
                # don't serve compressed data to clients who can't handle it
                # maintain different caches for logged-in users and non-logged in ones
-               $response->header( 'Vary: Accept-Encoding, Cookie' );
+               $response->header( 'Vary: ' . join( ', ', array_keys( $this->mVaryHeader ) ) );
 
                if ( $wgUseXVO ) {
                        # Add an X-Vary-Options header for Squid with Wikimedia patches
@@ -1073,6 +902,7 @@ class OutputPage {
                        $response->header( 'Cache-Control: no-cache, no-store, max-age=0, must-revalidate' );
                        $response->header( 'Pragma: no-cache' );
                }
+               wfRunHooks('CacheHeadersAfterSet', array( $this ) );
        }
 
        /**
@@ -1170,9 +1000,6 @@ class OutputPage {
 
                $sk = $wgUser->getSkin();
 
-               // Add our core scripts to output
-               $this->addCoreScripts2Top();
-
                if ( $wgUseAjax ) {
                        $this->addScriptFile( 'ajax.js' );
 
@@ -1191,17 +1018,6 @@ class OutputPage {
                        $this->addScriptFile( 'rightclickedit.js' );
                }
 
-               global $wgUseAJAXCategories, $wgEnableJS2system;
-               if ($wgUseAJAXCategories && $wgEnableJS2system) {
-                       global $wgAJAXCategoriesNamespaces;
-
-                       $title = $this->getTitle();
-
-                       if( empty( $wgAJAXCategoriesNamespaces ) || in_array( $title->getNamespace(), $wgAJAXCategoriesNamespaces ) ) {
-                               $this->addScriptClass( 'ajaxCategories' );
-                       }
-               }
-
                if( $wgUniversalEditButton ) {
                        if( isset( $wgArticle ) && $this->getTitle() && $this->getTitle()->quickUserCan( 'edit' )
                                && ( $this->getTitle()->exists() || $this->getTitle()->quickUserCan( 'create' ) ) ) {
@@ -1740,8 +1556,8 @@ class OutputPage {
         */
        public function headElement( Skin $sk, $includeStyle = true ) {
                global $wgDocType, $wgDTD, $wgContLanguageCode, $wgOutputEncoding, $wgMimeType;
-               global $wgXhtmlDefaultNamespace, $wgXhtmlNamespaces;
-               global $wgContLang, $wgUseTrackbacks, $wgStyleVersion, $wgEnableScriptLoader, $wgHtml5;
+               global $wgXhtmlDefaultNamespace, $wgXhtmlNamespaces, $wgHtml5Version;
+               global $wgContLang, $wgUseTrackbacks, $wgStyleVersion, $wgHtml5;
 
                $this->addMeta( "http:Content-Type", "$wgMimeType; charset={$wgOutputEncoding}" );
                if ( $sk->commonPrintStylesheet() ) {
@@ -1762,8 +1578,10 @@ class OutputPage {
                $dir = $wgContLang->getDir();
 
                if ( $wgHtml5 ) {
-                       $ret .= "<!doctype html>\n";
-                       $ret .= "<html lang=\"$wgContLanguageCode\" dir=\"$dir\">\n";
+                       $ret .= "<!DOCTYPE html>\n";
+                       $ret .= "<html lang=\"$wgContLanguageCode\" dir=\"$dir\" ";
+                       if ($wgHtml5Version) $ret .= " version=\"$wgHtml5Version\" ";
+                       $ret .= ">\n";
                } else {
                        $ret .= "<!DOCTYPE html PUBLIC \"$wgDocType\" \"$wgDTD\">\n";
                        $ret .= "<html xmlns=\"{$wgXhtmlDefaultNamespace}\" ";
@@ -1785,9 +1603,6 @@ class OutputPage {
                        $ret .= Html::inlineStyle( $sk->usercss );
                }
 
-               if( $wgEnableScriptLoader )
-                       $ret .= $this->getScriptLoaderJs();
-
                if ($wgUseTrackbacks && $this->isArticleRelated())
                        $ret .= $this->getTitle()->trackbackRDF();
 
@@ -1802,12 +1617,13 @@ class OutputPage {
        */
        function getHeadScripts( Skin $sk ) {
                global $wgUser, $wgRequest, $wgJsMimeType, $wgUseSiteJs;
+               global $wgStylePath, $wgStyleVersion;
 
-               $vars = Skin::makeGlobalVariablesScript( $sk->getSkinName() );
+               $scripts = Skin::makeGlobalVariablesScript( $sk->getSkinName() );
+               $scripts .= Html::linkedScript( "{$wgStylePath}/common/wikibits.js?$wgStyleVersion" );
 
                //add site JS if enabled:
                if( $wgUseSiteJs ) {
-                       $sk = $wgUser->getSkin();
                        $jsCache = $wgUser->isLoggedIn() ? '&smaxage=0' : '';
                        $this->addScriptFile(  Skin::makeUrl( '-',
                                        "action=raw$jsCache&gen=js&useskin=" .
@@ -1819,7 +1635,7 @@ class OutputPage {
                //add user js if enabled:
                if( $this->isUserJsAllowed() && $wgUser->isLoggedIn() ) {
                        $action = $wgRequest->getVal( 'action', 'view' );
-                       if( $this->mTitle->isJsSubpage() and $sk->userCanPreview( $action ) ) {
+                       if( $this->mTitle && $this->mTitle->isJsSubpage() and $sk->userCanPreview( $action ) ) {
                                # XXX: additional security check/prompt?
                                $this->addInlineScript( $wgRequest->getText( 'wpTextbox1' ) );
                        } else {
@@ -1831,7 +1647,8 @@ class OutputPage {
                        }
                }
 
-               return $vars . "\n" . $this->mScripts;
+               $scripts .= "\n" . $this->mScripts;
+               return $scripts;
        }
 
        protected function addDefaultMeta() {
@@ -1941,7 +1758,7 @@ class OutputPage {
         * Return URLs for each supported syndication format for this page.
         * @return array associating format keys with URLs
         */
-       public function getSyndicationLinks() {         
+       public function getSyndicationLinks() {
                return $this->mFeedLinks;
        }