// <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>