MediaWiki:Gadget-morebits.js: Difference between revisions
Content added Content deleted
imported>Amorymeltzer (Repo at 79b422a: Protection error needn't be full protection) |
(Repo at 53b6bf4: convert to use Morebits.wiki.page.undeletePage; Add undeletePage to morebits.wiki.page; Remove string trimming shims) |
||
Line 1,020: | Line 1,020: | ||
* **************** String; Morebits.string **************** |
* **************** String; Morebits.string **************** |
||
*/ |
*/ |
||
if (!String.prototype.trimLeft) { |
|||
String.prototype.trimLeft = function stringPrototypeLtrim( ) { |
|||
return this.replace( /^[\s]+/g, "" ); |
|||
}; |
|||
} |
|||
if (!String.prototype.trimRight) { |
|||
String.prototype.trimRight = function stringPrototypeRtrim( ) { |
|||
return this.replace( /[\s]+$/g, "" ); |
|||
}; |
|||
} |
|||
if (!String.prototype.trim) { |
|||
String.prototype.trim = function stringPrototypeTrim( ) { |
|||
return this.trimRight().trimLeft(); |
|||
}; |
|||
} |
|||
Morebits.string = { |
Morebits.string = { |
||
Line 1,108: | Line 1,090: | ||
*/ |
*/ |
||
formatReasonText: function( str ) { |
formatReasonText: function( str ) { |
||
var result = str.toString(). |
var result = str.toString().trim(); |
||
var unbinder = new Morebits.unbinder(result); |
var unbinder = new Morebits.unbinder(result); |
||
unbinder.unbind("<no" + "wiki>", "</no" + "wiki>"); |
unbinder.unbind("<no" + "wiki>", "</no" + "wiki>"); |
||
Line 1,641: | Line 1,623: | ||
* |
* |
||
* save([onSuccess], [onFailure]): Saves the text set via setPageText() for the page. |
* save([onSuccess], [onFailure]): Saves the text set via setPageText() for the page. |
||
* |
* Must be preceded by calling load(). |
||
* Warning: Calling save() can result in additional calls to the previous load() callbacks to |
* Warning: Calling save() can result in additional calls to the previous load() callbacks to |
||
* recover from edit conflicts! |
* recover from edit conflicts! |
||
Line 1,656: | Line 1,638: | ||
* |
* |
||
* deletePage(onSuccess, [onFailure]): Deletes a page (for admins only) |
* deletePage(onSuccess, [onFailure]): Deletes a page (for admins only) |
||
* |
|||
* undeletePage(onSuccess, [onFailure]): Undeletes a page (for admins only) |
|||
* |
* |
||
* protect(onSuccess, [onFailure]): Protects a page |
* protect(onSuccess, [onFailure]): Protects a page |
||
Line 1,801: | Line 1,785: | ||
onDeleteSuccess: null, |
onDeleteSuccess: null, |
||
onDeleteFailure: null, |
onDeleteFailure: null, |
||
onUndeleteSuccess: null, |
|||
onUndeleteFailure: null, |
|||
onProtectSuccess: null, |
onProtectSuccess: null, |
||
onProtectFailure: null, |
onProtectFailure: null, |
||
Line 1,815: | Line 1,801: | ||
deleteApi: null, |
deleteApi: null, |
||
deleteProcessApi: null, |
deleteProcessApi: null, |
||
undeleteApi: null, |
|||
undeleteProcessApi: null, |
|||
protectApi: null, |
protectApi: null, |
||
protectProcessApi: null, |
protectProcessApi: null, |
||
Line 2,422: | Line 2,410: | ||
ctx.deleteApi.setParent(this); |
ctx.deleteApi.setParent(this); |
||
ctx.deleteApi.post(); |
ctx.deleteApi.post(); |
||
} |
|||
}; |
|||
/** |
|||
* Undeletes a page (for admins only) |
|||
* @param {Function} onSuccess - callback function to run on success |
|||
* @param {Function} [onFailure] - callback function to run on failure (optional) |
|||
*/ |
|||
this.undeletePage = function(onSuccess, onFailure) { |
|||
ctx.onUndeleteSuccess = onSuccess; |
|||
ctx.onUndeleteFailure = onFailure || emptyFunction; |
|||
// if a non-admin tries to do this, don't bother |
|||
if (!Morebits.userIsInGroup('sysop')) { |
|||
ctx.statusElement.error("Cannot undelete page: only admins can do that"); |
|||
ctx.onUndeleteFailure(this); |
|||
return; |
|||
} |
|||
if (!ctx.editSummary) { |
|||
ctx.statusElement.error("Internal error: undelete reason not set before undelete (use setEditSummary function)!"); |
|||
ctx.onUndeleteFailure(this); |
|||
return; |
|||
} |
|||
if (fnCanUseMwUserToken('undelete')) { |
|||
fnProcessUndelete.call(this, this); |
|||
} else { |
|||
var query = { |
|||
action: 'query', |
|||
prop: 'info', |
|||
inprop: 'protection', |
|||
intoken: 'undelete', |
|||
titles: ctx.pageName |
|||
}; |
|||
ctx.undeleteApi = new Morebits.wiki.api("retrieving undelete token...", query, fnProcessUndelete, ctx.statusElement, ctx.onUndeleteFailure); |
|||
ctx.undeleteApi.setParent(this); |
|||
ctx.undeleteApi.post(); |
|||
} |
} |
||
}; |
}; |
||
Line 2,898: | Line 2,924: | ||
// check for "Database query error" |
// check for "Database query error" |
||
if ( errorCode === "internal_api_error_DBQueryError" && ctx.retries++ < ctx.maxRetries ) { |
if ( errorCode === "internal_api_error_DBQueryError" && ctx.retries++ < ctx.maxRetries ) { |
||
ctx.statusElement.info("Database query error, retrying"); |
ctx.statusElement.info("Database query error, retrying"); |
||
--Morebits.wiki.numberOfActionsLeft; // allow for normal completion if retry succeeds |
--Morebits.wiki.numberOfActionsLeft; // allow for normal completion if retry succeeds |
||
ctx.deleteProcessApi.post(); // give it another go! |
ctx.deleteProcessApi.post(); // give it another go! |
||
} else if ( errorCode === "badtoken" ) { |
} else if ( errorCode === "badtoken" ) { |
||
// this is pathetic, but given the current state of Morebits.wiki.page it would |
// this is pathetic, but given the current state of Morebits.wiki.page it would |
||
Line 2,910: | Line 2,934: | ||
ctx.onDeleteFailure.call(this, this, ctx.deleteProcessApi); |
ctx.onDeleteFailure.call(this, this, ctx.deleteProcessApi); |
||
} |
} |
||
} else if ( errorCode === "missingtitle" ) { |
} else if ( errorCode === "missingtitle" ) { |
||
ctx.statusElement.error("Cannot delete the page, because it no longer exists"); |
ctx.statusElement.error("Cannot delete the page, because it no longer exists"); |
||
if (ctx.onDeleteFailure) { |
if (ctx.onDeleteFailure) { |
||
ctx.onDeleteFailure.call(this, ctx.deleteProcessApi); // invoke callback |
ctx.onDeleteFailure.call(this, ctx.deleteProcessApi); // invoke callback |
||
} |
} |
||
// hard error, give up |
// hard error, give up |
||
} else { |
} else { |
||
ctx.statusElement.error( "Failed to delete the page: " + ctx.deleteProcessApi.getErrorText() ); |
ctx.statusElement.error( "Failed to delete the page: " + ctx.deleteProcessApi.getErrorText() ); |
||
if (ctx.onDeleteFailure) { |
if (ctx.onDeleteFailure) { |
||
ctx.onDeleteFailure.call(this, ctx.deleteProcessApi); // invoke callback |
ctx.onDeleteFailure.call(this, ctx.deleteProcessApi); // invoke callback |
||
} |
|||
} |
|||
}; |
|||
var fnProcessUndelete = function() { |
|||
var pageTitle, token; |
|||
// The whole handling of tokens in Morebits is outdated (#615) |
|||
// but has generally worked since intoken has been deprecated |
|||
// but remains. intoken does not, however, take undelete, so |
|||
// fnCanUseMwUserToken('undelete') is no good. Everything |
|||
// except watching and patrolling should eventually use csrf, |
|||
// but until then (#615) the stupid hack below should work for |
|||
// undeletion. |
|||
if (fnCanUseMwUserToken('undelete')) { |
|||
token = mw.user.tokens.get('editToken'); |
|||
pageTitle = ctx.pageName; |
|||
} else { |
|||
var xml = ctx.undeleteApi.getXML(); |
|||
if ($(xml).find('page').attr('missing') !== "") { |
|||
ctx.statusElement.error("Cannot undelete the page, because it already exists"); |
|||
ctx.onUndeleteFailure(this); |
|||
return; |
|||
} |
|||
// extract protection info |
|||
var editprot = $(xml).find('pr[type="create"]'); |
|||
if (editprot.length > 0 && editprot.attr('level') === 'sysop' && !ctx.suppressProtectWarning && |
|||
!confirm('You are about to undelete the fully create protected page "' + ctx.pageName + |
|||
(editprot.attr('expiry') === 'infinity' ? '" (protected indefinitely)' : ('" (protection expiring ' + editprot.attr('expiry') + ')')) + |
|||
'. \n\nClick OK to proceed with the undeletion, or Cancel to skip this undeletion.')) { |
|||
ctx.statusElement.error("Undeletion of fully create protected page was aborted."); |
|||
ctx.onUndeleteFailure(this); |
|||
return; |
|||
} |
|||
// KLUDGE: |
|||
token = mw.user.tokens.get('editToken'); |
|||
pageTitle = ctx.pageName; |
|||
} |
|||
var query = { |
|||
'action': 'undelete', |
|||
'title': pageTitle, |
|||
'token': token, |
|||
'reason': ctx.editSummary |
|||
}; |
|||
if (ctx.watchlistOption === 'watch') { |
|||
query.watch = 'true'; |
|||
} |
|||
ctx.undeleteProcessApi = new Morebits.wiki.api("undeleting page...", query, ctx.onUndeleteSuccess, ctx.statusElement, fnProcessUndeleteError); |
|||
ctx.undeleteProcessApi.setParent(this); |
|||
ctx.undeleteProcessApi.post(); |
|||
}; |
|||
// callback from undeleteProcessApi.post() |
|||
var fnProcessUndeleteError = function() { |
|||
var errorCode = ctx.undeleteProcessApi.getErrorCode(); |
|||
// check for "Database query error" |
|||
if ( errorCode === "internal_api_error_DBQueryError" && ctx.retries++ < ctx.maxRetries ) { |
|||
ctx.statusElement.info("Database query error, retrying"); |
|||
--Morebits.wiki.numberOfActionsLeft; // allow for normal completion if retry succeeds |
|||
ctx.undeleteProcessApi.post(); // give it another go! |
|||
} else if ( errorCode === "badtoken" ) { |
|||
// this is pathetic, but given the current state of Morebits.wiki.page it would |
|||
// be a dog's breakfast to try and fix this |
|||
ctx.statusElement.error("Invalid token. Please refresh the page and try again."); |
|||
if (ctx.onUndeleteFailure) { |
|||
ctx.onUndeleteFailure.call(this, this, ctx.undeleteProcessApi); |
|||
} |
|||
} else if ( errorCode === "cantundelete" ) { |
|||
ctx.statusElement.error("Cannot undelete the page, either because there are no revisions to undelete or because it has already been undeleted"); |
|||
if (ctx.onUndeleteFailure) { |
|||
ctx.onUndeleteFailure.call(this, ctx.undeleteProcessApi); // invoke callback |
|||
} |
|||
// hard error, give up |
|||
} else { |
|||
ctx.statusElement.error( "Failed to undelete the page: " + ctx.undeleteProcessApi.getErrorText() ); |
|||
if (ctx.onUndeleteFailure) { |
|||
ctx.onUndeleteFailure.call(this, ctx.undeleteProcessApi); // invoke callback |
|||
} |
} |
||
} |
} |
||
Line 3,723: | Line 3,827: | ||
* |
* |
||
* There are sample batchOperation implementations using Morebits.wiki.page in |
* There are sample batchOperation implementations using Morebits.wiki.page in |
||
* twinklebatchdelete.js, |
* twinklebatchdelete.js, twinklebatchundelete.js, and twinklebatchprotect.js. |
||
*/ |
*/ |
||