Index: branches/REL1_19/phase3/RELEASE-NOTES-1.19 |
— | — | @@ -248,6 +248,8 @@ |
249 | 249 | around a bug where not all styles were applied in Internet Explorer |
250 | 250 | * (bug 28936, bug 5280) Broken or invalid titles can't be removed from watchlist.* (Bug 33087) Exchange server rejected mail sent by MediaWiki |
251 | 251 | * (bug 34600) Older skins using useHeadElement=false were broken in 1.18 |
| 252 | +* (bug 34604) [mw.config] wgActionPaths should be an object instead of a numeral |
| 253 | + array. |
252 | 254 | |
253 | 255 | === API changes in 1.19 === |
254 | 256 | * Made action=edit less likely to return "unknownerror", by returning the actual error |
Index: branches/REL1_19/phase3/tests/phpunit/includes/PathRouterTest.php |
— | — | @@ -125,6 +125,16 @@ |
126 | 126 | } |
127 | 127 | |
128 | 128 | /** |
| 129 | + * Test to ensure that matches are not made if a parameter expects nonexistent input |
| 130 | + */ |
| 131 | + public function testFail() { |
| 132 | + $router = new PathRouter; |
| 133 | + $router->add( "/wiki/$1", array( 'title' => "$1$2" ) ); |
| 134 | + $matches = $router->parse( "/wiki/A" ); |
| 135 | + $this->assertEquals( array(), $matches ); |
| 136 | + } |
| 137 | + |
| 138 | + /** |
129 | 139 | * Test to ensure weight of paths is handled correctly |
130 | 140 | */ |
131 | 141 | public function testWeight() { |
— | — | @@ -172,12 +182,30 @@ |
173 | 183 | $this->assertEquals( $matches, array( 'title' => "Title_With Space" ) ); |
174 | 184 | } |
175 | 185 | |
| 186 | + public function dataRegexpChars() { |
| 187 | + return array( |
| 188 | + array( "$" ), |
| 189 | + array( "$1" ), |
| 190 | + array( "\\" ), |
| 191 | + array( "\\$1" ), |
| 192 | + ); |
| 193 | + } |
| 194 | + |
176 | 195 | /** |
| 196 | + * Make sure the router doesn't break on special characters like $ used in regexp replacements |
| 197 | + * @dataProvider dataRegexpChars |
| 198 | + */ |
| 199 | + public function testRegexpChars( $char ) { |
| 200 | + $matches = $this->basicRouter->parse( "/wiki/$char" ); |
| 201 | + $this->assertEquals( $matches, array( 'title' => "$char" ) ); |
| 202 | + } |
| 203 | + |
| 204 | + /** |
177 | 205 | * Make sure the router handles characters like +&() properly |
178 | 206 | */ |
179 | 207 | public function testCharacters() { |
180 | | - $matches = $this->basicRouter->parse( "/wiki/Plus+And&Stuff()" ); |
181 | | - $this->assertEquals( $matches, array( 'title' => "Plus+And&Stuff()" ) ); |
| 208 | + $matches = $this->basicRouter->parse( "/wiki/Plus+And&Dollar\\Stuff();[]{}*" ); |
| 209 | + $this->assertEquals( $matches, array( 'title' => "Plus+And&Dollar\\Stuff();[]{}*" ) ); |
182 | 210 | } |
183 | 211 | |
184 | 212 | /** |
Index: branches/REL1_19/phase3/includes/api/ApiParamInfo.php |
— | — | @@ -147,6 +147,9 @@ |
148 | 148 | $examples = $obj->getExamples(); |
149 | 149 | $retval['allexamples'] = array(); |
150 | 150 | if ( $examples !== false ) { |
| 151 | + if ( is_string( $examples ) ) { |
| 152 | + $examples = array( $examples ); |
| 153 | + } |
151 | 154 | foreach( $examples as $k => $v ) { |
152 | 155 | if ( strlen( $retval['examples'] ) ) { |
153 | 156 | $retval['examples'] .= ' '; |
Property changes on: branches/REL1_19/phase3/includes/api |
___________________________________________________________________ |
Modified: svn:mergeinfo |
154 | 157 | Merged /trunk/phase3/includes/api:r112290,112313 |
Index: branches/REL1_19/phase3/includes/resourceloader/ResourceLoaderStartUpModule.php |
— | — | @@ -68,7 +68,9 @@ |
69 | 69 | 'wgScriptExtension' => $wgScriptExtension, |
70 | 70 | 'wgScript' => $wgScript, |
71 | 71 | 'wgVariantArticlePath' => $wgVariantArticlePath, |
72 | | - 'wgActionPaths' => $wgActionPaths, |
| 72 | + // Force object to avoid "empty" associative array from |
| 73 | + // becoming [] instead of {} in JS (bug 34604) |
| 74 | + 'wgActionPaths' => (object)$wgActionPaths, |
73 | 75 | 'wgServer' => $wgServer, |
74 | 76 | 'wgUserLanguage' => $context->getLanguage(), |
75 | 77 | 'wgContentLanguage' => $wgContLang->getCode(), |
Index: branches/REL1_19/phase3/includes/PathRouter.php |
— | — | @@ -278,20 +278,14 @@ |
279 | 279 | } elseif ( isset( $paramData['pattern'] ) ) { |
280 | 280 | // For patterns we have to make value replacements on the string |
281 | 281 | $value = $paramData['pattern']; |
282 | | - // For each $# match replace any $# within the value |
283 | | - foreach ( $m as $matchKey => $matchValue ) { |
284 | | - if ( preg_match( '/^par\d+$/u', $matchKey ) ) { |
285 | | - $n = intval( substr( $matchKey, 3 ) ); |
286 | | - $value = str_replace( '$' . $n, rawurldecode( $matchValue ), $value ); |
287 | | - } |
288 | | - } |
289 | | - // If a key was set replace any $key within the value |
| 282 | + $replacer = new PathRouterPatternReplacer; |
| 283 | + $replacer->params = $m; |
290 | 284 | if ( isset( $pattern->key ) ) { |
291 | | - $value = str_replace( '$key', $pattern->key, $value ); |
| 285 | + $replacer->key = $pattern->key; |
292 | 286 | } |
293 | | - if ( preg_match( '/\$(\d+|key)/u', $value ) ) { |
294 | | - // Still contains $# or $key patterns after replacement |
295 | | - // Seams like we don't have all the data, abort |
| 287 | + $value = $replacer->replace( $value ); |
| 288 | + if ( $value === false ) { |
| 289 | + // Pattern required data that wasn't available, abort |
296 | 290 | return null; |
297 | 291 | } |
298 | 292 | } |
— | — | @@ -317,3 +311,41 @@ |
318 | 312 | } |
319 | 313 | |
320 | 314 | } |
| 315 | + |
| 316 | +class PathRouterPatternReplacer { |
| 317 | + |
| 318 | + public $key, $params, $error; |
| 319 | + |
| 320 | + /** |
| 321 | + * Replace keys inside path router patterns with text. |
| 322 | + * We do this inside of a replacement callback because after replacement we can't tell the |
| 323 | + * difference between a $1 that was not replaced and a $1 that was part of |
| 324 | + * the content a $1 was replaced with. |
| 325 | + */ |
| 326 | + public function replace( $value ) { |
| 327 | + $this->error = false; |
| 328 | + $value = preg_replace_callback( '/\$(\d+|key)/u', array( $this, 'callback' ), $value ); |
| 329 | + if ( $this->error ) { |
| 330 | + return false; |
| 331 | + } |
| 332 | + return $value; |
| 333 | + } |
| 334 | + |
| 335 | + protected function callback( $m ) { |
| 336 | + if ( $m[1] == "key" ) { |
| 337 | + if ( is_null( $this->key ) ) { |
| 338 | + $this->error = true; |
| 339 | + return ''; |
| 340 | + } |
| 341 | + return $this->key; |
| 342 | + } else { |
| 343 | + $d = $m[1]; |
| 344 | + if ( !isset( $this->params["par$d"] ) ) { |
| 345 | + $this->error = true; |
| 346 | + return ''; |
| 347 | + } |
| 348 | + return rawurldecode( $this->params["par$d"] ); |
| 349 | + } |
| 350 | + } |
| 351 | + |
| 352 | +} |
\ No newline at end of file |
Index: branches/REL1_19/phase3/includes/AutoLoader.php |
— | — | @@ -162,6 +162,7 @@ |
163 | 163 | 'Pager' => 'includes/Pager.php', |
164 | 164 | 'PasswordError' => 'includes/User.php', |
165 | 165 | 'PathRouter' => 'includes/PathRouter.php', |
| 166 | + 'PathRouterPatternReplacer' => 'includes/PathRouter.php', |
166 | 167 | 'PermissionsError' => 'includes/Exception.php', |
167 | 168 | 'PhpHttpRequest' => 'includes/HttpFunctions.php', |
168 | 169 | 'PoolCounter' => 'includes/PoolCounter.php', |
Property changes on: branches/REL1_19/phase3/includes/AutoLoader.php |
___________________________________________________________________ |
Modified: svn:mergeinfo |
169 | 170 | Merged /trunk/phase3/includes/AutoLoader.php:r112313 |
Property changes on: branches/REL1_19/phase3/includes |
___________________________________________________________________ |
Modified: svn:mergeinfo |
170 | 171 | Merged /trunk/phase3/includes:r112184,112290,112313 |
Index: branches/REL1_19/phase3/resources/Resources.php |
— | — | @@ -554,6 +554,7 @@ |
555 | 555 | ), |
556 | 556 | 'mediawiki.feedback' => array( |
557 | 557 | 'scripts' => 'resources/mediawiki/mediawiki.feedback.js', |
| 558 | + 'styles' => 'resources/mediawiki/mediawiki.feedback.css', |
558 | 559 | 'dependencies' => array( |
559 | 560 | 'mediawiki.api.edit', |
560 | 561 | 'mediawiki.Title', |
Index: branches/REL1_19/phase3/resources/mediawiki/mediawiki.feedback.js |
— | — | @@ -106,7 +106,7 @@ |
107 | 107 | $( '<div class="feedback-mode feedback-submitting" style="text-align:center;margin:3em 0;"></div>' ).append( |
108 | 108 | mw.msg( 'feedback-adding' ), |
109 | 109 | $( '<br/>' ), |
110 | | - $( '<img src="http://upload.wikimedia.org/wikipedia/commons/4/42/Loading.gif" />' ) |
| 110 | + $( '<span class="feedback-spinner"></span>' ) |
111 | 111 | ), |
112 | 112 | $( '<div class="feedback-mode feedback-thanks" style="text-align:center;margin:1em"></div>' ).msg( |
113 | 113 | 'feedback-thanks', _this.title.getNameText(), $feedbackPageLink.clone() |
Index: branches/REL1_19/phase3/resources/mediawiki/mediawiki.feedback.spinner.gif |
Cannot display: file marked as a binary type. |
svn:mime-type = image/gif |
Property changes on: branches/REL1_19/phase3/resources/mediawiki/mediawiki.feedback.spinner.gif |
___________________________________________________________________ |
Added: svn:mime-type |
114 | 114 | + image/gif |
Index: branches/REL1_19/phase3/resources/mediawiki/mediawiki.util.js |
— | — | @@ -2,7 +2,7 @@ |
3 | 3 | * Implements mediaWiki.util library |
4 | 4 | */ |
5 | 5 | ( function ( $, mw ) { |
6 | | -"use strict"; |
| 6 | + "use strict"; |
7 | 7 | |
8 | 8 | // Local cache and alias |
9 | 9 | var util = { |
— | — | @@ -121,19 +121,19 @@ |
122 | 122 | * @param str string String to be encoded |
123 | 123 | */ |
124 | 124 | wikiUrlencode: function ( str ) { |
125 | | - return this.rawurlencode( str ) |
| 125 | + return util.rawurlencode( str ) |
126 | 126 | .replace( /%20/g, '_' ).replace( /%3A/g, ':' ).replace( /%2F/g, '/' ); |
127 | 127 | }, |
128 | 128 | |
129 | 129 | /** |
130 | 130 | * Get the link to a page name (relative to wgServer) |
131 | 131 | * |
132 | | - * @param str string Page name to get the link for. |
133 | | - * @return string Location for a page with name of 'str' or boolean false on error. |
| 132 | + * @param str String: Page name to get the link for. |
| 133 | + * @return String: Location for a page with name of 'str' or boolean false on error. |
134 | 134 | */ |
135 | 135 | wikiGetlink: function ( str ) { |
136 | 136 | return mw.config.get( 'wgArticlePath' ).replace( '$1', |
137 | | - this.wikiUrlencode( str || mw.config.get( 'wgPageName' ) ) ); |
| 137 | + util.wikiUrlencode( typeof str === 'string' ? str : mw.config.get( 'wgPageName' ) ) ); |
138 | 138 | }, |
139 | 139 | |
140 | 140 | /** |
— | — | @@ -384,7 +384,7 @@ |
385 | 385 | $link.attr( 'title', tooltip ); |
386 | 386 | } |
387 | 387 | if ( accesskey && tooltip ) { |
388 | | - this.updateTooltipAccessKeys( $link ); |
| 388 | + util.updateTooltipAccessKeys( $link ); |
389 | 389 | } |
390 | 390 | |
391 | 391 | // Where to put our node ? |
Index: branches/REL1_19/phase3/resources/mediawiki/mediawiki.feedback.css |
— | — | @@ -0,0 +1,9 @@ |
| 2 | +.feedback-spinner { |
| 3 | + display: inline-block; |
| 4 | + zoom: 1; |
| 5 | + *display: inline; /* IE7 and below */ |
| 6 | + /* @embed */ |
| 7 | + background: url(mediawiki.feedback.spinner.gif); |
| 8 | + width: 18px; |
| 9 | + height: 18px; |
| 10 | +} |
Property changes on: branches/REL1_19/phase3/resources/mediawiki/mediawiki.feedback.css |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 11 | + native |
Property changes on: branches/REL1_19/phase3 |
___________________________________________________________________ |
Modified: svn:mergeinfo |
2 | 12 | Merged /trunk/phase3:r112169-112170,112172-112173,112179,112184,112290,112313 |