Merge "Fix encoding to roundtrip links without 'rawTitle'/'origTitle'"

This commit is contained in:
jenkins-bot 2023-01-09 18:42:47 +00:00 committed by Gerrit Code Review
commit 56bc2bb813
10 changed files with 58 additions and 106 deletions

View file

@ -57,8 +57,7 @@ ve.dm.MWInternalLinkAnnotation.static.toDataElement = function ( domElements, co
attributes: {
title: targetData.title,
normalizedTitle: this.normalizeTitle( targetData.title ),
lookupTitle: this.getLookupTitle( targetData.title ),
origTitle: targetData.rawTitle
lookupTitle: this.getLookupTitle( targetData.title )
}
};
};
@ -67,10 +66,9 @@ ve.dm.MWInternalLinkAnnotation.static.toDataElement = function ( domElements, co
* Build element from a given mw.Title and raw title
*
* @param {mw.Title} title The title to link to.
* @param {string} [rawTitle] String from which the title was created
* @return {Object} The element.
*/
ve.dm.MWInternalLinkAnnotation.static.dataElementFromTitle = function ( title, rawTitle ) {
ve.dm.MWInternalLinkAnnotation.static.dataElementFromTitle = function ( title ) {
var target = title.toText();
if ( title.getFragment() ) {
@ -86,10 +84,6 @@ ve.dm.MWInternalLinkAnnotation.static.dataElementFromTitle = function ( title, r
}
};
if ( rawTitle ) {
element.attributes.origTitle = rawTitle;
}
return element;
};
@ -97,11 +91,10 @@ ve.dm.MWInternalLinkAnnotation.static.dataElementFromTitle = function ( title, r
* Build a ve.dm.MWInternalLinkAnnotation from a given mw.Title.
*
* @param {mw.Title} title The title to link to.
* @param {string} [rawTitle] String from which the title was created
* @return {ve.dm.MWInternalLinkAnnotation} The annotation.
*/
ve.dm.MWInternalLinkAnnotation.static.newFromTitle = function ( title, rawTitle ) {
var element = this.dataElementFromTitle( title, rawTitle );
ve.dm.MWInternalLinkAnnotation.static.newFromTitle = function ( title ) {
var element = this.dataElementFromTitle( title );
return new ve.dm.MWInternalLinkAnnotation( element );
};
@ -114,31 +107,16 @@ ve.dm.MWInternalLinkAnnotation.static.toDomElements = function () {
};
ve.dm.MWInternalLinkAnnotation.static.getHref = function ( dataElement ) {
var encodedTitle,
title = dataElement.attributes.title,
origTitle = dataElement.attributes.origTitle;
if ( origTitle !== undefined && mw.libs.ve.decodeURIComponentIntoArticleTitle( origTitle ) === title ) {
// Restore href from origTitle
encodedTitle = origTitle;
} else {
// Don't escape slashes in the title; they represent subpages.
// Don't escape colons to work around a Parsoid bug with interwiki links (T95850)
// TODO: Maybe this should be using mw.util.wikiUrlencode(), which also doesn't escape them?
encodedTitle = title.split( /(\/|#|:)/ ).map( function ( part ) {
if ( part === '/' || part === '#' || part === ':' ) {
return part;
} else {
return encodeURIComponent( part );
}
} ).join( '' );
}
if ( encodedTitle.slice( 0, 1 ) === '#' ) {
var title = dataElement.attributes.title;
if ( title.slice( 0, 1 ) === '#' ) {
// Special case: For a newly created link to a #fragment with
// no explicit title use the current title as prefix (T218581)
// TODO: Pass a 'doc' param to getPageName
encodedTitle = ve.init.target.getPageName() + encodedTitle;
title = ve.init.target.getPageName() + title;
}
return './' + encodedTitle;
return mw.libs.ve.encodeParsoidResourceName( title );
};
/**

View file

@ -33,17 +33,14 @@ ve.dm.MWCategoryMetaItem.static.matchTagNames = [ 'link' ];
ve.dm.MWCategoryMetaItem.static.matchRdfaTypes = [ 'mw:PageProp/Category' ];
ve.dm.MWCategoryMetaItem.static.toDataElement = function ( domElements ) {
// Parsoid: LinkHandlerUtils::serializeAsWikiLink
var href = domElements[ 0 ].getAttribute( 'href' ),
data = mw.libs.ve.parseParsoidResourceName( href ),
rawTitleAndFragment = data.rawTitle.match( /^(.*?)(?:#(.*))?$/ ),
titleAndFragment = data.title.match( /^(.*?)(?:#(.*))?\s*$/ );
titleAndFragment = href.match( /^(.*?)(?:#(.*))?\s*$/ );
return {
type: this.name,
attributes: {
category: titleAndFragment[ 1 ],
origCategory: rawTitleAndFragment[ 1 ],
sortkey: titleAndFragment[ 2 ] || '',
origSortkey: rawTitleAndFragment[ 2 ] || ''
category: mw.libs.ve.parseParsoidResourceName( titleAndFragment[ 1 ] ).title,
sortkey: titleAndFragment[ 2 ] ? decodeURIComponent( titleAndFragment[ 2 ] ) : ''
}
};
};
@ -51,27 +48,17 @@ ve.dm.MWCategoryMetaItem.static.toDataElement = function ( domElements ) {
ve.dm.MWCategoryMetaItem.static.toDomElements = function ( dataElement, doc ) {
var domElement = doc.createElement( 'link' ),
category = dataElement.attributes.category || '',
sortkey = dataElement.attributes.sortkey || '',
origCategory = dataElement.attributes.origCategory || '',
origSortkey = dataElement.attributes.origSortkey || '',
normalizedOrigCategory = mw.libs.ve.decodeURIComponentIntoArticleTitle( origCategory ),
normalizedOrigSortkey = mw.libs.ve.decodeURIComponentIntoArticleTitle( origSortkey );
if ( normalizedOrigSortkey === sortkey ) {
sortkey = origSortkey;
} else {
sortkey = encodeURIComponent( sortkey );
}
var encodedCategory;
if ( normalizedOrigCategory === category ) {
encodedCategory = origCategory;
} else {
encodedCategory = encodeURIComponent( category );
}
sortkey = dataElement.attributes.sortkey || '';
domElement.setAttribute( 'rel', 'mw:PageProp/Category' );
var href = './' + encodedCategory;
// Parsoid: WikiLinkHandler::renderCategory
var href = mw.libs.ve.encodeParsoidResourceName( category );
if ( sortkey !== '' ) {
href += '#' + sortkey;
href += '#' + sortkey.replace( /[%? [\]#|<>]/g, function ( match ) {
return encodeURIComponent( match );
} );
}
domElement.setAttribute( 'href', href );
return [ domElement ];
};

View file

@ -83,7 +83,7 @@ ve.dm.MWBlockImageNode.static.toDataElement = function ( domElements, converter
// Otherwise Parsoid generates |link= options for copy-pasted images (T193253).
var targetData = mw.libs.ve.getTargetDataFromHref( href, converter.getTargetHtmlDocument() );
if ( targetData.isInternal ) {
href = './' + targetData.rawTitle;
href = mw.libs.ve.encodeParsoidResourceName( targetData.title );
}
}

View file

@ -71,7 +71,7 @@ ve.dm.MWInlineImageNode.static.toDataElement = function ( domElements, converter
// Otherwise Parsoid generates |link= options for copy-pasted images (T193253).
var targetData = mw.libs.ve.getTargetDataFromHref( href, converter.getTargetHtmlDocument() );
if ( targetData.isInternal ) {
href = './' + targetData.rawTitle;
href = mw.libs.ve.encodeParsoidResourceName( targetData.title );
}
}

View file

@ -265,8 +265,6 @@ mw.libs.ve.fixFragmentLinks = function ( container, docTitle, prefix ) {
* @return {Object} Information about the given href
* @return {string} [return.title]
* The title of the internal link (if the href is internal)
* @return {string} [return.rawTitle]
* The title without URL decoding and underscore normalization applied (if the href is internal)
* @return {boolean} return.isInternal
* True if the href pointed to the local wiki, false if href is external
*/
@ -353,13 +351,35 @@ mw.libs.ve.getTargetDataFromHref = function ( href, doc ) {
return data;
};
/**
* Encode a page title into a Parsoid resource name.
*
* @param {string} title
* @return {string}
*/
mw.libs.ve.encodeParsoidResourceName = function ( title ) {
// Parsoid: Sanitizer::sanitizeTitleURI, Env::makeLink
var idx = title.indexOf( '#' );
var anchor = null;
if ( idx !== -1 ) {
anchor = title.slice( idx + 1 );
title = title.slice( 0, idx );
}
var encodedTitle = title.replace( /[%? [\]#|<>]/g, function ( match ) {
return mw.util.wikiUrlencode( match );
} );
if ( anchor !== null ) {
encodedTitle += '#' + mw.util.escapeIdForLink( anchor );
}
return './' + encodedTitle;
};
/**
* Split Parsoid resource name into the href prefix and the page title.
*
* @param {string} resourceName Resource name, from a `href` or `resource` attribute
* @return {Object} Object with the following properties:
* @return {string} return.title Full page title in text form (with namespace, and spaces instead of underscores)
* @return {string} return.rawTitle The title without URL decoding and underscore normalization applied
*/
mw.libs.ve.parseParsoidResourceName = function ( resourceName ) {
// Resource names are always prefixed with './' to prevent the MediaWiki namespace from being
@ -369,8 +389,7 @@ mw.libs.ve.parseParsoidResourceName = function ( resourceName ) {
return {
// '%' and '?' are valid in page titles, but normally URI-encoded. This also changes underscores
// to spaces.
title: mw.libs.ve.decodeURIComponentIntoArticleTitle( matches[ 2 ] ),
rawTitle: matches[ 2 ]
title: mw.libs.ve.decodeURIComponentIntoArticleTitle( matches[ 2 ] )
};
};

View file

@ -41,7 +41,6 @@ QUnit.test( 'toDataElement', ( assert ) => {
attributes: {
lookupTitle: 'Foo',
normalizedTitle: 'Foo',
origTitle: 'Foo',
title: 'Foo'
}
}
@ -54,7 +53,6 @@ QUnit.test( 'toDataElement', ( assert ) => {
attributes: {
lookupTitle: 'Foo',
normalizedTitle: 'Foo',
origTitle: 'Foo',
title: 'Foo'
}
}
@ -87,7 +85,6 @@ QUnit.test( 'toDataElement', ( assert ) => {
attributes: {
lookupTitle: 'Foo',
normalizedTitle: 'Foo',
origTitle: 'Foo',
title: 'Foo'
}
}
@ -102,7 +99,6 @@ QUnit.test( 'toDataElement', ( assert ) => {
attributes: {
lookupTitle: 'Foo?',
normalizedTitle: 'Foo?',
origTitle: 'Foo?',
title: 'Foo?'
}
},
@ -111,7 +107,6 @@ QUnit.test( 'toDataElement', ( assert ) => {
attributes: {
lookupTitle: 'Foo?',
normalizedTitle: 'Foo?',
origTitle: 'Foo%3F',
title: 'Foo?'
}
}
@ -126,7 +121,6 @@ QUnit.test( 'toDataElement', ( assert ) => {
attributes: {
lookupTitle: 'Foo',
normalizedTitle: 'Foo#bar',
origTitle: 'Foo#bar',
title: 'Foo#bar'
}
}
@ -140,7 +134,6 @@ QUnit.test( 'toDataElement', ( assert ) => {
attributes: {
lookupTitle: 'Foo',
normalizedTitle: 'Foo#' + mw.util.escapeIdForLink( 'bar?' ),
origTitle: 'Foo#' + mw.util.escapeIdForLink( 'bar?' ),
title: 'Foo#' + mw.util.escapeIdForLink( 'bar?' )
}
}

View file

@ -226,7 +226,6 @@ ve.dm.mwExample.MWInternalLink.absoluteData = {
type: 'link/mwInternal',
attributes: {
title: 'Foo/Bar',
origTitle: 'Foo/Bar',
normalizedTitle: 'Foo/Bar',
lookupTitle: 'Foo/Bar'
}
@ -241,7 +240,6 @@ ve.dm.mwExample.MWInternalSectionLink.absoluteData = {
type: 'link/mwInternal',
attributes: {
title: 'Foo#Bar',
origTitle: 'Foo#Bar',
normalizedTitle: 'Foo#Bar',
lookupTitle: 'Foo'
}
@ -252,7 +250,6 @@ ve.dm.mwExample.MWMediaLinkExistsData = {
attributes: {
lookupTitle: 'Media:Exists.png',
normalizedTitle: 'Media:Exists.png',
origTitle: 'Media:Exists.png',
title: 'Media:Exists.png'
}
};
@ -262,7 +259,6 @@ ve.dm.mwExample.MWMediaLinkMissingData = {
attributes: {
lookupTitle: 'Media:Missing.png',
normalizedTitle: 'Media:Missing.png',
origTitle: 'Media:Missing.png',
title: 'Media:Missing.png'
}
};
@ -422,9 +418,7 @@ ve.dm.mwExample.withMeta = [
type: 'mwCategory',
attributes: {
category: 'Category:Bar',
origCategory: 'Category:Bar',
sortkey: '',
origSortkey: ''
sortkey: ''
}
},
{ type: '/mwCategory' },
@ -470,9 +464,7 @@ ve.dm.mwExample.withMeta = [
type: 'mwCategory',
attributes: {
category: 'Category:Foo foo',
origCategory: 'Category:Foo_foo',
sortkey: 'Bar baz#quux',
origSortkey: 'Bar baz%23quux'
sortkey: 'Bar baz#quux'
}
},
{ type: '/mwCategory' },
@ -527,9 +519,7 @@ ve.dm.mwExample.withMetaRealData = [
type: 'mwCategory',
attributes: {
category: 'Category:Bar',
origCategory: 'Category:Bar',
sortkey: '',
origSortkey: ''
sortkey: ''
}
},
{ type: '/mwCategory' },
@ -561,9 +551,7 @@ ve.dm.mwExample.withMetaRealData = [
type: 'mwCategory',
attributes: {
category: 'Category:Foo foo',
origCategory: 'Category:Foo_foo',
sortkey: 'Bar baz#quux',
origSortkey: 'Bar baz%23quux'
sortkey: 'Bar baz#quux'
}
},
{ type: '/mwCategory' },
@ -595,9 +583,7 @@ ve.dm.mwExample.withMetaMetaData = [
type: 'mwCategory',
attributes: {
category: 'Category:Bar',
origCategory: 'Category:Bar',
sortkey: '',
origSortkey: ''
sortkey: ''
}
}
],
@ -632,9 +618,7 @@ ve.dm.mwExample.withMetaMetaData = [
type: 'mwCategory',
attributes: {
category: 'Category:Foo foo',
origCategory: 'Category:Foo_foo',
sortkey: 'Bar baz#quux',
origSortkey: 'Bar baz%23quux'
sortkey: 'Bar baz#quux'
}
},
{
@ -1630,7 +1614,6 @@ ve.dm.mwExample.domToDataCases = {
type: 'link/mwInternal',
attributes: {
title: '',
origTitle: '',
normalizedTitle: '',
lookupTitle: ''
}
@ -1653,7 +1636,6 @@ ve.dm.mwExample.domToDataCases = {
type: 'link/mwInternal',
attributes: {
title: 'Foo?+%&Bar',
origTitle: 'Foo%3F+%25&Bar',
normalizedTitle: 'Foo?+%&Bar',
lookupTitle: 'Foo?+%&Bar'
}
@ -1857,19 +1839,19 @@ ve.dm.mwExample.domToDataCases = {
'<link rel="mw:PageProp/Category" href="./Category:Bar" />Bar' +
'<meta property="mw:foo" content="bar" />Ba<!-- inline -->z</p>' +
'<meta property="mw:bar" content="baz" /><!--barbaz-->' +
'<link rel="mw:PageProp/Category" href="./Category:Foo_foo#Bar baz%23quux" />' +
'<link rel="mw:PageProp/Category" href="./Category:Foo_foo#Bar%20baz%23quux" />' +
'<meta typeof="mw:Placeholder" data-parsoid="foobar" />',
clipboardBody: '<span rel="ve:Comment" data-ve-comment=" No conversion ">&nbsp;</span><meta property="mw:ThisIsAnAlien" /><p>Foo' +
'<link rel="mw:PageProp/Category" href="./Category:Bar" />Bar' +
'<meta property="mw:foo" content="bar" />Ba<span rel="ve:Comment" data-ve-comment=" inline ">&nbsp;</span>z</p>' +
'<meta property="mw:bar" content="baz" /><span rel="ve:Comment" data-ve-comment="barbaz">&nbsp;</span>' +
'<link rel="mw:PageProp/Category" href="./Category:Foo_foo#Bar baz%23quux" />' +
'<link rel="mw:PageProp/Category" href="./Category:Foo_foo#Bar%20baz%23quux" />' +
'<meta typeof="mw:Placeholder" data-parsoid="foobar" />',
previewBody: ve.dm.example.commentNodePreview( ' No conversion ' ) + '<meta property="mw:ThisIsAnAlien" /><p>Foo' +
'<link rel="mw:PageProp/Category" href="./Category:Bar" />Bar' +
'<meta property="mw:foo" content="bar" />Ba' + ve.dm.example.commentNodePreview( ' inline ' ) + 'z</p>' +
'<meta property="mw:bar" content="baz" />' + ve.dm.example.commentNodePreview( 'barbaz' ) +
'<link rel="mw:PageProp/Category" href="./Category:Foo_foo#Bar baz%23quux" />' +
'<link rel="mw:PageProp/Category" href="./Category:Foo_foo#Bar%20baz%23quux" />' +
'<meta typeof="mw:Placeholder" data-parsoid="foobar" />',
head: '<base href="http://example.com" />',
data: ve.dm.mwExample.withMeta,
@ -2178,7 +2160,6 @@ ve.dm.mwExample.domToDataCases = {
type: 'link/mwInternal',
attributes: {
title: 'Bar',
origTitle: 'Bar',
normalizedTitle: 'Bar',
lookupTitle: 'Bar'
}
@ -2190,7 +2171,6 @@ ve.dm.mwExample.domToDataCases = {
type: 'link/mwInternal',
attributes: {
title: 'Bar',
origTitle: 'Bar',
normalizedTitle: 'Bar',
lookupTitle: 'Bar'
}
@ -2202,7 +2182,6 @@ ve.dm.mwExample.domToDataCases = {
type: 'link/mwInternal',
attributes: {
title: 'Bar',
origTitle: 'Bar',
normalizedTitle: 'Bar',
lookupTitle: 'Bar'
}

View file

@ -91,7 +91,6 @@ QUnit.test( 'getTargetDataFromHref', ( assert ) => {
href: 'javascript:alert()',
expectedInfo: {
title: 'javascript:alert()',
rawTitle: 'javascript:alert()',
isInternal: true
}
/* eslint-enable no-script-url */
@ -101,7 +100,6 @@ QUnit.test( 'getTargetDataFromHref', ( assert ) => {
href: 'not-a-protocol:Some%20text',
expectedInfo: {
title: 'not-a-protocol:Some text',
rawTitle: 'not-a-protocol:Some%20text',
isInternal: true
}
},

View file

@ -95,7 +95,6 @@ QUnit.test( 'convert', function ( assert ) {
attributes: {
lookupTitle: 'Foo',
normalizedTitle: 'Foo',
origTitle: 'Foo',
title: 'Foo'
}
} ],
@ -129,7 +128,6 @@ QUnit.test( 'convert', function ( assert ) {
attributes: {
lookupTitle: 'Foo',
normalizedTitle: 'Foo',
origTitle: 'Foo',
title: 'Foo'
}
} ],

View file

@ -35,7 +35,7 @@ ve.ui.MWInternalLinkAnnotationWidget.static.getAnnotationFromText = function ( v
if ( !title ) {
return null;
}
return ve.dm.MWInternalLinkAnnotation.static.newFromTitle( title, trimmed );
return ve.dm.MWInternalLinkAnnotation.static.newFromTitle( title );
};
/**