Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
/**
* MediaWiki:AFC-submit-wizard.js
*
* JavaScript used for submitting drafts to AfC.
* Loaded via [[mw:Snippets/Load JS and CSS by URL]].
*
* Author: [[User:SD0001]]
* Licence: MIT
*/
/* jshint maxerr: 999 */
/* globals mw, $, OO */
/* <nowiki> */
$.when(
$.ready,
mw.loader.using(['mediawiki.util', 'mediawiki.api', 'oojs-ui-core', 'oojs-ui-widgets'])
).then(function () {
if (mw.config.get('wgPageName') !== 'Wikipedia:Submitting_your_draft_...') {
return;
}
if (mw.util.getParamValue('withJS') !== 'MediaWiki:AFC-submit-wizard.js') {
return;
}
var apiOptions = {
parameters: {
format: 'json',
formatversion: '2'
},
ajax: {
headers: {
'Api-User-Agent': 'w:en:MediaWiki:AFC-submit-wizard.js'
}
}
};
// Two different API objects so that aborts on the lookupApi don't stop the final
// evaluate process
var lookupApi = new mw.Api(apiOptions),
submitApi = new mw.Api(apiOptions);
var draftLayout, talkTagsLayout, shortdescLayout, isBLPLayout, isCompanyLayout, topicsLayout, submitLayout, draftInput, talkTagsInput, shortdescInput, isBLPInput, isCompanyInput, topicsInput, submitButton, mainStatusLayout, mainStatusArea, talkStatusLayout, talkStatusArea;
// global
var pagetext, talktext, oresTopics;
// Create the UI
var fieldset = new OO.ui.FieldsetLayout({
label: 'Submit your draft for review at Articles for Creation (AfC)',
classes: ['container'],
items: [
draftLayout = new OO.ui.FieldLayout(draftInput = new OO.ui.TextInputWidget({
value: mw.util.getParamValue('draft').replace(/_/g, ' '),
placeholder: 'Enter the draft title, begins with "Draft:" or "User:"'
}), {
label: 'Draft title',
align: 'top',
help: 'This should be pre-filled if you clicked the link while on the draft page',
helpInline: true
}),
isBLPLayout = new OO.ui.FieldLayout(isBLPInput = new OO.ui.CheckboxInputWidget({
selected: false
}), {
label: 'This draft is a biography of a living person',
align: 'inline'
}),
isCompanyLayout = new OO.ui.FieldLayout(isCompanyInput = new OO.ui.CheckboxInputWidget({
selected: false
}), {
label: 'This draft is about a company or a corporation',
align: 'inline'
}),
shortdescLayout = new OO.ui.FieldLayout(shortdescInput = new OO.ui.TextInputWidget({
placeholder: 'Briefly describe the subject (eg. "Kenyan astronomer", "Indian dessert")',
maxLength: 100
}), {
label: 'Short description',
align: 'top',
help: 'Try not to exceed 40 characters',
helpInline: true
}),
talkTagsLayout = new OO.ui.FieldLayout(talkTagsInput = new OO.ui.MenuTagMultiselectWidget({
placeholder: 'Start typing to search for tags ...',
tagLimit: 10,
autocomplete: false
}), {
label: 'WikiProject classification tags',
align: 'top',
help: 'Adding the 1–4 most applicable WikiProjects is plenty. For example, if you add the Physics tag, you do not need to also add the Science tag.',
helpInline: true
}),
// This is shown only if the ORES topic lookup fails, or is inconclusive
topicsLayout = new OO.ui.FieldLayout(topicsInput = new OO.ui.MenuTagMultiselectWidget({
placeholder: 'Start typing to search for topics ...',
tagLimit: 10,
autocomplete: false, // XXX: doesn't seem to work
options: ["biography", "women", "food-and-drink", "internet-culture", "linguistics", "literature", "books", "entertainment", "films", "media", "music", "radio", "software", "television", "video-games", "performing-arts", "philosophy-and-religion", "sports", "architecture", "comics-and-anime", "fashion", "visual-arts", "geographical", "africa", "central-africa", "eastern-africa", "northern-africa", "southern-africa", "western-africa", "central-america", "north-america", "south-america", "asia", "central-asia", "east-asia", "north-asia", "south-asia", "southeast-asia", "west-asia", "eastern-europe", "europe", "northern-europe", "southern-europe", "western-europe", "oceania", "business-and-economics", "education", "history", "military-and-warfare", "politics-and-government", "society", "transportation", "biology", "chemistry", "computing", "earth-and-environment", "engineering", "libraries-and-information", "mathematics", "medicine-and-health", "physics", "stem", "space", "technology"].map(function (e) {
return {
data: e,
label: e
};
})
}), {
label: 'Topic classifiers',
align: 'top',
help: 'Pick the topic areas that are relevant',
helpInline: true
}),
submitLayout = new OO.ui.FieldLayout(submitButton = new OO.ui.ButtonWidget({
label: 'Submit',
flags: ['progressive', 'primary'],
})),
]
});
topicsLayout.toggle(false);
var asUser = mw.util.getParamValue('username');
if (asUser && asUser !== mw.config.get('wgUserName')) {
fieldset.addItems([
new OO.ui.FieldLayout(new OO.ui.MessageWidget({
type: 'notice',
inline: true,
label: 'Submitting as User:' + asUser
}))
], /* position */ 6);
}
$('.mw-ui-button').parent().replaceWith(fieldset.$element);
// populate talk page tags for multi-select widget
var talkTagOptionsLoaded = $.Deferred();
$.getJSON('https://en.wikipedia.org/w/index.php?title=' + encodeURIComponent("User:Theo's Little Bot/afchwikiproject.js") + '&action=raw&ctype=text/json').then(function (data) {
talkTagsInput.addOptions(Object.keys(data).map(function (k) {
return {
data: data[k],
label: k
};
}));
talkTagOptionsLoaded.resolve();
});
submitButton.$element.on('click', evaluate);
draftInput.on('change', onDraftInputChange);
if (mw.util.getParamValue('draft')) {
onDraftInputChange();
}
function onDraftInputChange() {
lookupApi.abort(); // abort older API requests
if (!draftInput.getValue().trim()) { // empty
return;
}
// re-initialize
draftLayout.setErrors([]);
draftLayout.setWarnings([]);
oresTopics = null;
talktext = null;
pagetext = null;
lookupApi.get({
"action": "query",
"prop": "revisions|description|info",
"titles": draftInput.getValue(),
"rvprop": "content",
"rvslots": "main"
}).then(function (json) {
var page = json.query.pages[0];
var preNormalizedTitle = json.query.normalized && json.query.normalized[0] &&
json.query.normalized[0].from;
if (draftInput.getValue() !== (preNormalizedTitle || page.title)) {
return; // user must have changed the title already
}
if (!page || page.invalid) {
draftLayout.setErrors(['Please check draft title. This title is invalid.']);
return;
}
if (page.missing) {
draftLayout.setErrors(['Please check draft title. No such draft exists.']);
return;
}
pagetext = page.revisions[0].slots.main.content;
// Show no refs warning
if (!/<ref>/.test(pagetext) && !/\{\{[Ss]fn\}\}/.test(pagetext)) {
draftLayout.setWarnings([
new OO.ui.HtmlSnippet('This draft doesn\'t appear to contain any references. Please add references, without this it will almost certainly be declined. See <a href="/wiki/Help:Introduction_to_referencing_with_Wiki_Markup/2" target="_blank">help on adding references</a>.')
]);
}
// set checkbox states
isBLPInput.setSelected(/\{\{[Dd]raftBLP\}\}/.test(pagetext));
isCompanyInput.setSelected(/\{\{[Dd]raftCorp\}\}/.test(pagetext));
// set shortdesc in form
shortdescInput.setValue(page.description || '');
// fill ORES topics
getOresTopics(page.lastrevid).then(function (topics) {
if (!topics || !topics.length) { // unexpected API response or API returns unsorted
topicsLayout.toggle(true);
} else {
topicsLayout.toggle(false);
oresTopics = topics;
}
}, function() {
topicsLayout.toggle(true);
});
});
var talk = mw.Title.newFromText(draftInput.getValue());
if (!talk) {
return;
}
var talkpagename = talk.getTalkPage().toText();
lookupApi.get({
"action": "query",
"prop": "revisions",
"titles": talkpagename,
"rvprop": "content",
"rvslots": "main",
}).then(function (json) {
var talkpage = json.query.pages[0];
if (!talkpage || talkpage.missing) {
return;
}
talktext = talkpage.revisions[0].slots.main.content;
var existingWikiProjects = extractWikiProjectTagsFromText(talktext);
var existingTags = existingWikiProjects.map(function(e) {
return e.name;
});
talkTagOptionsLoaded.then(function() {
talkTagsInput.setValue(existingTags);
});
});
}
function getOresTopics(revid) {
return $.get('https://ores.wikimedia.org/v3/scores/enwiki/?models=drafttopic&revids=' + revid).then(function (json) {
// null is returned if at any point something in the API output is unexpected
return json &&
json.enwiki &&
json.enwiki.scores &&
json.enwiki.scores[revid] &&
json.enwiki.scores[revid].drafttopic &&
json.enwiki.scores[revid].drafttopic.score &&
(json.enwiki.scores[revid].drafttopic.score.prediction instanceof Array) &&
json.enwiki.scores[revid].drafttopic.score.prediction.map(function (topic, idx, topics) {
// Remove Asia.Asia* if Asia.South-Asia is present (example)
if (topic.slice(-1) === '*') {
var metatopic = topic.split('.').slice(0, -1).join('.');
for (var i = 0; i < topics.length; i++) {
if (topics[i] !== topic && topics[i].startsWith(metatopic)) {
return;
}
}
return metatopic.split('.').pop();
}
return topic.split('.').pop();
})
.filter(function (e) {
return e; // filter out undefined from above
})
.map(function (topic) {
// convert topic string to normalised form
return topic
.replace(/[A-Z]/g, function (match) {
return match[0].toLowerCase();
})
.replace(/ /g, '-')
.replace(/&/g, 'and');
});
});
}
function extractWikiProjectTagsFromText(text) {
if (!text) {
return [];
}
// this is best-effort, no guaranteed accuracy
var existingTags = [];
var rgx = /\{\{(WikiProject [^|}]*).*?\}\}/g;
var match;
while (match = rgx.exec(text)) { // jshint ignore:line
var tag = match[1].trim();
if (tag === 'WikiProject banner shell') {
continue;
}
existingTags.push({ wikitext: match[0], name: tag });
}
return existingTags;
}
function evaluate() {
if (!mainStatusLayout || !mainStatusLayout.isElementAttached()) {
fieldset.addItems([
mainStatusLayout = new OO.ui.FieldLayout(mainStatusArea = new OO.ui.MessageWidget({
type: 'notice',
label: 'Processing ...'
}))
]);
}
var draft = draftInput.getValue();
submitApi.get({
"action": "query",
"prop": "revisions",
"titles": draft,
"rvprop": "content",
"rvslots": "main"
}).then(function (json) {
var page = json.query.pages[0];
if (!page || page.invalid || page.missing) {
draftLayout.setErrors(['Please check draft title. No such draft exists.']);
fieldset.removeItems([mainStatusLayout]);
return;
}
var text = page.revisions[0].slots.main.content;
var header = '';
// add shortdesc
if (shortdescInput.getValue()) {
text = text.replace(/\{\{[Ss]hort description\|.*?\}\}\n*/g, '');
header += '{{Short description|' + shortdescInput.getValue() + '}}\n';
}
// draft topics
// if (topicsInput && topicsInput.isElementAttached()) {
if (topicsLayout.isVisible()) {
oresTopics = topicsInput.getValue();
}
if (oresTopics.length) {
text = text.replace(/\{\{[Dd]raft topics\|.*?\}\}\n*/g, '');
header += '{{Draft topics|' + oresTopics.join('|') + '}}\n';
}
// put BLP/Company categories
text = text.replace(/\{\{[Dd]raft(BLP|Corp)\}\}\n*/g, '');
if (isBLPInput.isSelected()) {
header += '{{DraftBLP}}\n';
}
if (isCompanyInput.isSelected()) {
header += '{{DraftCorp}}\n';
}
// put AFC submission template
header += '{{subst:submit|' + (mw.util.getParamValue('username') || mw.config.get('wgUserName')) + '}}\n';
// insert everything to the top
text = header + text;
mainStatusArea.setType('notice');
mainStatusArea.setLabel('Saving draft page ...');
// saving draft page
submitApi.postWithEditToken({
"action": "edit",
"title": draft,
"text": text,
"summary": 'Submitting using [[MediaWiki:AFC-submit-wizard.js|AFC-submit-wizard]])'
}).then(function (data) {
if (data.edit && data.edit.result === 'Success') {
mainStatusArea.setType('success');
mainStatusArea.setLabel('Submission succeeded. Redirecting you to the draft page ...');
setTimeout(function () {
___location.href = mw.util.getUrl(draft);
}, 1000);
} else {
return $.Deferred().reject('unexpected-result');
}
}).catch(function (err) {
mainStatusArea.setType('error');
mainStatusArea.setLabel('An error occurred (' + err + '). Please try again or refer to the help desk.');
});
fieldset.addItems([
talkStatusLayout = new OO.ui.FieldLayout(talkStatusArea = new OO.ui.MessageWidget({
type: 'notice',
label: 'Saving draft talk page ...'
}))
]);
// Process text of the talk page
var alreadyExistingWikiProjects = extractWikiProjectTagsFromText(talktext);
var alreadyExistingTags = alreadyExistingWikiProjects.map(function(e) {
return e.name;
});
var tagsToAdd = talkTagsInput.getValue().filter(function(tag) {
return alreadyExistingTags.indexOf(tag) === -1;
});
var tagsToRemove = alreadyExistingTags.filter(function(tag) {
return talkTagsInput.getValue().indexOf(tag) === -1;
});
tagsToRemove.forEach(function(tag) {
talktext = talktext.replace(new RegExp('\\{\\{\\s*' + tag + '\\s*(\\|.*?)?\\}\\}\\n?'), '');
});
var tagsToAddText = tagsToAdd.map(function (tag) {
return '{{' + tag + '}}';
}).join('\n') + (tagsToAdd.length ? '\n' : '');
talktext = tagsToAddText + (talktext || '');
submitApi.postWithEditToken({
"action": "edit",
"title": new mw.Title(draft).getTalkPage().toText(),
"text": talktext,
"summary": 'Adding WikiProject tags using [[MediaWiki:AFC-submit-wizard.js|AFC-submit-wizard]])'
}).then(function(data) {
if (data.edit && data.edit.result === 'Success') {
talkStatusArea.setType('success');
talkStatusArea.setLabel('Successfully added WikiProject tags to talk page');
} else {
return $.Deferred().reject('unexpected-result');
}
}).catch(function (err) {
talkStatusArea.setType('error');
talkStatusArea.setLabel('An error occurred in editing the talk page (' + err + ').');
});
}).catch(function (err) {
mainStatusArea.setType('error');
mainStatusArea.setLabel('An error occurred (' + err + '). Please try again or refer to the help desk.');
});
}
});
/* </nowiki> */