|
|
Line 1: |
Line 1: |
| /* 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((wgNamespaceNumber == 2 || wgNamespaceNumber == 3) && wgTitle.indexOf('/') == -1 && (wgAction != 'edit' || wgAction != 'submit') && wgEnableAPI) addOnloadHook(function() {
| |
| addPortletLink('p-cactions','/wiki/Special:BlankPage?blankspecial=ajaxmr&user=' + encodeURIComponent(wgTitle),'Mass Rollback','p-mr');
| |
| });
| |
|
| |
| if(wgCanonicalSpecialPageName && wgCanonicalSpecialPageName == 'Contributions' && wgEnableAPI) addOnloadHook(function() {
| |
| var ucfrm = document.getElementsByTagName('form')[0];
| |
| var targ = '';
| |
| if(ucfrm.target && ucfrm.target.value != '') {
| |
| targ = '&user=' + encodeURIComponent(ucfrm.target.value);
| |
| }
| |
| addPortletLink('p-cactions','/wiki/Special:BlankPage?blankspecial=ajaxmr' + targ,'Mass Rollback','p-mr');
| |
| });
| |
|
| |
| if(wgCanonicalSpecialPageName && 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 = 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, wgScript + '?curid=' + uc[i].pageid + '&diff=prev&oldid=' + uc[i].revid,'diff');
| |
| li.appendChild(document.createTextNode(') ('));
| |
| addlinkchild(li, wgScript + '?curid=' + uc[i].pageid + '&action=history','hist');
| |
| li.appendChild(document.createTextNode(') . . '));
| |
| addlinkchild(li, 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<ucs.length;i++) {
| |
| if(ucs[i].checked && ucs[i].parentNode.className == '') {
| |
| uc = ucs[i];
| |
| break;
| |
| }
| |
| }
| |
|
| |
| if(!uc) {
| |
| out.appendChild(document.createTextNode('* Done!'));
| |
| amrWorking = false;
| |
| } else {
| |
| var id = uc.id.replace(/amr\-cl\-/,'');
| |
| uc.parentNode.className = 'amr-step1';
| |
| uc.removeAttribute('checked');
| |
| var page = uc.title;
| |
| out.appendChild(document.createTextNode('> 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 = 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 = 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, 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;
| |
| }
| |