User:Gryllida/DraftsReview/v2.js
Appearance
Code that you insert on this page could contain malicious content capable of compromising your account. If you import a script from another page with "importScript", "mw.loader.load", "iusc", or "lusc", take note that this causes you to dynamically load a remote script, which could be changed by others. Editors are responsible for all edits and actions they perform, including by scripts. User scripts are not centrally supported and may malfunction or become inoperable due to software changes. A guide to help you find broken scripts is available. If you are unsure whether code you are adding to this page is safe, you can ask at the appropriate village pump. This code will be executed when previewing this page. |
Documentation for this user script can be added at User:Gryllida/DraftsReview/v2. |
// <nowiki>
var afch2 = {
script_home: 'User:Gryllida/DraftsReview/v2.js' // subst:FULLPAGENAME in double {}s
};
/* ****************************************************************************
************* General Log functions ********************************************
******************************************************************************* */
function bugOut(){
Log('Could not complete. Please see query error above. Sorry. :-(',0);
}
function finishOK(){
Log('Done. :-)',0);
}
// Log (MESSAGE, [BUTTONS_DISABLED_STATE])
function Log(msg, propDisabled){
$('#afch2Log').val($('#afch2Log').val() + msg + '\r\n');
if(typeof propDisabled != 'undefined'){
$('#afch2Box').prop('disabled',propDisabled);
/*$('#afch2ApproveButton').prop('disabled',propDisabled);
$('#afch2CancelButton').prop('disabled',propDisabled);
$('#afch2DeclineButton').prop('disabled',propDisabled);*/
}
}
/* ****************************************************************************
************* Query functions **************************************************
******************************************************************************* */
// Gets meaningful content from the query.
function understandJson(data){
var page, wikitext;
if(data.edit && data.edit.result == 'Success'){ // An edit was ok
return true;
}
if(data.move){ // A move was ok
return true;
}
if(!data.query){ // We really need this for a read query
return false;
}
for (page in data.query.pages) {
if(!data.query.pages[page].revisions){ // We also really need this for a read query
return false;
}
res = data.query.pages[page].revisions[0]['*'];
return res;
}
}
/* ****************************************************************************
************* Contributor functions ********************************************
******************************************************************************* */
function Contributor(name) {
this.name = name;
}
Contributor.prototype.notify = function(page){
var template = page.isApproved ? 'Afc talk' : 'Afc decline';
var humanStatus = page.isApproved ? 'passed' : 'not ready';
var pageName = page.name.replace(/_/g,' ');
var subject = pageName.replace('Wikipedia talk:Articles for creation/','');
var sectionTitle = '[['+pageName+']] (<span class="plainlinks">[http://en.wikipedia.org/w/index.php?oldid='+page.revid+' revision '+ page.revid+']</span>)';
var talkPage = new Page('User talk:'+this.name);
return talkPage.query(1,{
content: '{{subst:'+template+'|'+subject+'|sig=yes}}',
summary: '[['+page.name+']]: '+humanStatus,
section: 'new',
sectionTitle: sectionTitle
});
};
/* ****************************************************************************
************* Page functions ***************************************************
******************************************************************************* */
function Page(name) {
this.name = name;
this.revid = mw.config.get('wgCurRevisionId');
}
Page.prototype.query = function(actionType, info, stopNagging) {
console.log('processing page ' + this.name);
var dfd = $.Deferred(),
self = this;
var query;
console.log('setting up query for this action type');
switch (actionType){
case 0:
query = this.readQuery(info);
break;
case 1:
query = this.editQuery(info);
break;
case 3:
query = this.moveQuery(info);
break;
}
console.log('done');
// If the query fails, the check failed.
query.fail( function(data){
if(!stopNagging){
Log("Couldn't query " + self.name + " (Action type " + actionType + ")");
}
dfd.reject();
});
query.then( function ( data ) {
console.log('I got data.');
console.log(data);
var result = understandJson(data);
if (!result) {
if(!stopNagging){
Log("Got error with query " + self.name + " (Action type " + actionType + ")");
Log(JSON.stringify(data,null,4));
}
dfd.reject(data);
} else {
dfd.resolve(result);
}
} );
// calling .promise() makes the object 'read-only'; only we can resolve /
// reject this deferred. it's not necessary to do but it's a nice way of
// indicating which piece of code is responsible for some operation.
return dfd.promise();
};
Page.prototype.readQuery = function(info){
var hash = {
format: 'json',
action: 'query',
prop: 'revisions',
rvprop: 'content',
rvlimit: 1,
titles: this.name,
};
if (info && typeof info.sectionNum != 'undefined'){
hash.rvsection=info.sectionNum;
}
var promise = $.getJSON(mw.util.wikiScript('api'),hash);
return promise;
};
// content - target page content
// summary - target page summary
// section - section number
// sectionTitle - section title
Page.prototype.editQuery = function(info){
var hash = {
url: mw.util.wikiScript( 'api' ),
type: 'POST',
dataType: 'json',
data: {
format: 'json',
action: 'edit',
title: this.name,
text: info.content, // will replace entire page content
summary: info.summary + ' ([[WP:AFC/S|see other submissions]]; [['+afch2.script_home+'|semi-automated]])',
token: mw.user.tokens.get( 'editToken' )
}
};
if (typeof info.section != 'undefined'){
hash.data.section=info.section;
}
if (typeof info.sectionTitle != 'undefined'){
hash.data.sectiontitle=info.sectionTitle;
}
var promise = $.ajax(hash);
return promise;
};
Page.prototype.moveQuery = function(info){
fromName = this.name;
toName = info.to;
this.name=toName;
return $.ajax({
url: mw.util.wikiScript( 'api' ),
type: 'POST',
dataType: 'json',
data: {
format: 'json',
action: 'move',
from: fromName,
to: toName,
reason: 'ready ([[WP:AFC/S|see other submissions]])',
token: mw.user.tokens.get( 'editToken' )
}
});
};
/* ****************************************************************************
************* Page functions: review *******************************************
******************************************************************************* */
// Decline the page
Page.prototype.decline = function(){
var self=this;
Log('Declining... ',1);
this.isApproved=0;
// Grab the page markup and work on it
this.query(0)
.then(self.processMarkupToDecline.bind(self))
.fail(bugOut);
};
Page.prototype.processMarkupToDecline=function(data){
Log('... retrieved the page markup ...');
var markup = new MarkUp(data);
// Remove all review pending templates (or exit if there are none)
if(!markup.clean()){
Log('Article not in queue. Sorry. :-(',0);
return false;
}
// Add review comment into the markup
markup.decline();
contributor = new Contributor(markup.submitterName);
// Save the article with the processed markup (w/ review comment)
Log('... processed markup for declining, trying to save the article ...');
$.when(
this.query(1, {
title: this.name,
content: markup.data,
summary: 'draft not ready'
}),
contributor.notify(this)
)
.done(finishOK)
.fail(bugOut);
};
// Comment on the page
Page.prototype.comment = function(){
var self=this;
Log('Commenting... ',1);
// Grab the page markup and work on it
this.query(0)
.then(self.processMarkupToComment.bind(self))
.fail(bugOut);
};
Page.prototype.processMarkupToComment=function(data){
Log('... retrieved the page markup ...');
var markup = new MarkUp(data,this);
var summary = $('#afch2CommentSummary').val();
markup.comment(); // uses $('#reviewBox').val()
$.when(
this.query(1, {
title: this.name,
content: markup.data,
summary: '+comment (' + summary + ')'
})
)
.done(finishOK)
.fail(bugOut);
};
// Approve the page
Page.prototype.approve = function(){
var self=this;
Log('Approving... ',1);
this.isApproved=1;
// Grab the page markup and work on it
this.query(0)
.then(self.processMarkupToApprove.bind(self))
.fail(bugOut);
};
Page.prototype.processMarkupToApprove=function(data){
Log('... retrieved the page markup ...');
var markup = new MarkUp(data,this);
var targetName = $('#afch2TargetPageName').val();
var self=this;
// Remove all review pending templates (or exit if there are none)
if(!markup.clean()){
Log('Article not in queue. Sorry. :-(',0);
return false;
}
// Add review comment into the markup
markup.extractComments();
this.query(3,{
to: targetName
}).then(self.postMoveProcess.bind(markup))
.fail(bugOut);
};
Page.prototype.postMoveProcess = function(){
newTalk = new Page('Talk:' + this.page.name);
contributor = new Contributor(this.submitterName);
$.when(
newTalk.query(1, {
content: this.talk,
summary: 'creation from Draft discussion',
section: 'new',
sectionTitle: 'Draft discussion'
}),
this.page.query(1, {
content: this.data,
summary: 'move of discussion to the talk page',
}),
contributor.notify(this.page)
).then(finishOK)
.fail(bugOut);
};
/* ****************************************************************************
************* MarkUp functions *************************************************
******************************************************************************* */
// Construct a new MarkUp object
function MarkUp(data,page){
this.data = data;
this.page=page;
}
MarkUp.prototype.comment = function(){
var comment = $('#reviewBox').val() ;
comment = "{{afc comment|1=" + comment + " --[[User:Gryllida|Gryllida]] ([[User talk:Gryllida|talk]]) 08:21, 26 January 2014 (UTC)}}";
this.data = comment + "\r\n" + this.data;
return true;
};
/*
* Remove all 'review pending' tags from the MarkUp object
* Also place the oldest (valuable! :) review-pending tag
* into this.template_res, for later processing */
MarkUp.prototype.clean = function(){
var data = this.data;
// Find all 'review pending' templates on the page
var templates = this.data.match(/{{AFC submission\|\|\|.*?}}/gmi);
// Exit if no review pending is marked on the page
if (templates == null){
return false;
}
// Find the oldest 'review pending' teplace on the page; also, remove them all.
var ts_min = Infinity;
var template_res='';
$(templates).each( function (index, template){ // template = '@FC submission|||ts=20140113103528|u=Daniels Wembley|ns=2'
ts = template.replace('{{','').replace('}}','').split('|')[3].replace('ts=','');
if (ts<ts_min){
ts_min = ts;
template_res = template;
}
data = data.replace(template, '');
});
// Remove boiletplate
data = data.replace('== Request review at [[WP:AFC]] ==','');
data = data.replace(/<!-- Just press the "Save page" button below without .*/,'');
// Save template for processing
this.data=data;
this.template_res = template_res;
// Break the submit template into pieces
this.values = this.template_res.replace('{{','').replace('}}','').split('|');
// Retrieve the username of the submitter
this.submitterName = this.values[4].replace('u=','');
// Exit
return true;
};
MarkUp.prototype.extractComments = function(){
var data = this.data;
var talk = '';
if($('#afch2PrjName').val()){
talk += '{{WikiProject '+$('#afch2PrjName').val()+'}}\n';
}
// Find all AFC templates on the page and remove them
var templates = this.data.match(/{{AFC[^]*?}}/gmi);
$(templates).each( function (index, template){
data = data.replace(template, '');
talk += template;
talk += '\r\n';
});
talk+="Approved. --~~~~\r\n";
// Save template for processing
this.data=data;
this.talk = talk;
// Exit
return true;
};
MarkUp.prototype.decline = function(){
// Submit template pieces
var values = this.values;
// 'd' as first value: Decline
values[1]='d';
// 'reason' as second value: Review type ('cv', 'npov', other types exist; not implemented)
values[2]='reason';
// Add 3=, declinets, decliner params - no place for them, kick around
values.splice(3,0,
'3 = ' + $('#reviewBox').val(),
'declinets={{subst:REVISION'+'TIMESTAMP}}',
'decliner=' + mw.config.get('wgUserName'));
this.template_res = '{{' + values.join('|') + '}}';
// Prepend the declined template to the page.
this.data = this.template_res + '\r\n' + this.data;
return true;
};
/* ****************************************************************************
************* DOM functions ****************************************************
******************************************************************************* */
// Construct DOM object
function DOM(){
// Load HTML from subpage
Log('Making DOM...');
this.subPage = new Page(afch2.script_home+'/text');
this.page = new Page(mw.config.get('wgPageName'));
this.query = this.subPage.query(0, {sectionNum: 0});
}
// Hide things and set events as necessary in the DOM
DOM.prototype.setUp = function(){
var page = this.page;
// Hide everything
$('afch2Log').hide();
$('#afch2Box').hide();
$('#afch2ApproveBox').hide();
$('#afch2DeclineBox').hide();
$('#afch2CommentBox').hide();
// Set events
//$("#reviewCans").change( function() {afch2.dom.insertCannedResponse();});
$('#afch2DeclineButton').click(page.decline.bind(page));
$('#afch2ApproveButton').click(page.approve.bind(page));
$('#afch2CommentButton').click(page.comment.bind(page));
$('#afch2ApprovePageCheck').click(pageCheck);
$("#reviewCans").change(insertCannedResponse);
$('#afch2CancelButton').click(function(){
$('#afch2Box').hide();
});
};
function pageCheck(){
$('#afch2ApprovePageCheck').prop('disabled',true);
var targetPage = new Page($('#afch2TargetPageName').val());
targetPage.query(0,{},1)
.then(function(){
$('#afch2ApproveButton').prop('disabled',true);
}).fail(function(){
$('#afch2ApproveButton').prop('disabled',false);
}).always(function(){
$('#afch2ApprovePageCheck').prop('disabled',false);
});
}
DOM.prototype.fillInReviewBox = function(data){
var lines = data.split('\n');
lines.shift();
$('#reviewBox').val(lines.join('\n'));
Log('... filled in the review box. :-)');
};
DOM.prototype.fillInCannedResponses = function(data){
var lines = data.split('\n');
lines.shift();
// Process group, name, and content from each line
$(lines).each(function( i, line ) {
group = line.split(':')[0];
name = line.split(':')[1];
content = line.split(':').slice(2).join(':');
// Create optgroup if it doesn't yet exist
if($('#reviewCans optgroup[label="'+group+'"]').html() == null){
$('<optgroup/>', {
label: group
}).appendTo('#reviewCans');
}
// Add the option to the relevant optgroup
$('<option/>', {
html: name,
value: content
}).prependTo('#reviewCans optgroup[label="'+group+'"]');
});
Log('... filled in the canned responses. :-)');
};
DOM.prototype.fillIn = function(){
var self=this;
Log('... filling in DOM ...');
this.subPage.query(0, {sectionNum: 1})
.then(self.fillInReviewBox)
.fail(bugOut);
this.subPage.query(0, {sectionNum: 2})
.then(self.fillInCannedResponses)
.fail(bugOut);
};
function insertCannedResponse (){
var caretPos = document.getElementById("reviewBox").selectionStart;
var textAreaTxt = $("#reviewBox").val();
var txtToAdd = $( "#reviewCans" ).val();
$("#reviewBox").val(textAreaTxt.substring(0, caretPos) + txtToAdd + textAreaTxt.substring(caretPos) );
}
DOM.prototype.only_show = function(id){
$('#afch2Box').toggle();
$('#afch2ApproveBox').hide();
$('#afch2DeclineBox').hide();
//$('#afch2CommentBox').hide();
$('#' + id).show();
};
/* ****************************************************************************
************* Document onload routine ******************************************
******************************************************************************* */
// Wait for the page to be parsed
$(document).ready( function() {
// Adds review tab
var linkA = mw.util.addPortletLink( 'p-cactions', '#', 'Approve', 'ca-afch2-approve', 'AFC Approve');
var linkD = mw.util.addPortletLink( 'p-cactions', '#', 'Decline or comment', 'ca-afch2-decline', 'AFC Decline');
//var linkC = mw.util.addPortletLink( 'p-cactions', '#', 'Comment', 'ca-afch2-comment', 'AFC Comment');
console.log('Making dom object');
var dom = new DOM();
console.log('Made dom object');
dom.query.then(function(data){
console.log('Added in the html');
$(data).prependTo('#content');
dom.setUp();
console.log('dom set up');
dom.fillIn();
console.log('dom filled in');
}).fail(bugOut);
// Assigns actions to the tabs
$( linkA ).click( function ( event ) {
event.preventDefault();
dom.only_show('afch2ApproveBox');
} );
$( linkD ).click( function ( event ) {
event.preventDefault();
dom.only_show('afch2DeclineBox');
} );
//$( linkC ).click( function ( event ) {
// event.preventDefault();
// dom.only_show('afch2CommentBox');
//} );
} );
// </nowiki>