MediaWiki:Gadget-PatrollingEnhancements.js: difference between revisions
Jump to navigation
Jump to search
Content deleted Content added
fix previous: it turns out that mediaWiki.user.tokens.get('deleteToken') doesn't work: only editToken, patrolToken, and watchToken do. |
m indicate format of GPE.currMonth & GPE.lastMonth in a comment |
||
(2 intermediate revisions by the same user not shown) | |||
Line 23: | Line 23: | ||
==Automated patrolling (whitelisting)== |
==Automated patrolling (whitelisting)== |
||
<pre> */ |
<pre> */ |
||
// set GPE.currMonth and GPE.lastMonth (in the form of, e.g., '2013/February') |
|||
( |
|||
function () |
|||
{ |
|||
var monthNames = [ 'January', 'February', 'March', 'April', 'May', 'June', |
|||
'July', 'August', 'September', 'October', 'November', |
|||
'December' ]; |
|||
var now = new Date(); |
|||
var currYear = now.getFullYear(); |
|||
var currMonthNum = now.getMonth(); |
|||
GPE.currMonth = currYear + '/' + monthNames[currMonthNum]; |
|||
var lastYear = (currMonthNum == 0 ? currYear - 1 : currYear); |
|||
var lastMonthNum = (currMonthNum == 0 ? 11 : currMonthNum - 1); |
|||
GPE.lastMonth = lastYear + '/' + monthNames[lastMonthNum]; |
|||
} |
|||
)(); |
|||
GPE.individualWhiteListedPages = |
GPE.individualWhiteListedPages = |
||
Line 34: | Line 56: | ||
"Wiktionary:Tea room": true, |
"Wiktionary:Tea room": true, |
||
"Wiktionary:Etymology scriptorium": true, |
"Wiktionary:Etymology scriptorium": true, |
||
"Wiktionary:Beer parlour": true, |
|||
"Wiktionary:Grease pit": true, |
|||
"Wiktionary:Requested entries (English)": true, |
"Wiktionary:Requested entries (English)": true, |
||
"Wiktionary:Requested entries (Spanish)": true, |
"Wiktionary:Requested entries (Spanish)": true, |
||
Line 47: | Line 67: | ||
"Wiktionary:Word of the day/Nominations": true |
"Wiktionary:Word of the day/Nominations": true |
||
}; |
}; |
||
GPE.individualWhiteListedPages["Wiktionary:Beer parlour/" + GPE.currMonth] = |
|||
GPE.individualWhiteListedPages["Wiktionary:Beer parlour/" + GPE.lastMonth] = |
|||
GPE.individualWhiteListedPages["Wiktionary:Grease pit/" + GPE.currMonth] = |
|||
GPE.individualWhiteListedPages["Wiktionary:Grease pit/" + GPE.lastMonth] = |
|||
true; |
|||
// per-user white-listed sub-pages (for example, edits by user Foo |
// per-user white-listed sub-pages (for example, edits by user Foo |
Revision as of 06:21, 24 February 2013
var GPE = {};
/* </pre>
==Configuration options==
<pre> */
// The initial value to put in the "deletion reason" text-field; you can
// override this in your common.js (or vector.js or whatnot).
GPE.initialDeleteReason = '';
// The value to use as a deletion reason if you leave the text-field blank; you
// can override it in your common.js (or vector.js or whatnot). If you *don't*
// override this, then MediaWiki will generate an automatic deletion reason that
// indicates the entry's last editor and the beginning of its content.
GPE.deleteReasonIfBlank = '';
// By DCDuring's request. If you set this to true, then Special:Watchlist will
// show the deletion-reason text-input, but *not* the deletion-reason dropdown,
// when there's an unpatrolled new-page-creation.
GPE.hideDeleteReasonDropdownOnWatchlist = false;
/* </pre>
==Automated patrolling (whitelisting)==
<pre> */
// set GPE.currMonth and GPE.lastMonth (in the form of, e.g., '2013/February')
(
function ()
{
var monthNames = [ 'January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November',
'December' ];
var now = new Date();
var currYear = now.getFullYear();
var currMonthNum = now.getMonth();
GPE.currMonth = currYear + '/' + monthNames[currMonthNum];
var lastYear = (currMonthNum == 0 ? currYear - 1 : currYear);
var lastMonthNum = (currMonthNum == 0 ? 11 : currMonthNum - 1);
GPE.lastMonth = lastYear + '/' + monthNames[lastMonthNum];
}
)();
GPE.individualWhiteListedPages =
{
"Wiktionary:Requests for cleanup": true,
"Wiktionary:Requests for verification": true,
"Wiktionary:Requests for deletion": true,
"Wiktionary:Requests for deletion/Others": true,
"Wiktionary:Requests for moves, mergers and splits": true,
"Wiktionary:Information desk": true,
"Wiktionary:Tea room": true,
"Wiktionary:Etymology scriptorium": true,
"Wiktionary:Requested entries (English)": true,
"Wiktionary:Requested entries (Spanish)": true,
"Wiktionary:List of protologisms": true,
"Wiktionary:Translation requests": true,
"Wiktionary:Feedback": true,
"Wiktionary:Sandbox": true,
"Wiktionary talk:Sandbox": true,
"Wiktionary:Tutorial (Editing)/sandbox": true,
"Wiktionary:Featured word candidates": true,
"Wiktionary:Word of the day/Nominations": true
};
GPE.individualWhiteListedPages["Wiktionary:Beer parlour/" + GPE.currMonth] =
GPE.individualWhiteListedPages["Wiktionary:Beer parlour/" + GPE.lastMonth] =
GPE.individualWhiteListedPages["Wiktionary:Grease pit/" + GPE.currMonth] =
GPE.individualWhiteListedPages["Wiktionary:Grease pit/" + GPE.lastMonth] =
true;
// per-user white-listed sub-pages (for example, edits by user Foo
// to User:Foo/vector.js should be autopatrolled):
GPE.perUserWhiteListedSubPages =
{
"/Sandbox": true,
"/sandbox": true,
"/chick.js": true,
"/chick.css": true,
"/standard.js": true,
"/standard.css": true,
"/cologneblue.js": true,
"/cologneblue.css": true,
"/modern.js": true,
"/modern.css": true,
"/myskin.js": true,
"/myskin.css": true,
"/nostalgia.js": true,
"/nostalgia.css": true,
"/simple.js": true,
"/simple.css": true,
"/vector.js": true,
"/vector.css": true,
"/common.js": true,
"/common.css": true
};
GPE.individualWhiteListedContributors =
{
"16@r": true,
"176.249.126.12": true,
"71.66.97.228": true,
"89.73.179.94": true
};
GPE.shouldAutoPatrol = function(link)
{
var pagename = link.title;
if(pagename.indexOf('User talk:') == 0)
return true;
if(pagename in GPE.individualWhiteListedPages)
return true;
var contributor;
if(mediaWiki.config.get('wgCanonicalSpecialPageName') === 'Contributions')
{
contributor =
document.getElementById('t-contributions')
.getElementsByTagName('a')[0].href.replace(/.*\//, '');
}
else
{
var li = link.parentNode;
if(li.tagName.toUpperCase() == 'SPAN')
li = li.parentNode;
var links = li.getElementsByTagName('a');
for(var i = 0; i < links.length; ++i)
if(links[i].title.indexOf('Special:Contributions/') == 0)
{
contributor = links[i].title.substr('Special:Contributions/'.length);
break;
}
}
if(pagename.indexOf('User:' + contributor + '/') == 0)
if(pagename.substr(contributor.length + 5) in GPE.perUserWhiteListedSubPages)
return true;
if(contributor in GPE.individualWhiteListedContributors)
return true;
return false;
};
/* </pre>
==Utility functions==
<pre> */
GPE.newButton = function(text, color, hoverText)
{
var button = newNode('button', text);
button.style.background = color;
button.style.color = '#FFF';
button.style.border = '0';
button.style.padding = '0';
button.style.cursor = 'pointer';
button.title = hoverText;
return button;
};
GPE.disableButton = function(button, text, hoverText)
{
button.onclick = null;
button.title = (hoverText || '');
button.innerHTML = text;
// clear out explicit styling and disable, so we can get appropriate
// disabled-button styles:
button.style.background = '';
button.style.color = '';
button.style.cursor = '';
button.disabled = 'disabled';
// explicitly style like a disabled button:
var computedStyle = window.getComputedStyle(button);
button.style.background = computedStyle.backgroundColor;
button.style.color = computedStyle.color;
button.style.cursor = computedStyle.cursor;
// re-enable, so the title will show up as hover-text in Firefox
// (see https://bugzilla.mozilla.org/show_bug.cgi?id=274626):
button.disabled = '';
// technically the button is "enabled" now, but it *looks* disabled, and it
// doesn't have an onclick event, so it's disabled for all normal purposes
};
GPE.anErrorOccurred = function(shortMsg, debugInfo)
{
var msg, link;
if(debugInfo)
{
link = newNode('a', {href:'#'}, 'here');
msg =
newNode
(
'p',
'An error occurred in MediaWiki:Gadget-PatrollingEnhancements.js: ',
newNode('tt', shortMsg),
' \u2014 click ',
link,
' for debugging information.'
);
}
else
msg =
newNode
(
'p',
'An error occurred in MediaWiki:Gadget-PatrollingEnhancements.js: ',
newNode('tt', shortMsg)
);
var bodyContent = $('div#bodyContent');
bodyContent.insertBefore(msg, bodyContent.firstChild);
if(link)
link.onclick = function () { alert(debugInfo); };
if(mediaWiki.config.get('wgUserName') == 'Ruakh')
alert(shortMsg + '\n' + debugInfo);
};
/* </pre>
==Patrol-buttons==
<pre> */
GPE.addPatrolButton = function(link, rcid)
{
var button = GPE.newButton('M', '#009', 'click to mark as patrolled');
link.parentNode.insertBefore(button, link.nextSibling);
link.parentNode.insertBefore(document.createTextNode(' · '), button);
button.onclick =
function ()
{
var token = mediaWiki.user.tokens.get('patrolToken');
$.post
(
'/w/api.php?format=json&action=patrol',
{ token: token, rcid: rcid },
function (data)
{
if(data.patrol)
GPE.disableButton(button, 'm', 'marked as patrolled');
else if(data.error)
{
var msg = data.error.code + ': ' + data.error.info;
if(data.error.code == 'badtoken')
msg += ': "' + token + '"';
alert(msg);
}
},
'json'
);
};
if(GPE.shouldAutoPatrol(link))
button.click();
// remove the exclamation points:
var abbreviations = link.parentNode.getElementsByTagName('abbr');
for(var i = abbreviations.length - 1; i >= 0; --i)
if((' '+abbreviations[i].className+' ').indexOf(' unpatrolled ') > -1)
abbreviations[i].parentNode.removeChild(abbreviations[i]);
};
addOnloadHook
(
function ()
{
if(mediaWiki.config.get('wgPageName') != 'Special:RecentChanges')
if(mediaWiki.config.get('wgPageName') != 'Special:NewPages')
if(mediaWiki.config.get('wgPageName') != 'Special:Watchlist')
if(mediaWiki.config.get('wgAction') != 'markpatrolled')
if(mediaWiki.config.get('wgAction') != 'delete')
if(mediaWiki.config.get('wgAction') != 'rollback')
return;
var links =
document.getElementById('bodyContent').getElementsByTagName('a');
for(var i = links.length - 1; i >= 0; --i)
{
var link = links[i];
if(link.href.search(/&rcid=\d+/) == -1)
continue;
var rcid = /&rcid=(\d+)/.exec(link.href)[1];
GPE.addPatrolButton(link, rcid);
}
}
);
/* </pre>
==Delete-buttons==
<pre> */
GPE.addDeleteButton = function(link, title)
{
var button = GPE.newButton('D', '#900', 'click to delete');
link.parentNode.insertBefore(button, link.nextSibling);
link.parentNode.insertBefore(document.createTextNode(' · '), button);
button.onclick =
function ()
{
var dropdownReason =
document.getElementById('deleteReasonsDropdown')
? document.getElementById('deleteReasonsDropdown').value
: '';
if(dropdownReason == 'other')
dropdownReason = '';
var textInputReason =
document.getElementById('deleteReasonTextInput').value;
var reason;
if(dropdownReason.length && textInputReason.length)
reason = dropdownReason + ': ' + textInputReason;
else if(dropdownReason.length || textInputReason.length)
reason = dropdownReason + textInputReason;
else
reason = GPE.deleteReasonIfBlank;
var token = mediaWiki.user.tokens.get('deleteToken');
$.post
(
'/w/api.php?format=json&action=delete',
{ title: title, token: token, reason: reason },
function (data)
{
if(data['delete'])
GPE.disableButton(button, 'd', 'deleted');
else if(data.error)
{
var msg = data.error.code + ': ' + data.error.info;
if(data.error.code == 'badtoken')
msg += ': "' + token + '"';
alert(msg);
}
},
'json'
);
};
};
GPE.addDeleteReasonInput = function()
{ var deleteReasonDiv =
( newNode
( 'div',
{ style:
'background:#900; color:#FFF; ' +
'position:fixed; bottom:0; right:0; margin-bottom:0'
},
'\u00A0Deletion reason:\u00A0'
)
);
deleteReasonDiv.title =
'the deletion reason (message/summary) to use when you click "D"';
var deleteReasonTextInput =
( newNode
( 'input',
{ type: 'text',
size: 80,
id: 'deleteReasonTextInput',
value: GPE.initialDeleteReason,
style: 'position:fixed; right:0; margin-bottom:0'
}
)
);
deleteReasonDiv.appendChild(deleteReasonTextInput);
document.getElementById('bodyContent').appendChild(deleteReasonDiv);
if(GPE.hideDeleteReasonDropdownOnWatchlist)
if(mediaWiki.config.get('wgPageName') == 'Special:Watchlist')
return;
// get canned messages from [[MediaWiki:Deletereason-dropdown]]:
$.getJSON
( '/w/api.php?format=json&action=query&meta=allmessages&ammessages=Deletereason-dropdown',
function (data)
{ var rawDeleteReasons = data.query.allmessages[0]['*'];
var deleteReasonsDropdown =
newNode('select',
{ id: 'deleteReasonsDropdown', style: 'vertical-align: bottom' });
deleteReasonsDropdown.appendChild
(newNode('option', { value: 'other' }, 'Other reason'));
var optGroup = deleteReasonsDropdown;
rawDeleteReasons.replace
( /^(\*\*?) *(.+)$/gm,
function (s, asterisks, text)
{ if(asterisks == '*')
deleteReasonsDropdown.appendChild
(optGroup = newNode('optgroup', { label: text }));
else // '**'
optGroup.appendChild(newNode('option', { value: text }, text));
}
);
deleteReasonDiv.insertBefore(deleteReasonsDropdown, deleteReasonTextInput);
deleteReasonDiv.insertBefore(newNode('br'), deleteReasonTextInput);
deleteReasonDiv.insertBefore(document.createTextNode('\u00A0'), deleteReasonTextInput);
}
);
};
addOnloadHook
( function ()
{
if(mediaWiki.config.get('wgPageName') != 'Special:RecentChanges')
if(mediaWiki.config.get('wgPageName') != 'Special:NewPages')
if(mediaWiki.config.get('wgPageName') != 'Special:Watchlist')
if(mediaWiki.config.get('wgAction') != 'markpatrolled')
if(mediaWiki.config.get('wgAction') != 'delete')
if(mediaWiki.config.get('wgAction') != 'rollback')
return;
$.getJSON
(
'/w/api.php?format=json&action=tokens&type=delete',
function (data)
{
var token = data.tokens.deletetoken;
if(! token || token.search(/^[0-9a-f]{32}\+\\$/) != 0)
return;
mediaWiki.user.tokens.set('deleteToken', token);
var links =
document.getElementById('bodyContent').getElementsByTagName('a');
var shouldAddDeleteReasonInput = false;
for(var i = links.length - 1; i >= 0; --i)
{
var link = links[i];
if(link.href.search(/#/) == -1) // skip within-page links (don't ask . . .)
if(link.href.search(/&rcid=\d+/) > -1) // only want patrolled edits
if(link.href.search(/&diff=\d+/) == -1) // only want new pages
{
GPE.addDeleteButton(link, link.title);
shouldAddDeleteReasonInput = true;
}
}
if(shouldAddDeleteReasonInput)
GPE.addDeleteReasonInput();
}
);
}
);
/* </pre>
==Special:Contributions==
<pre> */
addOnloadHook
(
function ()
{
if(mediaWiki.config.get('wgPageName').search(/^Special:Contributions(\/|$)/) != 0)
return;
var user = document.getElementById('t-contributions').firstChild.href.replace(/^.*?\/Special:Contributions\//, '');
$.getJSON
(
'/w/api.php?format=json&action=query&list=recentchanges&rcuser=' + user +
'&rcprop=ids|title&rcshow=!patrolled&rclimit=500&rctype=edit|new',
function (data)
{
data = data.query.recentchanges;
if(data.length == 0)
return;
var map = {};
for(var i = 0; i < data.length; ++i)
if(data[i].type == 'edit')
map[''+data[i].revid] = data[i].rcid;
else // 'new'
map[data[i].title] = data[i].rcid;
// first, handle the edit-links:
var links =
document.getElementById('bodyContent').getElementsByTagName('a');
for(var i = links.length - 1; i >= 0; --i)
{
var mapKey = /&diff=prev&oldid=(\d+)/.exec(links[i].href);
if(mapKey && map[mapKey[1]])
{
GPE.addPatrolButton(links[i], map[mapKey[1]]);
}
}
// then, handle the new-links:
var abbrs =
document.getElementById('bodyContent').getElementsByTagName('abbr');
for(var i = abbrs.length - 1; i >= 0; --i)
{
if(abbrs[i].className != 'newpage')
continue;
var link = abbrs[i];
while(link && link.nodeName.toUpperCase() != 'A')
link = link.nextSibling;
if(link && link.title && map[link.title])
{
GPE.addPatrolButton(link, map[link.title]);
if(link.href.indexOf('?') == -1)
link.href = link.href + '?rcid=' + map[link.title];
else
link.href = link.href + '&rcid=' + map[link.title];
}
}
// can't add the delete-links until after we've done the above, because we
// only want to add delete-links to unpatrolled edits:
var shouldAddDeleteReasonInput = false;
var abbrs =
document.getElementById('bodyContent').getElementsByTagName('abbr');
for(var i = abbrs.length - 1; i >= 0; --i)
{
if(abbrs[i].className != 'newpage')
continue;
var link = abbrs[i];
while(link && link.nodeName.toUpperCase() != 'A')
link = link.nextSibling;
if(! link) // shouldn't really happen
continue;
if(link.href.search(/\?(.*&)?rcid=\d+$/) == -1)
continue;
GPE.addDeleteButton(link, link.title);
shouldAddDeleteReasonInput = true;
}
if(shouldAddDeleteReasonInput)
GPE.addDeleteReasonInput();
}
);
}
);