mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/TemplateData
synced 2024-11-24 16:03:45 +00:00
3820295f23
Add a sourceHandler that deals with all source-related actions like fetching template source and validating the existing TemplateData string. Also add a MessageDialog warning the user in case the TemplateData is invalid. In this case, the user will be given the option either to correct the JSON by hand or to start the editor to replace the existing TemplateData with a new one. Bug: T91730 Bug: T91325 Change-Id: I4e6d04f02565a02d8dcf5c70a575ab6433caa27f
272 lines
7.8 KiB
JavaScript
272 lines
7.8 KiB
JavaScript
/**
|
|
* TemplateData Source Handler
|
|
*
|
|
* Loads and validates the templatedata and template parameters
|
|
* whether in the page itself or from its parent.
|
|
*
|
|
* @class
|
|
* @extends OO.EventEmitter
|
|
*
|
|
* @constructor
|
|
* @param {Object} config Configuration object
|
|
* @cfg {string} [fullPageName] The full name of the current page
|
|
* @cfg {string} [parentPage] The name of the parent page
|
|
* @cfg {string} [isPageSubLevel] The page is sub-level of another template
|
|
*/
|
|
mw.TemplateData.SourceHandler = function MWTemplateDataSourceHander( config ) {
|
|
config = config || {};
|
|
|
|
// Mixin constructors
|
|
OO.EventEmitter.call( this );
|
|
|
|
this.apiCache = {};
|
|
this.templateSourceCodePromise = null;
|
|
this.templateSourceCodeParams = [];
|
|
|
|
// Config
|
|
this.setParentPage( config.parentPage );
|
|
this.setPageSubLevel( config.isPageSubLevel );
|
|
this.setFullPageName( config.fullPageName );
|
|
};
|
|
|
|
/* Setup */
|
|
OO.initClass( mw.TemplateData.SourceHandler );
|
|
OO.mixinClass( mw.TemplateData.SourceHandler, OO.EventEmitter );
|
|
|
|
/**
|
|
* Get information from the mediaWiki API
|
|
* @param {string} page Page name
|
|
* @param {boolean} [getTemplateData] Fetch the templatedata in the page.
|
|
* @return {jQuery.Promise} API promise
|
|
*/
|
|
mw.TemplateData.SourceHandler.prototype.getApi = function ( page, getTemplateData ) {
|
|
var config,
|
|
type = getTemplateData ? 'templatedata' : 'query',
|
|
api = new mw.Api(),
|
|
baseConfig = {
|
|
action: type,
|
|
titles: page,
|
|
redirects: getTemplateData ? 1 : 0
|
|
};
|
|
|
|
if ( type === 'query' ) {
|
|
config = $.extend( baseConfig, {
|
|
prop: 'revisions',
|
|
rvprop: 'content',
|
|
indexpageids: '1'
|
|
} );
|
|
}
|
|
|
|
// Cache
|
|
if ( !this.apiCache[page] || !this.apiCache[page][type] ) {
|
|
this.apiCache[page] = this.apiCache[page] || {};
|
|
this.apiCache[page][type] = api.get( config );
|
|
}
|
|
return this.apiCache[page][type];
|
|
};
|
|
|
|
/**
|
|
* Go over the current wikitext and build a new model.
|
|
*
|
|
* @param {string} [wikitext] Source of the template.
|
|
* @return {jQuery.Promise} Promise resolving into a new mw.TemplateData.Model
|
|
* or is rejected if the model was impossible to create.
|
|
*/
|
|
mw.TemplateData.SourceHandler.prototype.buildModel = function ( wikitext ) {
|
|
var tdObject;
|
|
|
|
// Get the TemplateData and parse it
|
|
tdObject = this.parseModelFromString( wikitext );
|
|
if ( !tdObject ) {
|
|
// The json object is invalid. There's no need to continue.
|
|
return $.Deferred().reject();
|
|
}
|
|
|
|
// Get parameters from source code
|
|
// Mostly used for the import option
|
|
return this.getParametersFromTemplateSource( wikitext )
|
|
// This is always successful by definition
|
|
.then( function ( templateSourceCodeParams ) {
|
|
return new mw.TemplateData.Model.static.newFromObject(
|
|
tdObject,
|
|
templateSourceCodeParams
|
|
);
|
|
} );
|
|
};
|
|
|
|
/**
|
|
* Retrieve parameters from the template code from source in this order:
|
|
*
|
|
* 1. Check if there's a template in the given 'wikitext' parameter. If not,
|
|
* 2. Check if there's a template in the current page. If not,
|
|
* 3. Check if the page is a subpage and go up a level to check for template code. If none found,
|
|
* 4. Repeat until we are in the root of the template
|
|
* 5. Save the name of the page where the template is taken from
|
|
*
|
|
* Cache the templateCodePromise so we don't have to do this all over again on each
|
|
* template code request.
|
|
*
|
|
* @param {string} [wikitext] Optional. Source of the template.
|
|
* @return {jQuery.Promise} Promise resolving into template parameter array
|
|
*/
|
|
mw.TemplateData.SourceHandler.prototype.getParametersFromTemplateSource = function ( wikitext ) {
|
|
var params = [],
|
|
sourceHandler = this;
|
|
|
|
if ( !this.templateSourceCodePromise ) {
|
|
// Check given page text first
|
|
if ( wikitext ) {
|
|
params = this.extractParametersFromTemplateCode( wikitext );
|
|
}
|
|
|
|
if ( params.length > 0 ) {
|
|
// There are parameters found; Resolve.
|
|
this.templateSourceCodePromise = $.Deferred().resolve( params );
|
|
} else if ( this.isPageSubLevel() && this.getParentPage() ) {
|
|
// Get the content of the parent
|
|
this.templateSourceCodePromise = this.getApi( this.getParentPage() ).then(
|
|
function ( resp ) {
|
|
var pageContent = '';
|
|
|
|
// Verify that we have a sane response from the API.
|
|
// This is particularly important for unit tests, since the
|
|
// requested page from the API is the Qunit module and has no content
|
|
if (
|
|
resp.query.pages[resp.query.pageids[0]].revisions &&
|
|
resp.query.pages[resp.query.pageids[0]].revisions[0]
|
|
) {
|
|
pageContent = resp.query.pages[resp.query.pageids[0]].revisions[0]['*'];
|
|
}
|
|
return sourceHandler.extractParametersFromTemplateCode( pageContent );
|
|
},
|
|
function () {
|
|
// Resolve an empty parameters array
|
|
return $.Deferred().resolve( [] );
|
|
}
|
|
);
|
|
} else {
|
|
// No template found. Resolve to empty array of parameters
|
|
this.templateSourceCodePromise = $.Deferred().resolve( [] );
|
|
}
|
|
}
|
|
|
|
return this.templateSourceCodePromise;
|
|
};
|
|
|
|
/**
|
|
* Retrieve template parameters from the template code.
|
|
*
|
|
* Adapted from https://he.wikipedia.org/wiki/MediaWiki:Gadget-TemplateParamWizard.js
|
|
*
|
|
* @param {string} templateSource Source of the template.
|
|
* @return {jQuery.Promise} A promise that resolves into an
|
|
* array of parameters that appear in the template code
|
|
*/
|
|
mw.TemplateData.SourceHandler.prototype.extractParametersFromTemplateCode = function ( templateCode ) {
|
|
var matches,
|
|
paramNames = [],
|
|
paramExtractor = /{{3,}(.*?)[<|}]/mg;
|
|
|
|
while ( ( matches = paramExtractor.exec( templateCode ) ) !== null ) {
|
|
if ( $.inArray( matches[1], paramNames ) === -1 ) {
|
|
paramNames.push( $.trim( matches[1] ) );
|
|
}
|
|
}
|
|
|
|
return paramNames;
|
|
};
|
|
|
|
/**
|
|
* Look for a templatedata json string and convert it into
|
|
* the object, if it exists.
|
|
*
|
|
* @param {string} templateDataString Wikitext templatedata string
|
|
* @return {Object|null} The parsed json string. Empty if no
|
|
* templatedata string was found. Null if the json string
|
|
* failed to parse.
|
|
*/
|
|
mw.TemplateData.SourceHandler.prototype.parseModelFromString = function ( templateDataString ) {
|
|
var parts;
|
|
|
|
parts = templateDataString.match(
|
|
/<templatedata>([\s\S]*?)<\/templatedata>/i
|
|
);
|
|
|
|
// Check if <templatedata> exists
|
|
if ( parts && parts[1] && $.trim( parts[1] ).length > 0 ) {
|
|
// Parse the json string
|
|
try {
|
|
return $.parseJSON( $.trim( parts[1] ) );
|
|
} catch ( err ) {
|
|
return null;
|
|
}
|
|
} else {
|
|
// Return empty model
|
|
return { params: {} };
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Set the page as a sub page of the main template
|
|
* @param {boolean} isSubLevel Page is sublevel
|
|
*/
|
|
mw.TemplateData.SourceHandler.prototype.setPageSubLevel = function ( isSubLevel ) {
|
|
this.subLevel = !!isSubLevel;
|
|
};
|
|
|
|
/**
|
|
* Set the page as a sub page of the main template
|
|
* @return {boolean} Page is sublevel
|
|
*/
|
|
mw.TemplateData.SourceHandler.prototype.isPageSubLevel = function () {
|
|
return this.subLevel;
|
|
};
|
|
|
|
/**
|
|
* Get full page name
|
|
* @param {string} pageName Page name
|
|
*/
|
|
mw.TemplateData.SourceHandler.prototype.setFullPageName = function ( pageName ) {
|
|
this.fullPageName = pageName || '';
|
|
};
|
|
|
|
/**
|
|
* Get page full name
|
|
* @return {string} Page full name
|
|
*/
|
|
mw.TemplateData.SourceHandler.prototype.getFullPageName = function () {
|
|
return this.fullPageName;
|
|
};
|
|
|
|
/**
|
|
* Set parent page
|
|
* @param {string} Parent page
|
|
*/
|
|
mw.TemplateData.SourceHandler.prototype.setParentPage = function ( parent ) {
|
|
this.parentPage = parent || '' ;
|
|
};
|
|
|
|
/**
|
|
* Get parent page
|
|
* @return {string} Parent page
|
|
*/
|
|
mw.TemplateData.SourceHandler.prototype.getParentPage = function () {
|
|
return this.parentPage;
|
|
};
|
|
|
|
/**
|
|
* Set template source code parameters
|
|
* @param {string[]} params Parameters from the template source code
|
|
*/
|
|
mw.TemplateData.SourceHandler.prototype.setTemplateSourceCodeParams = function ( params ) {
|
|
this.templateSourceCodeParams = params;
|
|
};
|
|
|
|
/**
|
|
* Set template source code parameters
|
|
* @return {string[]} Parameters from the template source code
|
|
*/
|
|
mw.TemplateData.SourceHandler.prototype.getTemplateSourceCodeParams = function () {
|
|
return this.templateSourceCodeParams;
|
|
};
|