r73686 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r73685‎ | r73686 | r73687 >
Date:22:10, 24 September 2010
Author:tparscal
Status:resolved (Comments)
Tags:
Comment:
* 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 (modified) (history)
  • /trunk/phase3/docs/hooks.txt (modified) (history)
  • /trunk/phase3/includes/DefaultSettings.php (modified) (history)
  • /trunk/phase3/includes/OutputPage.php (modified) (history)
  • /trunk/phase3/includes/ResourceLoader.php (modified) (history)
  • /trunk/phase3/includes/ResourceLoaderContext.php (modified) (history)
  • /trunk/phase3/includes/ResourceLoaderModule.php (modified) (history)
  • /trunk/phase3/includes/Skin.php (modified) (history)

Diff [purge]

Index: trunk/phase3/docs/hooks.txt
@@ -1337,6 +1337,9 @@
13381338 $article: the page the form is shown for
13391339 $out: OutputPage object
13401340
 1341+'ResourceLoaderRegisterModules': Right before modules information is required, such as when responding to a resource
 1342+loader request or generating HTML output.
 1343+
13411344 'RawPageViewBeforeOutput': Right before the text is blown out in action=raw
13421345 &$obj: RawPage object
13431346 &$text: The text that's going to be the output
Index: trunk/phase3/includes/ResourceLoaderContext.php
@@ -27,7 +27,7 @@
2828 * of a specific loader request
2929 */
3030 class ResourceLoaderContext {
31 -
 31+
3232 /* Protected Members */
3333
3434 protected $request;
Index: trunk/phase3/includes/OutputPage.php
@@ -2282,7 +2282,8 @@
22832283
22842284 // TODO: Document
22852285 static function makeResourceLoaderLink( $skin, $modules, $only, $useESI = false ) {
2286 - global $wgUser, $wgLang, $wgRequest, $wgLoadScript, $wgResourceLoaderDebug, $wgResourceLoaderUseESI;
 2286+ global $wgUser, $wgLang, $wgRequest, $wgLoadScript, $wgResourceLoaderDebug, $wgResourceLoaderUseESI,
 2287+ $wgResourceLoaderInlinePrivateModules;
22872288 // TODO: Should this be a static function of ResourceLoader instead?
22882289 // TODO: Divide off modules starting with "user", and add the user parameter to them
22892290 $query = array(
@@ -2308,20 +2309,38 @@
23092310 $links = '';
23102311 foreach ( $groups as $group => $modules ) {
23112312 $query['modules'] = implode( '|', array_keys( $modules ) );
2312 - // Special handling for user group
2313 - if ( $group === 'user' && $wgUser->isLoggedIn() ) {
 2313+ // Special handling for user-specific groups
 2314+ if ( ( $group === 'user' || $group === 'private' ) && $wgUser->isLoggedIn() ) {
23142315 $query['user'] = $wgUser->getName();
23152316 }
 2317+ // Support inlining of private modules if configured as such
 2318+ if ( $group === 'private' && $wgResourceLoaderInlinePrivateModules ) {
 2319+ $context = new ResourceLoaderContext( new FauxRequest( $query ) );
 2320+ if ( $only == 'styles' ) {
 2321+ $links .= Html::inlineStyle(
 2322+ ResourceLoader::makeLoaderConditionalScript(
 2323+ ResourceLoader::makeModuleResponse( $context, $modules )
 2324+ )
 2325+ );
 2326+ } else {
 2327+ $links .= Html::inlineScript(
 2328+ ResourceLoader::makeLoaderConditionalScript(
 2329+ ResourceLoader::makeModuleResponse( $context, $modules )
 2330+ )
 2331+ );
 2332+ }
 2333+ continue;
 2334+ }
23162335 // Special handling for user and site groups; because users might change their stuff on-wiki like site or
23172336 // user pages, or user preferences; we need to find the highest timestamp of these user-changable modules so
23182337 // we can ensure cache misses on change
23192338 if ( $group === 'user' || $group === 'site' ) {
23202339 // Create a fake request based on the one we are about to make so modules return correct times
2321 - $request = new ResourceLoaderContext( new FauxRequest( $query ) );
 2340+ $context = new ResourceLoaderContext( new FauxRequest( $query ) );
23222341 // Get the maximum timestamp
23232342 $timestamp = 0;
23242343 foreach ( $modules as $module ) {
2325 - $timestamp = max( $timestamp, $module->getModifiedTime( $request ) );
 2344+ $timestamp = max( $timestamp, $module->getModifiedTime( $context ) );
23262345 }
23272346 // Add a version parameter so cache will break when things change
23282347 $query['version'] = wfTimestamp( TS_ISO_8601, round( $timestamp, -2 ) );
Index: trunk/phase3/includes/ResourceLoader.php
@@ -59,7 +59,7 @@
6060 * This is not inside the module code because it's so much more performant to request all of the information at once
6161 * than it is to have each module requests it's own information.
6262 *
63 - * @param $modules array list of modules to preload information for
 63+ * @param $modules array list of module names to preload information for
6464 * @param $context ResourceLoaderContext context to load the information within
6565 */
6666 protected static function preloadModuleInfo( array $modules, ResourceLoaderContext $context ) {
@@ -268,10 +268,10 @@
269269 // Split requested modules into two groups, modules and missing
270270 $modules = array();
271271 $missing = array();
272 -
 272+
273273 foreach ( $context->getModules() as $name ) {
274274 if ( isset( self::$modules[$name] ) ) {
275 - $modules[] = $name;
 275+ $modules[$name] = self::$modules[$name];
276276 } else {
277277 $missing[] = $name;
278278 }
@@ -291,14 +291,19 @@
292292 }
293293
294294 // Preload information needed to the mtime calculation below
295 - self::preloadModuleInfo( $modules, $context );
 295+ self::preloadModuleInfo( array_keys( $modules ), $context );
296296
297297 // To send Last-Modified and support If-Modified-Since, we need to detect
298298 // the last modified time
299299 wfProfileIn( __METHOD__.'-getModifiedTime' );
300300 $mtime = 1;
301 - foreach ( $modules as $name ) {
302 - $mtime = max( $mtime, self::$modules[$name]->getModifiedTime( $context ) );
 301+ foreach ( $modules as $module ) {
 302+ // Bypass squid cache if the request includes any private modules
 303+ if ( $module->getGroup() === 'private' ) {
 304+ $smaxage = 0;
 305+ }
 306+ // Calculate maximum modified time
 307+ $mtime = max( $mtime, $module->getModifiedTime( $context ) );
303308 }
304309 wfProfileOut( __METHOD__.'-getModifiedTime' );
305310
@@ -316,26 +321,34 @@
317322 return;
318323 }
319324
 325+ echo self::makeModuleResponse( $context, $modules, $missing );
 326+
 327+ wfProfileOut( __METHOD__ );
 328+ }
 329+
 330+ public static function makeModuleResponse( ResourceLoaderContext $context, array $modules, $missing = null ) {
 331+ global $wgUser;
 332+
320333 // Pre-fetch blobs
321334 $blobs = $context->shouldIncludeMessages() ?
322 - MessageBlobStore::get( $modules, $context->getLanguage() ) : array();
 335+ MessageBlobStore::get( array_keys( $modules ), $context->getLanguage() ) : array();
323336
324337 // Generate output
325338 $out = '';
326 - foreach ( $modules as $name ) {
 339+ foreach ( $modules as $name => $module ) {
327340 wfProfileIn( __METHOD__ . '-' . $name );
328341
329342 // Scripts
330343 $scripts = '';
331344 if ( $context->shouldIncludeScripts() ) {
332 - $scripts .= self::$modules[$name]->getScript( $context ) . "\n";
 345+ $scripts .= $module->getScript( $context ) . "\n";
333346 }
334347
335348 // Styles
336349 $styles = array();
337350 if (
338351 $context->shouldIncludeStyles() &&
339 - ( count( $styles = self::$modules[$name]->getStyles( $context ) ) )
 352+ ( count( $styles = $module->getStyles( $context ) ) )
340353 ) {
341354 // Flip CSS on a per-module basis
342355 if ( self::$modules[$name]->getFlip( $context ) ) {
@@ -360,7 +373,7 @@
361374 $out .= self::makeMessageSetScript( $messages );
362375 break;
363376 default:
364 - // Minify CSS, unless in debug mode, before embedding in implment script
 377+ // Minify CSS before embedding in mediaWiki.loader.implement call (unless in debug mode)
365378 if ( !$context->getDebug() ) {
366379 foreach ( $styles as $media => $style ) {
367380 $styles[$media] = self::filter( 'minify-css', $style );
@@ -376,29 +389,28 @@
377390 // Update module states
378391 if ( $context->shouldIncludeScripts() ) {
379392 // Set the state of modules loaded as only scripts to ready
380 - if ( count( $modules ) && $context->getOnly() === 'scripts' && !in_array( 'startup', $modules ) ) {
381 - $out .= self::makeLoaderStateScript( array_fill_keys( $modules, 'ready' ) );
 393+ if ( count( $modules ) && $context->getOnly() === 'scripts' && !isset( $modules['startup'] ) ) {
 394+ $out .= self::makeLoaderStateScript( array_fill_keys( array_keys( $modules ), 'ready' ) );
382395 }
383396 // Set the state of modules which were requested but unavailable as missing
384 - if ( count( $missing ) ) {
 397+ if ( is_array( $missing ) && count( $missing ) ) {
385398 $out .= self::makeLoaderStateScript( array_fill_keys( $missing, 'missing' ) );
386399 }
387400 }
388401
389 - // Send output
390402 if ( $context->getDebug() ) {
391 - echo $out;
 403+ return $out;
392404 } else {
393405 if ( $context->getOnly() === 'styles' ) {
394 - echo self::filter( 'minify-css', $out );
 406+ return self::filter( 'minify-css', $out );
395407 } else {
396 - echo self::filter( 'minify-js', $out );
 408+ return self::filter( 'minify-js', $out );
397409 }
398410 }
399 -
400 - wfProfileOut( __METHOD__ );
401411 }
402 -
 412+
 413+ // Client code generation methods
 414+
403415 public static function makeLoaderImplementScript( $name, $scripts, $styles, $messages ) {
404416 if ( is_array( $scripts ) ) {
405417 $scripts = implode( $scripts, "\n" );
@@ -437,7 +449,7 @@
438450 return "mediaWiki.loader.state( '$name', '$state' );\n";
439451 }
440452 }
441 -
 453+
442454 public static function makeCustomLoaderScript( $name, $version, $dependencies, $group, $script ) {
443455 $name = Xml::escapeJsString( $name );
444456 $version = (int) $version > 1 ? (int) $version : 1;
@@ -454,10 +466,10 @@
455467 $group = 'null';
456468 }
457469 $script = str_replace( "\n", "\n\t", trim( $script ) );
458 - return "( function( name, version, dependencies ) {\t$script\t} )" .
 470+ return "( function( name, version, dependencies ) {\n\t$script\n} )" .
459471 "( '$name', $version, $dependencies, $group );\n";
460472 }
461 -
 473+
462474 public static function makeLoaderRegisterScript( $name, $version = null, $dependencies = null, $group = null ) {
463475 if ( is_array( $name ) ) {
464476 $registrations = FormatJson::encode( $name );
@@ -480,4 +492,14 @@
481493 return "mediaWiki.loader.register( '$name', $version, $dependencies, $group );\n";
482494 }
483495 }
 496+
 497+ public static function makeLoaderConditionalScript( $script ) {
 498+ $script = str_replace( "\n", "\n\t", trim( $script ) );
 499+ return "if ( window.mediaWiki ) {\n\t$script\n}\n";
 500+ }
 501+
 502+ public static function makeConfigSetScript( array $configuration ) {
 503+ $configuration = FormatJson::encode( $configuration );
 504+ return "mediaWiki.config.set( $configuration );\n";
 505+ }
484506 }
Index: trunk/phase3/includes/DefaultSettings.php
@@ -1661,6 +1661,12 @@
16621662 );
16631663
16641664 /**
 1665+ * Whether to embed private modules inline with HTML output or to bypass caching and check the user parameter against
 1666+ * $wgUser to prevent unauthorized access to private modules.
 1667+ */
 1668+$wgResourceLoaderInlinePrivateModules = true;
 1669+
 1670+/**
16651671 * The default debug mode (on/off) for of ResourceLoader requests. This will still
16661672 * be overridden when the debug URL parameter is used.
16671673 */
Index: trunk/phase3/includes/Skin.php
@@ -358,7 +358,9 @@
359359
360360 static function makeVariablesScript( $data ) {
361361 if ( $data ) {
362 - return Html::inlineScript( 'mediaWiki.config.set(' . FormatJson::encode( $data ) . ');' );
 362+ return Html::inlineScript(
 363+ ResourceLoader::makeLoaderConditionalScript( ResourceLoader::makeConfigSetScript( $data ) )
 364+ );
363365 } else {
364366 return '';
365367 }
Index: trunk/phase3/includes/ResourceLoaderModule.php
@@ -205,7 +205,6 @@
206206 $this->msgBlobMtime[$lang] = $mtime;
207207 }
208208
209 -
210209 /* Abstract Methods */
211210
212211 /**
@@ -905,21 +904,20 @@
906905 }
907906
908907 global $wgUser;
909 - $username = $context->getUser();
910 - // Avoid extra db query by using $wgUser if possible
911 - $user = $wgUser->getName() === $username ? $wgUser : User::newFromName( $username );
912908
913 - if ( $user ) {
914 - return $this->modifiedTime[$hash] = $user->getTouched();
 909+ if ( $context->getUser() === $wgUser->getName() ) {
 910+ return $this->modifiedTime[$hash] = $wgUser->getTouched();
915911 } else {
916912 return 1;
917913 }
918914 }
919915
920916 public function getScript( ResourceLoaderContext $context ) {
921 - $user = User::newFromName( $context->getUser() );
922 - if ( $user instanceof User ) {
923 - $options = FormatJson::encode( $user->getOptions() );
 917+ global $wgUser;
 918+
 919+ // Verify identity -- this is a private module
 920+ if ( $context->getUser() === $wgUser->getName() ) {
 921+ $options = FormatJson::encode( $wgUser->getOptions() );
924922 } else {
925923 $options = FormatJson::encode( User::getDefaultOptions() );
926924 }
@@ -927,11 +925,17 @@
928926 }
929927
930928 public function getStyles( ResourceLoaderContext $context ) {
931 - global $wgAllowUserCssPrefs;
 929+ global $wgUser, $wgAllowUserCssPrefs;
 930+
932931 if ( $wgAllowUserCssPrefs ) {
933 - $user = User::newFromName( $context->getUser() );
934 - $options = $user instanceof User ? $user->getOptions() : User::getDefaultOptions();
935 -
 932+ // Verify identity -- this is a private module
 933+ if ( $context->getUser() === $wgUser->getName() ) {
 934+ $options = FormatJson::encode( $wgUser->getOptions() );
 935+ } else {
 936+ $options = FormatJson::encode( User::getDefaultOptions() );
 937+ }
 938+
 939+ // Build CSS rules
936940 $rules = array();
937941 if ( $options['underline'] < 2 ) {
938942 $rules[] = "a { text-decoration: " . ( $options['underline'] ? 'underline' : 'none' ) . "; }";
@@ -965,9 +969,9 @@
966970
967971 return $wgContLang->getDir() !== $context->getDirection();
968972 }
969 -
 973+
970974 public function getGroup() {
971 - return 'user';
 975+ return 'private';
972976 }
973977 }
974978
Index: trunk/phase3/RELEASE-NOTES
@@ -62,6 +62,11 @@
6363 version parameter or not.
6464 * $wgResourceLoaderDebug was added to specify the default state of debug mode;
6565 this will still be overridden with the debug URL parameter a la $wgLanguageCode.
 66+* $wgResourceLoaderInlinePrivateModules was added to specify whether private
 67+ modules such as user.options should be embedded in the HTML output or delivered
 68+ through a resource loader request, which bypasses server cache (like squid) and
 69+ checks the user parameter against $wgUser. The former adds more data to all
 70+ pages, while the latter adds a request which cannot be cached server side.
6671
6772 === New features in 1.17 ===
6873 * (bug 10183) Users can now add personal styles and scripts to all skins via

Follow-up revisions

RevisionCommit summaryAuthorDate
r74116Fixed mistake in r73686 where I wrapped CSS in a JavaScript conditional.tparscal22:13, 1 October 2010
r80583Followup r73686: make private modules really private (i.e. Cache-Control: pri...catrope19:31, 19 January 2011

Past revisions this follows-up on

RevisionCommit summaryAuthorDate
r73673* Moved registration generation to startup module....tparscal18:49, 24 September 2010

Comments

#Comment by Nikerabbit (talk | contribs)   21:13, 25 September 2010

Not sure if it is this revision or earlier:

/w/load.php?debug=false&lang=en&modules=translate-css&only=styles&skin=modern&version=1970-01-01T00%3A00%3A00Z: Exception: DatabaseBase::makeList: empty input
#0 /www/w/includes/db/Database.php(1067): DatabaseBase->makeList(Array, 1)
#1 /www/w/includes/db/Database.php(1020): DatabaseBase->selectSQLText('module_deps', Array, Array, 'ResourceLoader:...', Array, Array)
#2 /www/w/includes/ResourceLoader.php(75): DatabaseBase->select('module_deps', Array, Array, 'ResourceLoader:...')
#3 /www/w/includes/ResourceLoader.php(294): ResourceLoader::preloadModuleInfo(Array, Object(ResourceLoaderContext))
#4 /www/w/load.php(48): ResourceLoader::respond(Object(ResourceLoaderContext))
#5 {main}
#Comment by Trevor Parscal (WMF) (talk | contribs)   20:08, 27 September 2010

Given that that URL works without errors, I assume this is resolved?

#Comment by Nikerabbit (talk | contribs)   20:28, 27 September 2010

Nope unless something changed. It seems to be dependent on something else too, since I was never able to reproduce it accessing the same url afterwards.

#Comment by Brion VIBBER (talk | contribs)   02:16, 3 October 2010

r74139 resolves this; error occurred when we got down that far but had no modules to work with.

#Comment by Catrope (talk | contribs)   21:39, 1 October 2010
+					$links .= Html::inlineStyle(
+						ResourceLoader::makeLoaderConditionalScript(
+							ResourceLoader::makeModuleResponse( $context, $modules )
+						)
+					);

You can't use makeLoaderConditionalScript in combination with inlineStyle.

#Comment by Trevor Parscal (WMF) (talk | contribs)   22:14, 1 October 2010

That's just plain embarrassing... r74116 redeems me, a little bit.

Status & tagging log