User:Perhelion/massrename.js
Note: After saving, you have to bypass your browser's cache to see the changes. Internet Explorer: press Ctrl-F5, Mozilla: hold down Shift while clicking Reload (or press Ctrl-Shift-R), Opera/Konqueror: press F5, Safari: hold down Shift + Alt while clicking Reload, Chrome: hold down Shift while clicking Reload
/**
@description: Support for file mass-move/rename at Wikimedia Commons.
@author: User:Legoktm/massrename.js - 2014
@author: User:Perhelion - 2017 (Commons lib, jsHint ready, regex support)
@revision: 20:03, 24 October 2017 (UTC)
@required modules: mediawiki.util, mediawiki.user, jquery.ui.dialog, ext.gadget.libCommons, ext.gadget.libJQuery, ext.gadget.libUtil, mediawiki.RegExp, query.ui.progressbar, ext.gadget.libGlobalReplace, ext.gadget.AjaxQuickDelete
* <nowiki>
**/
/*global jQuery, mediaWiki*/
(function (mw, $) {
"use strict";
var mrg_queue, // In-progress queue
mrg_count, // Total count
mrg_done, // Already moved count
AQD;
function fetch_cat_members(name, cont, callback) {
var params = {
action: 'query',
rawcontinue: 1,
list: 'categorymembers',
cmtitle: 'Category:' + name,
cmnamespace: 6,
cmtype: 'file',
cmlimit: 'max'
};
if (cont)
params.cmcontinue = cont;
(new mw.Api()).get(params).done(function (data) {
if (data['query-continue']) {
fetch_cat_members(
name,
data['query-continue'].categorymembers.cmcontinue,
callback);
}
callback(data.query.categorymembers);
});
}
function refreshProgressbar() {
var $sel = $('#mrg-progress');
if (!$sel.length && AQD.progressDialog) {
$sel = $('<div>', {
id: 'mrg-progress'
}).progressbar();
AQD.progressDialog.append($sel);
}
$sel.progressbar('option', {
'max': mrg_count,
'value': mrg_done
});
}
function movePage() { // Like 'movePage' on AQD
mw.user.tokens.set('csrfToken', AQD.csrftoken);
// Some users don't get it: They want to move pages to themselves.
if (AQD.pageName === AQD.destination)
return AQD.nextTask();
var moveArgs = {
cb: function () {
mrg_done++;
refreshProgressbar();
AQD.nextTask();
},
// r-result, query, text
errCb: function (t, r, q) {
console.log(t, r, q, '\n++++\nTask: ' + AQD.currentTask + '\n:NextTask: ' + AQD.tasks[0] + '\n:LastTask: ' + AQD.tasks[AQD.tasks.length - 1]);
AQD.fail(t);
},
from: AQD.pageName,
to: AQD.destination,
reason: AQD.reason,
movetalk: true,
// No change won't watch the file under the new location
// even if it was watched under the old location
watchlist: AQD.pageWasWatched ? 'watch' : 'nochange'
};
// Option to not leave a redirect behind, MediaWiki default does leave one behind
// Just like movetalk, an empty parameter sets it to true (true to not leave a redirect behind)
if (!AQD.wpLeaveRedirect)
moveArgs.noredirect = true;
AQD.showProgress(AQD.i18n.movingFile + ": " + AQD.pageName);
mw.libs.commons.api.movePage(moveArgs);
}
function renameFile(oldFile, newFile, fullReason, delinkerOptOut, doPrompt) {
if (!newFile)
return AQD.nextQueue();
// initialize:
AQD.pageName = "File:" + oldFile.replace(/_/g, ' ');
// AQD.tasks = [];
AQD.possibleDestination = newFile;
AQD.replaceUsingCORS = delinkerOptOut;
AQD.details = undefined;
AQD.destination = AQD.cleanFileName(newFile);
// console.log("Move: ", AQD.pageName, "→", AQD.destination);
// FIXME?: if ($('#globalusage').length || !$('#mw-imagepage-nolinkstoimage').length) ; this needs an extra ajax request
AQD.inUse = AQD.mrgAndReplace; // Move only?
AQD.addTask('getMoveToken');
if (doPrompt) {
AQD.addTask('promptForMoveTarget');
// Let's be sure we have a fresh token and the latest MIME-Info
// AQD.addTask('getMoveToken');
}
AQD.fileNameExistsCB = 'fileExists'; // possible break
AQD.addTask('doesFileExist');
AQD.addTask('movePage');
AQD.addTask('removeTemplate');
AQD.addTask('queryRedirects');
AQD.addTask('replaceUsage');
AQD.nextTask();
AQD.addTask('nextQueue');
}
function escapeRegExp(str) {
// From MDN
if (!$.trim(str)) {
str = "Fail: Please fill a source name!";
alert(str);
throw new Error(str);
}
return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
}
function run() {
if (!mrg_queue.length)
return alert("Fail: No file found!");
// Save values
var find = window.mrgFind = AQD.mrgFind;
var replaceStr = window.mrgDestination = AQD.mrgRep;
var reason = window.mrgReason = AQD.reason;
var flags = 'g';
if (!AQD.mrgReCase)
flags += 'i';
if (!AQD.mrgRegex)
find = escapeRegExp(find);
try {
find = new RegExp(find, flags);
} catch (e) {
AQD.showProgress(mw.msg('wikieditor-toolbar-tool-replace-invalidregex',
e.message));
AQD.progressDialog.dialog({
dialogClass: "ajaxDeleteError"
});
}
// if (mw.config.get('debug')) console.log("search ", find);
AQD.nextQueue = function () {
if (!mrg_queue.length) {
if (!mrg_done)
alert("Fail: No match!");
return AQD.showProgress();
}
var oldFile = mrg_queue.shift().replace(/^(?:Image|File):/i, '');
var newFile = oldFile.replace(find, replaceStr);
if (oldFile && newFile && oldFile !== newFile) {
window.setTimeout(function () {
// TODO: Hack to omit file-extension check
AQD.mimeFileExtension = oldFile.toLowerCase().replace(/.*?\.(\w{2,5})$/, '$1');
AQD.possibleReason = AQD.reason = AQD.cleanReason(reason);
AQD.renameFile(oldFile, newFile, reason, AQD.delinkerOptOut, AQD.mrgRePrompt);
}, 200);
} else
AQD.nextQueue();
};
AQD.nextQueue();
}
function init() {
var possibleName = window.mrgFind || "";
var possibleDestination = window.mrgDestination || mw.config.get('wgTitle');
var possibleReason = window.mrgReason || "[[COM:FR|rename criterion 2]]"; // Prefix c: gets automatic added if global replace
AQD.initialize();
AQD.inUse = 0;
AQD.addTask('promptForMassMove');
AQD.addTask('mrgQueue');
AQD.movePage = movePage;
AQD.renameFile = renameFile;
AQD.fileExists = function (r) {
if (typeof r !== 'string')
r = this.destination;
r += ': ' + this.i18n.moveOtherDestination;
this.disableReport = true;
this.fail(r);
throw new Error(r);
};
AQD.mrgQueue = function () {
mrg_queue = [];
mrg_count = mrg_done = 0;
fetch_cat_members(this.mrgCat, undefined, function (members) {
var old_length = mrg_queue.length;
$.each(members, function (_, v) {
mrg_queue.push(v.title);
});
mrg_count += members.length;
if (!old_length)
run();
// refreshProgressbar();
});
};
AQD.promptForMassMove = function () {
this.showProgress();
this.prompt([{
message: "Source category name (without namespace):",
prefill: mw.config.get('wgTitle'),
returnvalue: 'mrgCat',
noEmpty: true
}, {
message: mw.msg("wikieditor-toolbar-tool-replace-search"),
prefill: possibleName,
returnvalue: 'mrgFind',
noEmpty: true
}, {
message: mw.msg("wikieditor-toolbar-tool-replace-replace"),
prefill: this.possibleDestination || possibleDestination,
returnvalue: 'mrgRep',
noEmpty: true
}, {
message: "Reason / " + this.i18n.reasonForMove,
prefill: $.trim((this.reason || this.possibleReason || '').replace(/['\s]{2,}/g, '')) || possibleReason,
returnvalue: 'reason',
cleanUp: true,
noEmpty: true
}, {
message: mw.msg("wikieditor-toolbar-tool-replace-case"),
prefill: true,
returnvalue: 'mrgReCase',
type: 'checkbox'
}, {
message: mw.msg("wikieditor-toolbar-tool-replace-regex"),
returnvalue: 'mrgRegex',
type: 'checkbox'
}, {
message: "Do prompt every file?", // TODO: i18n string
prefill: false,
returnvalue: 'mrgRePrompt',
type: 'checkbox'
}, {
message: this.i18n.dropdownMove,
prefill: true,
returnvalue: 'mrgAndReplace',
type: 'checkbox'
}, { // 8
message: this.i18n.leaveRedirect,
// prefill: true,
returnvalue: 'wpLeaveRedirect',
type: 'checkbox'
}, {
message: this.i18n.useCORSForReplace,
prefill: !window.aqdCORSOptOut,
returnvalue: 'delinkerOptOut',
type: 'checkbox'
}
], 'File-MassRename-Gadget');
if (this.inUse || this.userRights === 'filemover')
$('#AjaxQuestion8').prop('disabled', true);
$('#AjaxDeleteContainer').find('br+br').remove(); // Hack: double-lines looks ugly
};
AQD.replaceUsage = function (reasonShort) {
this.showProgress(this.i18n.replacingUsage);
if (typeof reasonShort !== 'string') {
reasonShort = '[[COM:Duplicate|Duplicate]]:';
if (!this.details) {
this.reason = this.reason.replace(/\[\[Commons:File[_ ]renaming[^\[\]]*\]\]:? ?/i, '');
reasonShort = '[[COM:FR|File renamed]]:';
}
}
mw.libs.globalReplace(AQD.pageName, AQD.destination, reasonShort,
AQD.reason, AQD.replaceUsingCORS)
.done(function () {
refreshProgressbar();
AQD.nextTask();
}).progress(function (r) {
AQD.showProgress(r);
console.log(r);
}).fail(function (r) {
AQD.disableReport = true;
AQD.fail(r);
console.log(r);
});
};
AQD.nextTask();
}
$(function () {
if ($.inArray(mw.config.get('wgNamespaceNumber'), [14]) !== -1) // Only on categories
mw.loader.using(['mediawiki.util', 'ext.gadget.AjaxQuickDelete'], function () {
AQD = window.AjaxQuickDelete;
if (AQD.userRights === 'filemover' || AQD.userRights === 'sysop') {
$(mw.util.addPortletLink('p-cactions', '#', 'MassRename', 'ca-mrg', 'Rename stuff'))
.click(function (e) {
e.preventDefault();
mw.loader.using(['jquery.ui.dialog', 'jquery.ui.progressbar', 'ext.gadget.libGlobalReplace',
'ext.gadget.libAPI', 'jquery.wikiEditor.toolbar.i18n'], init);
});
}
});
});
}(mediaWiki, jQuery)); // </nowiki> EOF