From 99ff8ef729dd9f040aea9d6aacb89155188db3d0 Mon Sep 17 00:00:00 2001 From: Fomafix Date: Mon, 21 Dec 2015 06:17:11 +0000 Subject: [PATCH] mediawiki.util: Use correct encoding for fragment in getUrl Introduce mw.util.escapeId with same encoding as Sanitizer::escapeId. Add more tests to getUrl: * empty title * several characters for encoding test Bug: T103553 Bug: T103661 Change-Id: I15d8c48b9ea4a144a0938353c5b265cb6b6a159b --- resources/src/mediawiki/mediawiki.util.js | 24 +++++++++--- .../mediawiki/mediawiki.util.test.js | 39 ++++++++++++++++++- 2 files changed, 56 insertions(+), 7 deletions(-) diff --git a/resources/src/mediawiki/mediawiki.util.js b/resources/src/mediawiki/mediawiki.util.js index 7b45248407..f9810f9aa8 100644 --- a/resources/src/mediawiki/mediawiki.util.js +++ b/resources/src/mediawiki/mediawiki.util.js @@ -58,6 +58,18 @@ .replace( /\)/g, '%29' ).replace( /\*/g, '%2A' ).replace( /~/g, '%7E' ); }, + /** + * Encode the string like Sanitizer::escapeId in PHP + * + * @param {string} str String to be encoded. + */ + escapeId: function ( str ) { + str = String( str ); + return util.rawurlencode( str.replace( / /g, '_' ) ) + .replace( /%3A/g, ':' ) + .replace( /%/g, '.' ); + }, + /** * Encode page titles for use in a URL * @@ -98,28 +110,28 @@ var titleFragmentStart, url, fragment = '', - pageName = util.wikiUrlencode( typeof str === 'string' ? str : mw.config.get( 'wgPageName' ) ); + pageName = typeof str === 'string' ? str : mw.config.get( 'wgPageName' ); // Find any fragment should one exist if ( typeof str === 'string' ) { - titleFragmentStart = pageName.indexOf( '%23' ); + titleFragmentStart = pageName.indexOf( '#' ); if ( titleFragmentStart !== -1 ) { - fragment = pageName.slice( titleFragmentStart + 3 ); + fragment = pageName.slice( titleFragmentStart + 1 ); // Exclude the fragment from the page name pageName = pageName.slice( 0, titleFragmentStart ); } } - url = mw.config.get( 'wgArticlePath' ).replace( '$1', pageName ); + url = mw.config.get( 'wgArticlePath' ).replace( '$1', util.wikiUrlencode( pageName ) ); // Add query string if necessary if ( params && !$.isEmptyObject( params ) ) { url += ( url.indexOf( '?' ) !== -1 ? '&' : '?' ) + $.param( params ); } - // Insert the already URL-encoded fragment should it exist, replacing % with . + // Append the encoded fragment if ( fragment.length > 0 ) { - url += '#' + fragment.replace( /%/g, '.' ); + url += '#' + util.escapeId( fragment ); } return url; diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.util.test.js b/tests/qunit/suites/resources/mediawiki/mediawiki.util.test.js index 75dd8cdbb0..0b981065ad 100644 --- a/tests/qunit/suites/resources/mediawiki/mediawiki.util.test.js +++ b/tests/qunit/suites/resources/mediawiki/mediawiki.util.test.js @@ -92,6 +92,31 @@ assert.equal( mw.util.rawurlencode( 'Test:A & B/Here' ), 'Test%3AA%20%26%20B%2FHere' ); } ); + QUnit.test( 'escapeId', 17, function ( assert ) { + mw.config.set( 'wgExperimentalHtmlIds', false ); + $.each( { + '+': '.2B', + '&': '.26', + '=': '.3D', + ':': ':', + ';': '.3B', + '@': '.40', + $: '.24', + '-_.': '-_.', + '!': '.21', + '*': '.2A', + '/': '.2F', + '[]': '.5B.5D', + '<>': '.3C.3E', + '\'': '.27', + '§': '.C2.A7', + 'Test:A & B/Here': 'Test:A_.26_B.2FHere', + 'A&B&C&amp;D&amp;amp;E': 'A.26B.26amp.3BC.26amp.3Bamp.3BD.26amp.3Bamp.3Bamp.3BE' + }, function ( input, output ) { + assert.equal( mw.util.escapeId( input ), output ); + } ); + } ); + QUnit.test( 'wikiUrlencode', 11, function ( assert ) { assert.equal( mw.util.wikiUrlencode( 'Test:A & B/Here' ), 'Test:A_%26_B/Here' ); // See also wfUrlencodeTest.php#provideURLS @@ -111,7 +136,7 @@ } ); } ); - QUnit.test( 'getUrl', 8, function ( assert ) { + QUnit.test( 'getUrl', 12, function ( assert ) { // Not part of startUp module mw.config.set( 'wgArticlePath', '/wiki/$1' ); mw.config.set( 'wgPageName', 'Foobar' ); @@ -135,11 +160,23 @@ href = mw.util.getUrl( 'Foo:Sandbox#Fragment', { action: 'edit' } ); assert.equal( href, '/wiki/Foo:Sandbox?action=edit#Fragment', 'advanced title with query string and fragment' ); + href = mw.util.getUrl( 'Foo:Sandbox#', { action: 'edit' } ); + assert.equal( href, '/wiki/Foo:Sandbox?action=edit', 'title with query string and empty fragment' ); + + href = mw.util.getUrl( '#Fragment' ); + assert.equal( href, '/wiki/#Fragment', 'epmty title with fragment' ); + + href = mw.util.getUrl( '#Fragment', { action: 'edit' } ); + assert.equal( href, '/wiki/?action=edit#Fragment', 'epmty title with query string and fragment' ); + href = mw.util.getUrl( 'Foo:Sandbox \xC4#Fragment \xC4', { action: 'edit' } ); assert.equal( href, '/wiki/Foo:Sandbox_%C3%84?action=edit#Fragment_.C3.84', 'title with query string, fragment, and special characters' ); href = mw.util.getUrl( 'Foo:%23#Fragment', { action: 'edit' } ); assert.equal( href, '/wiki/Foo:%2523?action=edit#Fragment', 'title containing %23 (#), fragment, and a query string' ); + + href = mw.util.getUrl( '#+&=:;@$-_.!*/[]<>\'§', { action: 'edit' } ); + assert.equal( href, '/wiki/?action=edit#.2B.26.3D:.3B.40.24-_..21.2A.2F.5B.5D.3C.3E.27.C2.A7', 'fragment with various characters' ); } ); QUnit.test( 'wikiScript', 4, function ( assert ) { -- 2.20.1