MediaWiki:Gadget-WikiForm.js
Note: After publishing, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (â-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (â-Shift-R on a Mac)
- Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
- Opera: Press Ctrl-F5.
/**
* This script interacts with [[Template:Form]]
*
*/
var WikiForm = {
init: function () {
// Finish building PageInputWidget
OO.inheritClass( WikiForm.PageInputWidget, OO.ui.TextInputWidget );
OO.mixinClass( WikiForm.PageInputWidget, OO.ui.mixin.LookupElement );
// Finish building LocationInputWidget
OO.inheritClass( WikiForm.LocationInputWidget, OO.ui.TextInputWidget );
OO.mixinClass( WikiForm.LocationInputWidget, OO.ui.mixin.LookupElement );
$( '.template-form' ).each( WikiForm.makeForm );
},
makeForm: function () {
var $template = $( this );
// Set the messages
var messages = {
'form-submit': $template.data( 'submit' ) || 'Submit',
'form-submit-success': $template.data( 'submit-success' ) || 'The form was submitted, thanks!',
'form-submit-error': $template.data( 'submit-error' ) || 'Something went wrong! $1',
'form-template-error': $template.data( 'template-error' ) || 'The "template" parameter is required.',
'form-namespace-error': $template.data( 'namespace-error' ) || "Forms don't work in the Template namespace.",
'form-group-error': $template.data( 'group-error' ) || "This form is restricted to the '$1' group.",
};
mw.messages.set( messages );
// Basic validation
var template = $template.data( 'template' );
if ( !template ) {
$template.addClass( 'error' ).text( mw.msg( 'form-template-error' ) );
return;
}
var group = $template.data( 'group' );
var groups = mw.config.get( 'wgUserGroups' );
if ( group && !groups.includes( group ) ) {
$template.addClass( 'error' ).text( mw.msg( 'form-group-error', group ) );
return;
}
// Make the fields and layouts
var fields = {};
var layouts = [];
for ( var i = 0; i < 100; i++ ) {
var name = $template.data( 'field' + i );
if ( !name ) {
continue;
}
// Make the field input
var type = $template.data( 'field' + i + '-type' );
var value = $template.data( 'field' + i + '-value' );
var placeholder = $template.data( 'field' + i + '-placeholder' );
var required = $template.data( 'field' + i + '-required' ) ? true : false;
var disabled = $template.data( 'field' + i + '-disabled' ) ? true : false;
var selected = $template.data( 'field' + i + '-selected' ) ? true : false;
var min = $template.data( 'field' + i + '-min' );
var max = $template.data( 'field' + i + '-max' );
var options = $template.data( 'field' + i + '-options' );
var category = $template.data( 'field' + i + '-category' );
var config = {
name: name,
value: value,
placeholder: placeholder,
required: required,
disabled: disabled,
data: { category: category },
};
var field = new OO.ui.TextInputWidget( config );
if ( type === 'hidden' ) {
field = new OO.ui.HiddenInputWidget( config );
}
if ( type === 'textarea' ) {
config.autosize = true;
field = new OO.ui.MultilineTextInputWidget( config );
}
if ( type === 'number' ) {
config.min = min;
config.max = max;
field = new OO.ui.NumberInputWidget( config );
}
if ( type === 'boolean' ) {
value = value || 1;
config.selected = selected;
config.value = selected ? value : '';
field = new OO.ui.CheckboxInputWidget( config );
field.on( 'change', function ( selected ) {
field.setValue( selected ? value : '' );
} );
}
if ( type === 'dropdown' ) {
if ( options ) {
config.options = [];
if ( !required ) {
config.options.push( { label: placeholder } );
}
options.split( ',' ).forEach( function ( value ) {
value = value.trim();
var option = { data: value };
config.options.push( option );
} );
}
field = new OO.ui.DropdownInputWidget( config );
}
if ( type === 'radio' ) {
if ( options ) {
config.options = [];
if ( !required ) {
config.options.push( { label: placeholder } );
}
options.split( ',' ).forEach( function ( value ) {
value = value.trim();
var option = { data: value };
config.options.push( option );
} );
}
field = new OO.ui.RadioSelectInputWidget( config );
}
if ( type === 'checkbox' ) {
if ( options ) {
config.options = [];
options.split( ',' ).forEach( function ( value ) {
value = value.trim();
var option = { data: value };
config.options.push( option );
} );
}
field = new OO.ui.CheckboxMultiselectInputWidget( config );
}
if ( type === 'page' ) {
field = new WikiForm.PageInputWidget( config );
}
if ( type === 'location' ) {
field = new WikiForm.LocationInputWidget( config );
}
// Make the field layout
var label = $template.data( 'field' + i + '-label' ) || name;
var help = $template.data( 'field' + i + '-help' );
var helpInline = $template.data( 'field' + i + '-help-inline' );
var align = type === 'boolean' ? 'inline' : 'top';
var layout = new OO.ui.FieldLayout( field, { label: label, align: align, help: help, helpInline: helpInline } );
if ( type === 'hidden' ) {
layout = field;
}
fields[ name ] = field;
layouts.push( layout );
}
// Make the submit button
var submitButton = new OO.ui.ButtonInputWidget( { label: mw.msg( 'form-submit' ), flags: [ 'primary', 'progressive' ] } );
var submitButtonLayout = new OO.ui.FieldLayout( submitButton, {} );
submitButton.on( 'click', WikiForm.submit, [ $template, submitButton, fields ] );
layouts.push( submitButtonLayout );
var form = new OO.ui.FormLayout( { items: layouts } );
$template.html( form.$element );
},
submit: function ( $template, submitButton, fields ) {
// Check the required fields
var name, field, $input;
for ( name in fields ) {
field = fields[ name ];
$input = field.$input;
if ( $input.attr( 'required' ) && !$input.val() ) {
$input.focus();
return;
}
}
// Check the namespace
if ( mw.config.get( 'wgCanonicalNamespace' ) === 'Template' ) {
mw.notify( mw.msg( 'form-namespace-error' ) );
return;
}
// Signal success
submitButton.setDisabled( true );
// Build the wikitext
var template = $template.data( 'template' );
var wikitext = '{{' + template;
var value;
for ( name in fields ) {
field = fields[ name ];
value = field.getValue();
if ( Array.isArray( value ) ) {
value = value.join( ', ' );
}
wikitext += '\n| ' + name + ' = ' + value;
}
wikitext += '\n}}';
// Figure out the page where to post
var page = $template.data( 'page' );
for ( name in fields ) {
field = fields[ name ];
value = field.getValue();
page = page.replace( '{{{' + name + '}}}', value );
}
if ( !page ) {
page = mw.config.get( 'wgPageName' );
}
// Figure out the section where to post
var section = $template.data( 'section' );
for ( name in fields ) {
field = fields[ name ];
value = field.getValue();
section = section.replace( '{{{' + name + '}}}', value );
}
// Append the wikitext to the page
WikiForm.post( wikitext, page, section, $template );
},
post: function ( wikitext, page, section, $template ) {
return new mw.Api().get( {
action: 'parse',
page: page,
prop: 'text',
formatversion: 2
} ).always( function ( data ) {
console.log( page, section, data );
// Figure out if the section already exists and its number
var sectionNumber;
if ( section ) {
sectionNumber = 'new';
if ( data !== 'missingtitle' ) {
var html = $.parseHTML( data.parse.text );
var $header = $( ':header:contains(' + section + ')', html );
if ( $header.length ) {
sectionNumber = 1 + $header.prevAll( ':header' ).length;
wikitext = '\n\n' + wikitext;
}
}
} else if ( data !== 'missingtitle' ) {
wikitext = '\n\n' + wikitext;
}
var params = {
action: 'edit',
title: page,
section: sectionNumber
};
if ( sectionNumber === 'new' ) {
params.sectiontitle = section;
params.text = wikitext;
} else {
params.appendtext = wikitext;
}
return new mw.Api().postWithEditToken( params ).done( function () {
var redirect = $template.data( 'redirect' );
var url = mw.util.getUrl( page + '#' + section );
if ( redirect ) {
window.location.href = url;
} else {
$template.text( mw.msg( 'form-submit-success' ) ).focus();
}
} ).fail( function ( code, info ) {
$template.addClass( 'error' ).text( mw.msg( 'form-submit-error', info ) ).focus();
} );
} );
},
/**
* Custom class for page fields
*/
PageInputWidget: function ( config ) {
OO.ui.TextInputWidget.call( this, config );
OO.ui.mixin.LookupElement.call( this, config );
this.getLookupRequest = function () {
var value = this.getValue();
if ( value.length < 3 ) {
return $.when(); // Return a resolved promise
}
var search = 'intitle:"' + value + '"';
var data = this.getData();
if ( data && data.category ) {
search += ' incategory:"' + data.category + '"';
}
var namespace = 0;
if ( data && data.namespace ) {
namespace = data.namespace;
}
var params = {
format: 'json',
formatversion: 2,
action: 'query',
list: 'search',
srsearch: search,
srnamespace: namespace,
};
return new mw.Api().get( params );
};
this.getLookupCacheDataFromResponse = function ( response ) {
var pages = [];
if ( response && response.query && response.query.search ) {
response.query.search.forEach( function ( result ) {
var title = new mw.Title( result.title, result.ns );
var text = title.getPrefixedText();
pages.push( text );
} );
}
return pages;
};
this.getLookupMenuOptionsFromData = function ( pages ) {
var items = [];
var data = this.getData();
pages.forEach( function ( page ) {
var config = { data: page, label: page };
var item = new OO.ui.MenuOptionWidget( config );
items.push( item );
} );
return items;
};
},
/**
* Custom class for location fields
*/
LocationInputWidget: function ( config ) {
OO.ui.TextInputWidget.call( this, config );
OO.ui.mixin.LookupElement.call( this, config );
this.getLookupRequest = function () {
var value = this.getValue();
if ( value.length < 3 ) {
return $.when(); // Return a resolved promise
}
var data = { q: value, format: 'json' };
return $.get( '//nominatim.openstreetmap.org/search', data );
};
this.getLookupCacheDataFromResponse = function ( response ) {
var locations = [];
response.forEach( function ( location ) {
locations.push( location.display_name );
} );
return locations;
};
this.getLookupMenuOptionsFromData = function ( locations ) {
var items = [];
locations.forEach( function ( location ) {
var config = { data: location, label: location };
var item = new OO.ui.MenuOptionWidget( config );
items.push( item );
} );
return items;
};
}
};
mw.loader.using( [
'mediawiki.api',
'mediawiki.user',
'mediawiki.util',
'oojs-ui-core',
'oojs-ui-widgets'
], WikiForm.init );