http://www.mediawiki.org/wiki/Special:Code/MediaWiki/73686

Revision: 73686
Author:   tparscal
Date:     2010-09-24 22:10:25 +0000 (Fri, 24 Sep 2010)

Log Message:
-----------
* Fixed bug #25281 by adding special treatment for modules in the "private" 
group
* Added $wgResourceLoaderInlinePrivateModules to allow private modules to be 
either embedded in the HTML output or accessed through ResourceLoader (which 
will bypass squid cache and check the user paramter against $wgUser)
* Moved more generated javascript functionality to ResourceLoader
* Fixed comment typo made in r73673
* Added documentation for ResoruceLoaderRegisterModules hook

Modified Paths:
--------------
    trunk/phase3/RELEASE-NOTES
    trunk/phase3/docs/hooks.txt
    trunk/phase3/includes/DefaultSettings.php
    trunk/phase3/includes/OutputPage.php
    trunk/phase3/includes/ResourceLoader.php
    trunk/phase3/includes/ResourceLoaderContext.php
    trunk/phase3/includes/ResourceLoaderModule.php
    trunk/phase3/includes/Skin.php

Modified: trunk/phase3/RELEASE-NOTES
===================================================================
--- trunk/phase3/RELEASE-NOTES  2010-09-24 22:06:17 UTC (rev 73685)
+++ trunk/phase3/RELEASE-NOTES  2010-09-24 22:10:25 UTC (rev 73686)
@@ -62,6 +62,11 @@
   version parameter or not.
 * $wgResourceLoaderDebug was added to specify the default state of debug mode;
   this will still be overridden with the debug URL parameter a la 
$wgLanguageCode.
+* $wgResourceLoaderInlinePrivateModules was added to specify whether private
+  modules such as user.options should be embedded in the HTML output or 
delivered
+  through a resource loader request, which bypasses server cache (like squid) 
and
+  checks the user parameter against $wgUser. The former adds more data to all
+  pages, while the latter adds a request which cannot be cached server side.
 
 === New features in 1.17 ===
 * (bug 10183) Users can now add personal styles and scripts to all skins via

Modified: trunk/phase3/docs/hooks.txt
===================================================================
--- trunk/phase3/docs/hooks.txt 2010-09-24 22:06:17 UTC (rev 73685)
+++ trunk/phase3/docs/hooks.txt 2010-09-24 22:10:25 UTC (rev 73686)
@@ -1337,6 +1337,9 @@
 $article: the page the form is shown for
 $out: OutputPage object
 
+'ResourceLoaderRegisterModules': Right before modules information is required, 
such as when responding to a resource
+loader request or generating HTML output.
+
 'RawPageViewBeforeOutput': Right before the text is blown out in action=raw
 &$obj: RawPage object
 &$text: The text that's going to be the output

Modified: trunk/phase3/includes/DefaultSettings.php
===================================================================
--- trunk/phase3/includes/DefaultSettings.php   2010-09-24 22:06:17 UTC (rev 
73685)
+++ trunk/phase3/includes/DefaultSettings.php   2010-09-24 22:10:25 UTC (rev 
73686)
@@ -1661,6 +1661,12 @@
 );
 
 /**
+ * Whether to embed private modules inline with HTML output or to bypass 
caching and check the user parameter against
+ * $wgUser to prevent unauthorized access to private modules.
+ */
+$wgResourceLoaderInlinePrivateModules = true;
+
+/**
  * The default debug mode (on/off) for of ResourceLoader requests. This will 
still
  * be overridden when the debug URL parameter is used.
  */

Modified: trunk/phase3/includes/OutputPage.php
===================================================================
--- trunk/phase3/includes/OutputPage.php        2010-09-24 22:06:17 UTC (rev 
73685)
+++ trunk/phase3/includes/OutputPage.php        2010-09-24 22:10:25 UTC (rev 
73686)
@@ -2282,7 +2282,8 @@
        
        // TODO: Document
        static function makeResourceLoaderLink( $skin, $modules, $only, $useESI 
= false ) {
-               global $wgUser, $wgLang, $wgRequest, $wgLoadScript, 
$wgResourceLoaderDebug, $wgResourceLoaderUseESI;
+               global $wgUser, $wgLang, $wgRequest, $wgLoadScript, 
$wgResourceLoaderDebug, $wgResourceLoaderUseESI,
+                       $wgResourceLoaderInlinePrivateModules;
                // TODO: Should this be a static function of ResourceLoader 
instead?
                // TODO: Divide off modules starting with "user", and add the 
user parameter to them
                $query = array(
@@ -2308,20 +2309,38 @@
                $links = '';
                foreach ( $groups as $group => $modules ) {
                        $query['modules'] = implode( '|', array_keys( $modules 
) );
-                       // Special handling for user group
-                       if ( $group === 'user' && $wgUser->isLoggedIn() ) {
+                       // Special handling for user-specific groups
+                       if ( ( $group === 'user' || $group === 'private' ) && 
$wgUser->isLoggedIn() ) {
                                $query['user'] = $wgUser->getName();
                        }
+                       // Support inlining of private modules if configured as 
such
+                       if ( $group === 'private' && 
$wgResourceLoaderInlinePrivateModules ) {
+                               $context = new ResourceLoaderContext( new 
FauxRequest( $query ) );
+                               if ( $only == 'styles' ) {
+                                       $links .= Html::inlineStyle(
+                                               
ResourceLoader::makeLoaderConditionalScript(
+                                                       
ResourceLoader::makeModuleResponse( $context, $modules )
+                                               )
+                                       );
+                               } else {
+                                       $links .= Html::inlineScript(
+                                               
ResourceLoader::makeLoaderConditionalScript(
+                                                       
ResourceLoader::makeModuleResponse( $context, $modules )
+                                               )
+                                       );
+                               }
+                               continue;
+                       }
                        // Special handling for user and site groups; because 
users might change their stuff on-wiki like site or
                        // user pages, or user preferences; we need to find the 
highest timestamp of these user-changable modules so
                        // we can ensure cache misses on change
                        if ( $group === 'user' || $group === 'site' ) {
                                // Create a fake request based on the one we 
are about to make so modules return correct times
-                               $request = new ResourceLoaderContext( new 
FauxRequest( $query ) );
+                               $context = new ResourceLoaderContext( new 
FauxRequest( $query ) );
                                // Get the maximum timestamp
                                $timestamp = 0;
                                foreach ( $modules as $module ) {
-                                       $timestamp = max( $timestamp, 
$module->getModifiedTime( $request ) );
+                                       $timestamp = max( $timestamp, 
$module->getModifiedTime( $context ) );
                                }
                                // Add a version parameter so cache will break 
when things change
                                $query['version'] = wfTimestamp( TS_ISO_8601, 
round( $timestamp, -2 ) );

Modified: trunk/phase3/includes/ResourceLoader.php
===================================================================
--- trunk/phase3/includes/ResourceLoader.php    2010-09-24 22:06:17 UTC (rev 
73685)
+++ trunk/phase3/includes/ResourceLoader.php    2010-09-24 22:10:25 UTC (rev 
73686)
@@ -59,7 +59,7 @@
         * This is not inside the module code because it's so much more 
performant to request all of the information at once
         * than it is to have each module requests it's own information.
         * 
-        * @param $modules array list of modules to preload information for
+        * @param $modules array list of module names to preload information for
         * @param $context ResourceLoaderContext context to load the 
information within
         */
        protected static function preloadModuleInfo( array $modules, 
ResourceLoaderContext $context ) {
@@ -268,10 +268,10 @@
                // Split requested modules into two groups, modules and missing
                $modules = array();
                $missing = array();
-
+               
                foreach ( $context->getModules() as $name ) {
                        if ( isset( self::$modules[$name] ) ) {
-                               $modules[] = $name;
+                               $modules[$name] = self::$modules[$name];
                        } else {
                                $missing[] = $name;
                        }
@@ -291,14 +291,19 @@
                }
 
                // Preload information needed to the mtime calculation below
-               self::preloadModuleInfo( $modules, $context );
+               self::preloadModuleInfo( array_keys( $modules ), $context );
 
                // To send Last-Modified and support If-Modified-Since, we need 
to detect 
                // the last modified time
                wfProfileIn( __METHOD__.'-getModifiedTime' );
                $mtime = 1;
-               foreach ( $modules as $name ) {
-                       $mtime = max( $mtime, 
self::$modules[$name]->getModifiedTime( $context ) );
+               foreach ( $modules as $module ) {
+                       // Bypass squid cache if the request includes any 
private modules
+                       if ( $module->getGroup() === 'private' ) {
+                               $smaxage = 0;
+                       }
+                       // Calculate maximum modified time
+                       $mtime = max( $mtime, $module->getModifiedTime( 
$context ) );
                }
                wfProfileOut( __METHOD__.'-getModifiedTime' );
 
@@ -316,26 +321,34 @@
                        return;
                }
 
+               echo self::makeModuleResponse( $context, $modules, $missing );
+
+               wfProfileOut( __METHOD__ );
+       }
+
+       public static function makeModuleResponse( ResourceLoaderContext 
$context, array $modules, $missing = null ) {
+               global $wgUser;
+               
                // Pre-fetch blobs
                $blobs = $context->shouldIncludeMessages() ?
-                       MessageBlobStore::get( $modules, 
$context->getLanguage() ) : array();
+                       MessageBlobStore::get( array_keys( $modules ), 
$context->getLanguage() ) : array();
 
                // Generate output
                $out = '';
-               foreach ( $modules as $name ) {
+               foreach ( $modules as $name => $module ) {
                        wfProfileIn( __METHOD__ . '-' . $name );
 
                        // Scripts
                        $scripts = '';
                        if ( $context->shouldIncludeScripts() ) {
-                               $scripts .= self::$modules[$name]->getScript( 
$context ) . "\n";
+                               $scripts .= $module->getScript( $context ) . 
"\n";
                        }
 
                        // Styles
                        $styles = array();
                        if (
                                $context->shouldIncludeStyles() &&
-                               ( count( $styles = 
self::$modules[$name]->getStyles( $context ) ) )
+                               ( count( $styles = $module->getStyles( $context 
) ) )
                        ) {
                                // Flip CSS on a per-module basis
                                if ( self::$modules[$name]->getFlip( $context ) 
) {
@@ -360,7 +373,7 @@
                                        $out .= self::makeMessageSetScript( 
$messages );
                                        break;
                                default:
-                                       // Minify CSS, unless in debug mode, 
before embedding in implment script
+                                       // Minify CSS before embedding in 
mediaWiki.loader.implement call (unless in debug mode)
                                        if ( !$context->getDebug() ) {
                                                foreach ( $styles as $media => 
$style ) {
                                                        $styles[$media] = 
self::filter( 'minify-css', $style );
@@ -376,29 +389,28 @@
                // Update module states
                if ( $context->shouldIncludeScripts() ) {
                        // Set the state of modules loaded as only scripts to 
ready
-                       if ( count( $modules ) && $context->getOnly() === 
'scripts' && !in_array( 'startup', $modules ) ) {
-                               $out .= self::makeLoaderStateScript( 
array_fill_keys( $modules, 'ready' ) );
+                       if ( count( $modules ) && $context->getOnly() === 
'scripts' && !isset( $modules['startup'] ) ) {
+                               $out .= self::makeLoaderStateScript( 
array_fill_keys( array_keys( $modules ), 'ready' ) );
                        }
                        // Set the state of modules which were requested but 
unavailable as missing
-                       if ( count( $missing ) ) {
+                       if ( is_array( $missing ) && count( $missing ) ) {
                                $out .= self::makeLoaderStateScript( 
array_fill_keys( $missing, 'missing' ) );
                        }
                }
 
-               // Send output
                if ( $context->getDebug() ) {
-                       echo $out;
+                       return $out;
                } else {
                        if ( $context->getOnly() === 'styles' ) {
-                               echo self::filter( 'minify-css', $out );
+                               return self::filter( 'minify-css', $out );
                        } else {
-                               echo self::filter( 'minify-js', $out );
+                               return self::filter( 'minify-js', $out );
                        }
                }
-
-               wfProfileOut( __METHOD__ );
        }
-
+       
+       // Client code generation methods
+       
        public static function makeLoaderImplementScript( $name, $scripts, 
$styles, $messages ) {
                if ( is_array( $scripts ) ) {
                        $scripts = implode( $scripts, "\n" );
@@ -437,7 +449,7 @@
                        return "mediaWiki.loader.state( '$name', '$state' );\n";
                }
        }
-       
+
        public static function makeCustomLoaderScript( $name, $version, 
$dependencies, $group, $script ) {
                $name = Xml::escapeJsString( $name );
                $version = (int) $version > 1 ? (int) $version : 1;
@@ -454,10 +466,10 @@
                        $group = 'null';
                }
                $script = str_replace( "\n", "\n\t", trim( $script ) );
-               return "( function( name, version, dependencies ) {\t$script\t} 
)" .
+               return "( function( name, version, dependencies ) 
{\n\t$script\n} )" .
                        "( '$name', $version, $dependencies, $group );\n";
        }
-       
+
        public static function makeLoaderRegisterScript( $name, $version = 
null, $dependencies = null, $group = null ) {
                if ( is_array( $name ) ) {
                        $registrations = FormatJson::encode( $name );
@@ -480,4 +492,14 @@
                        return "mediaWiki.loader.register( '$name', $version, 
$dependencies, $group );\n";
                }
        }
+
+       public static function makeLoaderConditionalScript( $script ) {
+               $script = str_replace( "\n", "\n\t", trim( $script ) );
+               return "if ( window.mediaWiki ) {\n\t$script\n}\n";
+       }
+
+       public static function makeConfigSetScript( array $configuration ) {
+               $configuration = FormatJson::encode( $configuration );
+               return "mediaWiki.config.set( $configuration );\n";
+       }
 }

Modified: trunk/phase3/includes/ResourceLoaderContext.php
===================================================================
--- trunk/phase3/includes/ResourceLoaderContext.php     2010-09-24 22:06:17 UTC 
(rev 73685)
+++ trunk/phase3/includes/ResourceLoaderContext.php     2010-09-24 22:10:25 UTC 
(rev 73686)
@@ -27,7 +27,7 @@
  * of a specific loader request
  */
 class ResourceLoaderContext {
-       
+
        /* Protected Members */
 
        protected $request;

Modified: trunk/phase3/includes/ResourceLoaderModule.php
===================================================================
--- trunk/phase3/includes/ResourceLoaderModule.php      2010-09-24 22:06:17 UTC 
(rev 73685)
+++ trunk/phase3/includes/ResourceLoaderModule.php      2010-09-24 22:10:25 UTC 
(rev 73686)
@@ -205,7 +205,6 @@
                $this->msgBlobMtime[$lang] = $mtime;
        }
        
-       
        /* Abstract Methods */
        
        /**
@@ -905,21 +904,20 @@
                }
 
                global $wgUser;
-               $username = $context->getUser();
-               // Avoid extra db query by using $wgUser if possible
-               $user = $wgUser->getName() === $username ? $wgUser : 
User::newFromName( $username );
 
-               if ( $user ) {
-                       return $this->modifiedTime[$hash] = $user->getTouched();
+               if ( $context->getUser() === $wgUser->getName() ) {
+                       return $this->modifiedTime[$hash] = 
$wgUser->getTouched();
                } else {
                        return 1;
                }
        }
 
        public function getScript( ResourceLoaderContext $context ) {
-               $user = User::newFromName( $context->getUser() );
-               if ( $user instanceof User ) {
-                       $options = FormatJson::encode( $user->getOptions() );
+               global $wgUser;
+
+               // Verify identity -- this is a private module
+               if ( $context->getUser() === $wgUser->getName() ) {
+                       $options = FormatJson::encode( $wgUser->getOptions() );
                } else {
                        $options = FormatJson::encode( 
User::getDefaultOptions() );
                }
@@ -927,11 +925,17 @@
        }
 
        public function getStyles( ResourceLoaderContext $context ) {
-               global $wgAllowUserCssPrefs;
+               global $wgUser, $wgAllowUserCssPrefs;
+
                if ( $wgAllowUserCssPrefs ) {
-                       $user = User::newFromName( $context->getUser() );
-                       $options = $user instanceof User ? $user->getOptions() 
: User::getDefaultOptions();
-                       
+                       // Verify identity -- this is a private module
+                       if ( $context->getUser() === $wgUser->getName() ) {
+                               $options = FormatJson::encode( 
$wgUser->getOptions() );
+                       } else {
+                               $options = FormatJson::encode( 
User::getDefaultOptions() );
+                       }
+
+                       // Build CSS rules
                        $rules = array();
                        if ( $options['underline'] < 2 ) {
                                $rules[] = "a { text-decoration: " . ( 
$options['underline'] ? 'underline' : 'none' ) . "; }";
@@ -965,9 +969,9 @@
 
                return $wgContLang->getDir() !== $context->getDirection();
        }
-       
+
        public function getGroup() {
-               return 'user';
+               return 'private';
        }
 }
 

Modified: trunk/phase3/includes/Skin.php
===================================================================
--- trunk/phase3/includes/Skin.php      2010-09-24 22:06:17 UTC (rev 73685)
+++ trunk/phase3/includes/Skin.php      2010-09-24 22:10:25 UTC (rev 73686)
@@ -358,7 +358,9 @@
 
        static function makeVariablesScript( $data ) {
                if ( $data ) {
-                       return Html::inlineScript( 'mediaWiki.config.set(' . 
FormatJson::encode( $data ) . ');' );
+                       return Html::inlineScript(
+                               ResourceLoader::makeLoaderConditionalScript( 
ResourceLoader::makeConfigSetScript( $data ) )
+                       );
                } else {
                        return '';
                } 



_______________________________________________
MediaWiki-CVS mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-cvs

Reply via email to