User:Splarka/ajaxmassrollback.js

/* Ajax mass user-rollback, version [0.0.1b] Originally from: http://en.wikipedia.org/wiki/User:Splarka/ajaxmassrollback.js

Notes:
 * Oh god this is ugly code. Haha.
 * This does not revert moves but thinks it does. Not sure how to skip them either. BE AWARE.
 * This only goes back 7 (configurable) days by default, but should work with any number of contribs in that timeframe.

Operation:
 * Gets user's contribs (query-continues until reaching the time limit).
 * For each contrib that is (top) and not (new) it leaves a checkbox (and info/links).
 * After selecting which to rollback via checkboxes, it iterates over these checkboxes.
 * For each revid, it gets the rollback token, title, and user.
 * Upon getting the token, it attempts rollback.
 * If successful, checkbox is unchecked and next is selected (after 1 second delay).

To do:
 * Delete link for pages created by user? Maybe.
 * Rewrite for efficiency when generator=usercontribs becomes implemented! Oh yah.
 * Markbot optional?
 * Reason field?

var amrNumContribs; if(!window.amrNumDays) var amrNumDays = 7 var amrWorking = false;

if((mw.config.get('wgNamespaceNumber') == 2 || mw.config.get('wgNamespaceNumber') == 3) && mw.config.get('wgTitle').indexOf('/') == -1 && (mw.config.get('wgAction') != 'edit' || mw.config.get('wgAction') != 'submit') && true) $(function { mw.util.addPortletLink('p-cactions','/wiki/Special:BlankPage?blankspecial=ajaxmr&user=' + encodeURIComponent(mw.config.get('wgTitle')),'Mass Rollback','p-mr'); });

if(mw.config.get('wgCanonicalSpecialPageName') && mw.config.get('wgCanonicalSpecialPageName') == 'Contributions' && true) $(function { var ucfrm = document.getElementsByTagName('form')[0];  var targ = ;  if(ucfrm.target && ucfrm.target.value != ) {    targ = '&user=' + encodeURIComponent(ucfrm.target.value);  }  mw.util.addPortletLink('p-cactions','/wiki/Special:BlankPage?blankspecial=ajaxmr' + targ,'Mass Rollback','p-mr'); });

if(mw.config.get('wgCanonicalSpecialPageName') && mw.config.get('wgCanonicalSpecialPageName').toLowerCase == 'blankpage' && queryString('blankspecial') == 'ajaxmr') { document.title = 'Ajax Mass Rollback'; addOnloadHook(amrForm); appendCSS('#amr-contriblist {border:2px solid black;margin:.7em .1em;padding:.5em;height:20em;overflow:auto;}' + '\n#amr-contriblist li {white-space:nowrap;} .amr-step1 {background-color:#ffff99;} .amr-step2 {background-color:#9999ff;} .amr-step3 {background-color:#99ff99;}'); }

function amrForm { //subvert this Special: page to our own needs. var con = document.getElementById('content') || document.getElementById('mw_content'); var bcon = document.getElementById('bodyContent') || document.getElementById('mw_contentholder'); var fh = getElementsByClassName(con,'h1','firstHeading')[0]; while(fh.firstChild) fh.removeChild(fh.firstChild) fh.appendChild(document.createTextNode('Ajax Mass Rollback')); for(var i=0;i<bcon.childNodes.length;i++) { bcur = bcon.childNodes[i]; if(bcur.id != 'siteSub' && bcur.id != 'contentSub' && bcur.className != 'visualClear') { while(bcur.firstChild) bcur.removeChild(bcur.firstChild) if(bcur.nodeType == 3) bcur.nodeValue = ''; } }

var form = document.createElement('form'); form.setAttribute('action','javascript:void(0)'); var lab1 = document.createElement('label'); lab1.setAttribute('for','amr-user') lab1.appendChild(document.createTextNode('User (vandal): ')); form.appendChild(lab1); var inp1 = document.createElement('input'); inp1.style.width = '20em'; inp1.setAttribute('type','text'); if(queryString('user')) inp1.setAttribute('value',queryString('user')); inp1.setAttribute('id','amr-user'); form.appendChild(inp1); var sub1 = document.createElement('input'); sub1.setAttribute('type','button'); sub1.setAttribute('id','amr-getcontribs'); sub1.setAttribute('value','start'); sub1.setAttribute('onclick','amrGetContribs'); form.appendChild(sub1); var ul = document.createElement('ul'); ul.setAttribute('id','amr-contriblist'); form.appendChild(ul); bcon.appendChild(form); var pre = document.createElement('pre'); pre.setAttribute('id','amr-output'); bcon.appendChild(pre); }

function amrGetContribs(tsoffset) { var start = ''; if(tsoffset) { start = '&ucstart=' + tsoffset; } else { if(amrWorking) return amrWorking = true; document.getElementById('amr-user').setAttribute('disabled','disabled'); injectSpinner(document.getElementById('amr-getcontribs'),'getcontribs-spin'); var ul = document.getElementById('amr-contriblist'); while(ul.firstChild) ul.removeChild(ul.firstChild) amrNumContribs = 0; } var user = document.getElementById('amr-user').value; var now = new Date; var stop = parseInt(now.getTime / 1000) - 86400 * amrNumDays; var url = mw.config.get('wgScriptPath') + '/api.php?action=query&format=json&list=usercontribs&ucprop=flags|title|ids|comment|timestamp&uclimit=50' + start + '&ucend=' + stop + '&ucuser=' + encodeURIComponent(user);

var req = sajax_init_object; req.open('GET', url, true); req.onreadystatechange = function { if(req.readyState == 4 && req.status == 200) { eval("amrPopulatContribs(" + req.responseText + ",'" + req.responseText.replace(/\'/g,"`") + "')"); } }  req.send(null); }

function amrPopulatContribs(obj,txt) { var ul = document.getElementById('amr-contriblist'); if(obj['error']) { ul.parentNode.appendChild(document.createTextNode('Api error: ' + obj['error']['code'] + ' - ' + obj['error']['info'] + '\n')); return; } if(!obj['query'] || !obj['query']['usercontribs']) { ul.parentNode.appendChild(document.createTextNode('Unexpected response: ' + txt + '\n')); return; } var uc = obj['query']['usercontribs']; for(var i=0;i<uc.length;i++) { if(uc[i]['new'] == '') { // delete link? } else if(uc[i]['top'] == '') { var li = document.createElement('li'); var inp = document.createElement('input'); inp.setAttribute('type','checkbox'); inp.setAttribute('checked','checked'); inp.setAttribute('class','amr-cl'); inp.setAttribute('title',uc[i].title); inp.setAttribute('id','amr-cl-' + uc[i].revid); li.appendChild(inp); var lab = document.createElement('label'); lab.setAttribute('for','amr-cl-' + uc[i].revid); lab.appendChild(document.createTextNode(uc[i].timestamp.replace(/[TZ]/g,' '))); li.appendChild(lab); li.appendChild(document.createTextNode('('));      addlinkchild(li, mw.config.get('wgScript') + '?curid=' + uc[i].pageid + '&diff=prev&oldid=' + uc[i].revid,'diff');       li.appendChild(document.createTextNode(') ('));       addlinkchild(li, mw.config.get('wgScript') + '?curid=' + uc[i].pageid + '&action=history','hist');       li.appendChild(document.createTextNode('). . '));      addlinkchild(li, mw.config.get('wgScript') + '?curid=' + uc[i].pageid,uc[i].title); if(uc[i].comment) li.appendChild(document.createTextNode(' (' + uc[i].comment + ')')) ul.appendChild(li); amrNumContribs++; } }

if(obj['query-continue'] && obj['query-continue']['usercontribs'] && obj['query-continue']['usercontribs']['ucstart']) { amrGetContribs(obj['query-continue']['usercontribs']['ucstart']); } else { amrWorking = false; document.getElementById('amr-user').removeAttribute('disabled'); removeSpinner('getcontribs-spin'); var li = document.createElement('li'); li.appendChild(document.createTextNode(amrNumContribs + ' top contributions found for user (over last ' + amrNumDays + ' days). '));    var sub = document.createElement('input'); sub.setAttribute('type','button'); sub.setAttribute('id','amr-startrollbacks'); sub.setAttribute('value','rollback selected'); sub.setAttribute('onclick','amrRollbackContribs'); li.appendChild(sub); ul.insertBefore(li,ul.firstChild); } }

function amrRollbackContribs(automated) { var out = document.getElementById('amr-output'); if(!automated) { if(amrWorking) return amrWorking = true; injectSpinner(document.getElementById('amr-startrollbacks'),'startrollbacks-spin'); }

var ul = document.getElementById('amr-contriblist'); var ucs = getElementsByClassName(ul,'input','amr-cl'); var uc = false;

for(var i=0;i Attempting to rollback ' + page + '\n')); amrGetToken(id,page); } }

function amrGetToken(id,page) { var out = document.getElementById('amr-output'); out.appendChild(document.createTextNode(' > Fetching rollback token for ' + page + '\n')); var url = mw.config.get('wgScriptPath') + '/api.php?action=query&format=json&prop=revisions&indexpageids&rvprop=user|ids&rvtoken=rollback&revids=' + id; var req = sajax_init_object; req.open('GET', url, true); req.onreadystatechange = function { if(req.readyState == 4 && req.status == 200) { eval("amrRollback(" + req.responseText + ",'" + req.responseText.replace(/\'/g,"`") + "')"); } }  req.send(null); }

function amrRollback(obj,txt) { var out = document.getElementById('amr-output'); if(obj['error']) { out.appendChild(document.createTextNode(' ! Api error: ' + obj['error']['code'] + ' - ' + obj['error']['info'] + '\n')); return; } if(!obj['query'] || !obj['query']['pageids'] || !obj['query']['pages'][obj['query']['pageids'][0]]) { out.appendChild(document.createTextNode(' ? Unexpected response: ' + txt + '\n')); return; } var pid = obj['query']['pages'][obj['query']['pageids'][0]]; if(!pid['title'] || !pid['revisions'] || !pid['revisions'][0] || !pid['revisions'][0]['user'] || !pid['revisions'][0]['revid'] || !pid['revisions'][0]['rollbacktoken']) { out.appendChild(document.createTextNode(' ?? Unexpected response: ' + txt + '\n')); return; } var id = pid['revisions'][0]['revid']; var uc = document.getElementById('amr-cl-' + id); if(uc) uc.parentNode.className = 'amr-step2' var user = pid['revisions'][0]['user']; var token = pid['revisions'][0]['rollbacktoken']; var title = pid['title']; out.appendChild(document.createTextNode(' > Token found, attempting rollback\n'));

var params = 'action=rollback&format=json&markbot=1&token=' + encodeURIComponent(token) + '&title=' + encodeURIComponent(title) + '&user=' + encodeURIComponent(user); var url = mw.config.get('wgScriptPath') + '/api.php';

var req = sajax_init_object; req.open('POST', url, true); req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); req.setRequestHeader('Content-length', params.length); req.setRequestHeader('Connection', 'close'); req.onreadystatechange = function { if(req.readyState == 4 && req.status == 200) { eval("amrRollbackAftermath(" + req.responseText + ",'" + req.responseText.replace(/\'/g,"`") + "')"); } }  req.send(params); }

function amrRollbackAftermath(obj,txt) { var out = document.getElementById('amr-output'); if(obj['error']) { out.appendChild(document.createTextNode('  ! Api error: ' + obj['error']['code'] + ' - ' + obj['error']['info'] + '\n')); } else if(obj['rollback'] && obj['rollback']['title']) { var rb = obj['rollback']; out.appendChild(document.createTextNode('  > Page ' + rb['title'] + ' rollbacked ('));    addlinkchild(out, mw.config.get('wgScript') + '?curid=' + rb['pageid'] + '&diff=next&oldid=' + rb['old_revid'],'diff');    out.appendChild(document.createTextNode(').\n')); if(obj['rollback']['old_revid']) { var uc = document.getElementById('amr-cl-' + obj['rollback']['old_revid']); uc.setAttribute('disabled','disabled'); if(uc) uc.parentNode.className = 'amr-step3' } } else { out.appendChild(document.createTextNode('  ? Unexpected response: ' + txt + '\n')); return; } setTimeout('amrRollbackContribs(true)',1000); }

function addlinkchild(obj,href,text,id,classes) { if(!obj || !href || !text) return false; var a = document.createElement('a'); a.setAttribute('href',href); a.appendChild(document.createTextNode(text)); if(id) a.setAttribute('id',id); if(classes) a.setAttribute('class',classes); obj.appendChild(a); return a; }

function queryString(p) { var re = RegExp('[&?#]' + p + '=([^&#]*)'); var matches; if (matches = re.exec(document.location)) { try { return decodeURI(matches[1]); } catch (e) { } }  return null; }