MediaWiki:Gadget-morebits.js: Difference between revisions

v2.0-737-g19690a8: batchdel: Single-request actions; morebits: wgRestrictionEdit applies to current page, single-request delete/append/prepend; speedy, image, xfd, tag: Prevent warning oneself; batchdel, batchundel: Tweak; speedy: Tweak reason, tweak
imported>Amalthea
(v2.0-728-g051bc0c: unlink: checkboxes have shift-click support; morebits: function to give a set of checkboxes shift-click support)
(v2.0-737-g19690a8: batchdel: Single-request actions; morebits: wgRestrictionEdit applies to current page, single-request delete/append/prepend; speedy, image, xfd, tag: Prevent warning oneself; batchdel, batchundel: Tweak; speedy: Tweak reason, tweak)
Line 341:
// styles go on the label, doesn't make sense to style a checkbox/radio
if( current.style ) {
subnodelabel.setAttribute( 'style', current.style );
}
 
Line 1,973:
ctx.onSaveFailure = onFailure || emptyFunction;
 
// are we getting our edit token from mw.user.tokens?
if (!ctx.pageLoaded) {
var canUseMwUserToken = fnCanUseMwUserToken('edit');
 
if (!ctx.pageLoaded && !canUseMwUserToken) {
ctx.statusElement.error("Internal error: attempt to save a page that has not been loaded!");
ctx.onSaveFailure(this);
Line 1,984 ⟶ 1,987:
}
 
// shouldn't happen if canUseMwUserToken === true
if (ctx.fullyProtected && !ctx.suppressProtectWarning &&
!confirm('You are about to make an edit to the fully protected page "' + ctx.pageName +
Line 1,999 ⟶ 2,003:
title: ctx.pageName,
summary: ctx.editSummary,
token: canUseMwUserToken ? mw.user.tokens.get('editToken') : ctx.editToken,
watchlist: ctx.watchlistOption
};
Line 2,045 ⟶ 2,049:
if (['recreate', 'createonly', 'nocreate'].indexOf(ctx.createOption) !== -1) {
query[ctx.createOption] = '';
}
 
if (canUseMwUserToken && ctx.followRedirect) {
query.redirect = true;
}
 
Line 2,054 ⟶ 2,062:
this.append = function(onSuccess, onFailure) {
ctx.editMode = 'append';
 
ctx.onSaveSuccess = onSuccess;
if (fnCanUseMwUserToken('edit')) {
ctx.onSaveFailure = onFailure || emptyFunction;
this.loadsave(fnAutoSaveonSuccess, ctx.onSaveFailureonFailure);
} else {
ctx.onSaveSuccess = onSuccess;
ctx.onSaveFailure = onFailure || emptyFunction;
this.load(fnAutoSave, ctx.onSaveFailure);
}
};
 
this.prepend = function(onSuccess, onFailure) {
ctx.editMode = 'prepend';
 
ctx.onSaveSuccess = onSuccess;
if (fnCanUseMwUserToken('edit')) {
ctx.onSaveFailure = onFailure || emptyFunction;
this.loadsave(fnAutoSaveonSuccess, ctx.onSaveFailureonFailure);
} else {
ctx.onSaveSuccess = onSuccess;
ctx.onSaveFailure = onFailure || emptyFunction;
this.load(fnAutoSave, ctx.onSaveFailure);
}
};
 
Line 2,180 ⟶ 2,198:
}
 
if (fnCanUseMwUserToken('delete')) {
var query = {
fnProcessDelete();
action: 'query',
} else {
prop: 'info',
var query = {
inprop: 'protection',
intoken action: 'deletequery',
titles prop: ctx.pageName'info',
inprop: 'protection',
};
intoken: 'delete',
if (ctx.followRedirect) {
titles: ctx.pageName
query.redirects = ''; // follow all redirects
};
if (ctx.followRedirect) {
query.redirects = ''; // follow all redirects
}
 
ctx.deleteApi = new Morebits.wiki.api("retrieving delete token...", query, fnProcessDelete, ctx.statusElement, ctx.onDeleteFailure);
ctx.deleteApi.setParent(this);
ctx.deleteApi.post();
}
 
ctx.deleteApi = new Morebits.wiki.api("retrieving delete token...", query, fnProcessDelete, ctx.statusElement, ctx.onDeleteFailure);
ctx.deleteApi.setParent(this);
ctx.deleteApi.post();
};
 
Line 2,217 ⟶ 2,239:
}
 
// because of the way MW API interprets protection levels (absolute, not
// differential), we need to request protection levels from the server
var query = {
action: 'query',
Line 2,273 ⟶ 2,297:
};
 
/* Private member functions
/**
* Private member functions
*
* These are not exposed outside
*/
 
/**
* Determines whether we can save an API call by using the edit token sent with the page
* HTML, or whether we need to ask the server for more info (e.g. protection expiry).
*
* Currently only used for append, prepend, and deletePage.
*
* @param {string} action The action being undertaken, e.g. "edit", "delete".
*/
var fnCanUseMwUserToken = function(action) {
// API-based redirect resolution only works for action=query and
// action=edit in append/prepend modes (and section=new, but we don't
// really support that)
if (ctx.followRedirect && (action !== 'edit' ||
(ctx.editMode !== 'append' && ctx.editMode !== 'prepend'))) {
return false;
}
 
// do we need to fetch the edit protection expiry?
if (Morebits.userIsInGroup('sysop') && !ctx.suppressProtectWarning) {
// poor man's normalisation
if (Morebits.string.toUpperCaseFirstChar(mw.config.get('wgPageName')).replace(/ /g, '_').trim() !==
Morebits.string.toUpperCaseFirstChar(ctx.pageName).replace(/ /g, '_').trim()) {
return false;
}
 
var editRestriction = mw.config.get('wgRestrictionEdit');
if (!editRestriction || editRestriction.indexOf('sysop') !== -1) {
return false;
}
}
 
return !!mw.user.tokens.get('editToken');
};
 
// callback from loadSuccess() for append() and prepend() threads
Line 2,459 ⟶ 2,516:
 
ctx.statusElement.info("Edit conflict detected, reapplying edit");
if (fnCanUseMwUserToken('edit')) {
ctx.loadApi.post(); // reload the page and reapply the edit
ctx.saveApi.post(); // necessarily append or prepend, so this should work as desired
} else {
ctx.loadApi.post(); // reload the page and reapply the edit
}
 
// check for loss of edit token
Line 2,467 ⟶ 2,528:
ctx.statusElement.info("Edit token is invalid, retrying");
--Morebits.wiki.numberOfActionsLeft; // allow for normal completion if retry succeeds
if (fnCanUseMwUserToken('edit')) {
ctx.loadApi.post(); // reload
this.load(fnAutoSave, ctx.onSaveFailure); // try the append or prepend again
} else {
ctx.loadApi.post(); // reload the page and reapply the edit
}
 
// check for network or server error
Line 2,563 ⟶ 2,628:
 
var fnProcessDelete = function() {
var pageTitle, token;
var xml = ctx.deleteApi.getXML();
 
if ($(xml).find('page').attrfnCanUseMwUserToken('missingdelete') === "") {
token = mw.user.tokens.get('editToken');
ctx.statusElement.error("Cannot delete the page, because it no longer exists");
pageTitle = ctx.pageName;
ctx.onDeleteFailure(this);
} else {
return;
var xml = ctx.deleteApi.getXML();
}
 
if ($(xml).find('page').attr('missing') === "") {
// extract protection info
ctx.statusElement.error("Cannot delete the page, because it no longer exists");
var editprot = $(xml).find('pr[type="edit"]');
ctx.onDeleteFailure(this);
if (editprot.length > 0 && editprot.attr('level') === 'sysop' && !ctx.suppressProtectWarning &&
return;
!confirm('You are about to delete the fully protected page "' + ctx.pageName +
}
(editprot.attr('expiry') === 'infinity' ? '" (protected indefinitely)' : ('" (protection expiring ' + editprot.attr('expiry') + ')')) +
'. \n\nClick OK to proceed with the deletion, or Cancel to skip this deletion.')) {
ctx.statusElement.error("Deletion of fully protected page was aborted.");
ctx.onDeleteFailure(this);
return;
}
 
// extract protection info
var deleteToken = $(xml).find('page').attr('deletetoken');
var editprot = $(xml).find('pr[type="edit"]');
if (!deleteToken) {
if (editprot.length > 0 && editprot.attr('level') === 'sysop' && !ctx.suppressProtectWarning &&
ctx.statusElement.error("Failed to retrieve delete token.");
!confirm('You are about to delete the fully protected page "' + ctx.pageName +
ctx.onDeleteFailure(this);
(editprot.attr('expiry') === 'infinity' ? '" (protected indefinitely)' : ('" (protection expiring ' + editprot.attr('expiry') + ')')) +
return;
'. \n\nClick OK to proceed with the deletion, or Cancel to skip this deletion.')) {
ctx.statusElement.error("Deletion of fully protected page was aborted.");
ctx.onDeleteFailure(this);
return;
}
 
token = $(xml).find('page').attr('deletetoken');
if (!token) {
ctx.statusElement.error("Failed to retrieve delete token.");
ctx.onDeleteFailure(this);
return;
}
 
pageTitle = $(xml).find('page').attr('title');
}
 
var query = {
'action': 'delete',
'title': $(xml).find('page').attr('title')pageTitle,
'token': deleteTokentoken,
'reason': ctx.editSummary
};
Line 2,616 ⟶ 2,690:
ctx.deleteProcessApi.post(); // give it another go!
 
} else if ( errorCode === "missingtitle" ) {
ctx.statusElement.error("Cannot delete the page, because it no longer exists");
if (ctx.onDeleteFailure) {
ctx.onDeleteFailure.call(this, ctx.deleteProcessApi); // invoke callback
}
// hard error, give up
} else {
Anonymous user