Index: trunk/phase3/includes/ResourceLoaderContext.php |
— | — | @@ -33,6 +33,7 @@ |
34 | 34 | protected $user; |
35 | 35 | protected $debug; |
36 | 36 | protected $only; |
| 37 | + protected $version; |
37 | 38 | protected $hash; |
38 | 39 | |
39 | 40 | /* Methods */ |
— | — | @@ -49,6 +50,7 @@ |
50 | 51 | $this->user = $request->getVal( 'user' ); |
51 | 52 | $this->debug = $request->getBool( 'debug' ) && $request->getVal( 'debug' ) === 'true'; |
52 | 53 | $this->only = $request->getVal( 'only' ); |
| 54 | + $this->version = $request->getVal( 'version' ); |
53 | 55 | |
54 | 56 | // Fallback on system defaults |
55 | 57 | if ( !$this->language ) { |
— | — | @@ -63,7 +65,7 @@ |
64 | 66 | $this->skin = $wgDefaultSkin; |
65 | 67 | } |
66 | 68 | } |
67 | | - |
| 69 | + |
68 | 70 | public function getRequest() { |
69 | 71 | return $this->request; |
70 | 72 | } |
— | — | @@ -96,6 +98,10 @@ |
97 | 99 | return $this->only; |
98 | 100 | } |
99 | 101 | |
| 102 | + public function getVersion() { |
| 103 | + return $this->version; |
| 104 | + } |
| 105 | + |
100 | 106 | public function shouldIncludeScripts() { |
101 | 107 | return is_null( $this->only ) || $this->only === 'scripts'; |
102 | 108 | } |
— | — | @@ -111,7 +117,7 @@ |
112 | 118 | public function getHash() { |
113 | 119 | return isset( $this->hash ) ? |
114 | 120 | $this->hash : $this->hash = implode( '|', array( |
115 | | - $this->language, $this->direction, $this->skin, $this->user, $this->debug, $this->only |
| 121 | + $this->language, $this->direction, $this->skin, $this->user, $this->debug, $this->only, $this->version |
116 | 122 | ) ); |
117 | 123 | } |
118 | 124 | } |
\ No newline at end of file |
Index: trunk/phase3/includes/OutputPage.php |
— | — | @@ -2302,14 +2302,25 @@ |
2303 | 2303 | if ( $group === 'user' ) { |
2304 | 2304 | $query['user'] = $wgUser->getName(); |
2305 | 2305 | } |
2306 | | - $context = new ResourceLoaderContext( new FauxRequest( $query ) ); |
| 2306 | + // Users might change their stuff on-wiki like site or user pages, or user preferences; we need to find |
| 2307 | + // the highest timestamp of these user-changable modules so we can ensure cache misses upon change |
2307 | 2308 | $timestamp = 0; |
2308 | 2309 | foreach ( $modules as $name ) { |
2309 | | - if ( $module = ResourceLoader::getModule( $name ) ) { |
2310 | | - $timestamp = max( $timestamp, $module->getModifiedTime( $context ) ); |
| 2310 | + $module = ResourceLoader::getModule( $name ); |
| 2311 | + if ( |
| 2312 | + $module instanceof ResourceLoaderWikiModule || |
| 2313 | + $module instanceof ResourceLoaderUserPreferencesModule |
| 2314 | + ) { |
| 2315 | + $timestamp = max( |
| 2316 | + $timestamp, |
| 2317 | + $module->getModifiedTime( new ResourceLoaderContext( new FauxRequest( $query ) ) ) |
| 2318 | + ); |
2311 | 2319 | } |
2312 | 2320 | } |
2313 | | - $query['version'] = wfTimestamp( TS_ISO_8601, round( $timestamp, -2 ) ); |
| 2321 | + // Add a version parameter if any of the modules were user-changable |
| 2322 | + if ( $timestamp ) { |
| 2323 | + $query['version'] = wfTimestamp( TS_ISO_8601, round( $timestamp, -2 ) ); |
| 2324 | + } |
2314 | 2325 | // Make queries uniform in order |
2315 | 2326 | ksort( $query ); |
2316 | 2327 | // Automatically select style/script elements |
Index: trunk/phase3/includes/ResourceLoader.php |
— | — | @@ -24,6 +24,7 @@ |
25 | 25 | * Dynamic JavaScript and CSS resource loading system |
26 | 26 | */ |
27 | 27 | class ResourceLoader { |
| 28 | + |
28 | 29 | /* Protected Static Members */ |
29 | 30 | |
30 | 31 | // @var array list of module name/ResourceLoaderModule object pairs |
— | — | @@ -188,6 +189,9 @@ |
189 | 190 | * @param $context ResourceLoaderContext object |
190 | 191 | */ |
191 | 192 | public static function respond( ResourceLoaderContext $context ) { |
| 193 | + global $wgResourceLoaderVersionedClientMaxage, $wgResourceLoaderVersionedServerMaxage; |
| 194 | + global $wgResourceLoaderUnversionedServerMaxage, $wgResourceLoaderUnversionedClientMaxage; |
| 195 | + |
192 | 196 | // Split requested modules into two groups, modules and missing |
193 | 197 | $modules = array(); |
194 | 198 | $missing = array(); |
— | — | @@ -200,33 +204,31 @@ |
201 | 205 | } |
202 | 206 | } |
203 | 207 | |
204 | | - // Calculate the mtime and caching maxages for this request. We need this, 304 or no 304 |
| 208 | + // If a version wasn't specified we need a shorter expiry time for updates to propagate to clients quickly |
| 209 | + if ( is_null( $context->getVersion() ) ) { |
| 210 | + $maxage = $wgResourceLoaderUnversionedClientMaxage; |
| 211 | + $smaxage = $wgResourceLoaderUnversionedServerMaxage; |
| 212 | + } |
| 213 | + // If a version was specified we can use a longer expiry time since changing version numbers causes cache misses |
| 214 | + else { |
| 215 | + $maxage = $wgResourceLoaderVersionedClientMaxage; |
| 216 | + $smaxage = $wgResourceLoaderVersionedServerMaxage; |
| 217 | + } |
| 218 | + |
| 219 | + // To send Last-Modified and support If-Modified-Since, we need to detect the last modified time |
205 | 220 | $mtime = 1; |
206 | | - $maxage = PHP_INT_MAX; |
207 | | - $smaxage = PHP_INT_MAX; |
208 | | - |
209 | 221 | foreach ( $modules as $name ) { |
210 | 222 | $mtime = max( $mtime, self::$modules[$name]->getModifiedTime( $context ) ); |
211 | | - $maxage = min( $maxage, self::$modules[$name]->getClientMaxage() ); |
212 | | - $smaxage = min( $smaxage, self::$modules[$name]->getServerMaxage() ); |
213 | 223 | } |
214 | 224 | |
215 | | - // Output headers |
216 | | - if ( $context->getOnly() === 'styles' ) { |
217 | | - header( 'Content-Type: text/css' ); |
218 | | - } else { |
219 | | - header( 'Content-Type: text/javascript' ); |
220 | | - } |
221 | | - |
| 225 | + header( 'Content-Type: ' . ( $context->getOnly() === 'styles' ? 'text/css' : 'text/javascript' ) ); |
222 | 226 | header( 'Last-Modified: ' . wfTimestamp( TS_RFC2822, $mtime ) ); |
223 | | - $expires = wfTimestamp( TS_RFC2822, min( $maxage, $smaxage ) + time() ); |
224 | 227 | header( "Cache-Control: public, max-age=$maxage, s-maxage=$smaxage" ); |
225 | | - header( "Expires: $expires" ); |
| 228 | + header( 'Expires: ' . wfTimestamp( TS_RFC2822, min( $maxage, $smaxage ) + time() ) ); |
226 | 229 | |
227 | | - // Check if there's an If-Modified-Since header and respond with a 304 Not Modified if possible |
| 230 | + // If there's an If-Modified-Since header, respond with a 304 appropriately |
228 | 231 | $ims = $context->getRequest()->getHeader( 'If-Modified-Since' ); |
229 | | - |
230 | | - if ( $ims !== false && wfTimestamp( TS_UNIX, $ims ) == $mtime ) { |
| 232 | + if ( $ims !== false && $mtime >= wfTimestamp( TS_UNIX, $ims ) ) { |
231 | 233 | header( 'HTTP/1.0 304 Not Modified' ); |
232 | 234 | header( 'Status: 304 Not Modified' ); |
233 | 235 | return; |
Index: trunk/phase3/includes/DefaultSettings.php |
— | — | @@ -1645,19 +1645,32 @@ |
1646 | 1646 | $wgClockSkewFudge = 5; |
1647 | 1647 | |
1648 | 1648 | /** |
1649 | | - * Maximum time in seconds to cache resources served by the resource loader on |
1650 | | - * the client side (e.g. in the browser cache). |
| 1649 | + * Maximum time in seconds to cache versioned resources served by the resource |
| 1650 | + * loader on the client side (e.g. in the browser cache). |
1651 | 1651 | */ |
1652 | | -$wgResourceLoaderClientMaxage = 30*24*60*60; // 30 days |
| 1652 | +$wgResourceLoaderVersionedClientMaxage = 30 * 24 * 60 * 60; // 30 days |
1653 | 1653 | |
1654 | 1654 | /** |
1655 | | - * Maximum time in seconds to cache resources served by the resource loader on |
1656 | | - * the server side. This means Squid/Varnish but also any other public proxy |
1657 | | - * cache between the client and MediaWiki. |
| 1655 | + * Maximum time in seconds to cache versioned resources served by the resource |
| 1656 | + * loader on the server side. This means Squid/Varnish but also any other public |
| 1657 | + * proxy cache between the client and MediaWiki. |
1658 | 1658 | */ |
1659 | | -$wgResourceLoaderServerMaxage = 30*24*60*60; // 30 days |
| 1659 | +$wgResourceLoaderVersionedServerMaxage = 30 * 24 * 60 * 60; // 30 days |
1660 | 1660 | |
1661 | 1661 | /** |
| 1662 | + * Maximum time in seconds to cache unversioned resources served by the resource |
| 1663 | + * loader on the client. |
| 1664 | + */ |
| 1665 | +$wgResourceLoaderUnversionedClientMaxage = 5 * 60; // 5 minutes |
| 1666 | + |
| 1667 | +/** |
| 1668 | + * Maximum time in seconds to cache unversioned resources served by the resource |
| 1669 | + * loader on the server. This means Squid/Varnish but also any other public |
| 1670 | + * proxy cache between the client and MediaWiki. |
| 1671 | + */ |
| 1672 | +$wgResourceLoaderUnversionedServerMaxage = 5 * 60; // 5 minutes |
| 1673 | + |
| 1674 | +/** |
1662 | 1675 | * Enable data URL embedding (experimental). This variable is very temporary and |
1663 | 1676 | * will be removed once we get this feature stable. |
1664 | 1677 | */ |
Index: trunk/phase3/includes/ResourceLoaderModule.php |
— | — | @@ -51,30 +51,6 @@ |
52 | 52 | } |
53 | 53 | |
54 | 54 | /** |
55 | | - * The maximum number of seconds to cache this module for in the |
56 | | - * client-side (browser) cache. Override this only if you have a good |
57 | | - * reason not to use $wgResourceLoaderClientMaxage. |
58 | | - * |
59 | | - * @return Integer: cache maxage in seconds |
60 | | - */ |
61 | | - public function getClientMaxage() { |
62 | | - global $wgResourceLoaderClientMaxage; |
63 | | - return $wgResourceLoaderClientMaxage; |
64 | | - } |
65 | | - |
66 | | - /** |
67 | | - * The maximum number of seconds to cache this module for in the |
68 | | - * server-side (Squid / proxy) cache. Override this only if you have a |
69 | | - * good reason not to use $wgResourceLoaderServerMaxage. |
70 | | - * |
71 | | - * @return Integer: cache maxage in seconds |
72 | | - */ |
73 | | - public function getServerMaxage() { |
74 | | - global $wgResourceLoaderServerMaxage; |
75 | | - return $wgResourceLoaderServerMaxage; |
76 | | - } |
77 | | - |
78 | | - /** |
79 | 55 | * Get whether CSS for this module should be flipped |
80 | 56 | */ |
81 | 57 | public function getFlip( $context ) { |
— | — | @@ -1013,14 +989,6 @@ |
1014 | 990 | return $this->modifiedTime[$hash]; |
1015 | 991 | } |
1016 | 992 | |
1017 | | - public function getClientMaxage() { |
1018 | | - return 300; // 5 minutes |
1019 | | - } |
1020 | | - |
1021 | | - public function getServerMaxage() { |
1022 | | - return 300; // 5 minutes |
1023 | | - } |
1024 | | - |
1025 | 993 | public function getFlip( $context ) { |
1026 | 994 | global $wgContLang; |
1027 | 995 | |
Index: trunk/phase3/RELEASE-NOTES |
— | — | @@ -54,7 +54,14 @@ |
55 | 55 | $wgUseMemCached, $wgDisableSearchContext, $wgColorErrors, |
56 | 56 | $wgUseZhdaemon, $wgZhdaemonHost and $wgZhdaemonPort. |
57 | 57 | * (bug 24408) The include_path is not modified in the default LocalSettings.php |
58 | | -* $wgVectorExtraStyles has been removed, and is no longer in use. |
| 58 | +* $wgVectorExtraStyles was removed, and is no longer in use. |
| 59 | +* $wgLoadScript was added to specify alternative locations for ResourceLoader |
| 60 | + requests. |
| 61 | +* $wgResourceLoaderVersionedClientMaxage, $wgResourceLoaderVersionedServerMaxage, |
| 62 | + $wgResourceLoaderUnversionedClientMaxage and |
| 63 | + wgResourceLoaderUnversionedServerMaxage were added to specify maxage and |
| 64 | + smaxage times for responses from ResourceLoader based on whether the request's |
| 65 | + URL contained a version parameter or not. |
59 | 66 | |
60 | 67 | === New features in 1.17 === |
61 | 68 | * (bug 10183) Users can now add personal styles and scripts to all skins via |