User:Perhelion/massrename.js
Jump to navigation
Jump to search
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.
Documentation for this user script can be added at User:Perhelion/massrename. |
/**
@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: 17:00, 12 June 2020 (UTC)
@required modules: mediawiki.util, mediawiki.user, jquery.ui, ext.gadget.libCommons, ext.gadget.libJQuery, ext.gadget.libUtil, mediawiki.util, ext.gadget.libGlobalReplace, ext.gadget.AjaxQuickDelete
* <nowiki>
**/
/* eslint indent:["error","tab",{"outerIIFEBody":0}], one-var:0, vars-on-top:0, no-console:0, camelcase:0 */
/* 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);
}).fail(function (t, r, q) {
mw.log.warn(t, r, q);
});
}
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();
},
// text, result, query
errCb: function (t, r, q) {
AQD.fail(t);
console.error(t, r, q, '\n++++\nTask: ' + AQD.currentTask + '\n:NextTask: ' + AQD.tasks[0] + '\n:LastTask: ' + AQD.tasks.pop());
},
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);
}
var renameFile = function (oldFile, newFile, fullReason, delinkerOptOut, doPrompt) {
if (!newFile)
return this.nextQueue();
// initialize:
this.pageName = mw.config.get('wgFormattedNamespaces')[6] + ':' + oldFile.replace(/_/g, ' ');
// this.tasks = [];
this.possibleDestination = newFile;
this.replaceUsingCORS = delinkerOptOut;
this.details = undefined;
this.destination = this.cleanFileName(newFile);
// console.log("Move: ", this.pageName, "→", this.destination);
// FIXME?: if ($('#globalusage').length || !$('#mw-imagepage-nolinkstoimage').length) ; this needs an extra ajax request
this.inUse = this.mrgAndReplace; // Move only?
this.addTask('getMoveToken');
if (doPrompt)
this.addTask('promptForMoveTarget');
// Let's be sure we have a fresh token and the latest MIME-Info
// this.addTask('getMoveToken');
this.fileNameExistsCB = 'fileExists'; // possible break
this.addTask('doesFileExist');
this.addTask('movePage');
this.addTask('removeTemplate');
this.addTask('queryRedirects');
this.addTask('replaceUsage');
this.nextTask();
this.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,
replaceStr = window.mrgDestination = AQD.mrgRep,
reason = window.mrgReason = AQD.reason,
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, ''),
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();
}
var init = function () {
var possibleName = window.mrgFind || '',
possibleDestination = window.mrgDestination || mw.config.get('wgTitle'),
possibleReason = window.mrgReason || '[[COM:FR|rename criterion 2]]'; // Prefix c: gets automatic added if global replace
this.initialize();
this.inUse = 0;
this.addTask('promptForMassMove');
this.addTask('mrgQueue');
this.movePage = movePage;
this.renameFile = renameFile;
this.fileExists = function (r) {
if (typeof r !== 'string')
r = this.destination;
r += ': ' + this.i18n.moveOtherDestination;
this.disableReport = true;
this.fail(r);
throw new Error(r);
};
this.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.call(this);
// refreshProgressbar();
});
};
this.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 unknown bug, declares fully randomly as empty if not so
}, {
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)
$('#AjaxQuestion8').prop('disabled', true);
$('#AjaxDeleteContainer').find('br+br').remove(); // Hack: double-lines looks ugly
};
this.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(this.pageName, this.destination, reasonShort,
this.reason, this.replaceUsingCORS)
.fail(function (r) {
AQD.showProgress(r);
if (console.warn) console.warn(r);
})
.done(function () {
refreshProgressbar();
AQD.nextTask();
}).progress(function (r) {
AQD.showProgress(r);
console.log(r);
}).fail(function (r) {
AQD.disableReport = true;
AQD.fail(r);
if (console.warn) console.warn(r);
AQD.showProgress();
});
};
this.nextTask();
};
$(function () {
// Only on categories
if (mw.config.get('wgNamespaceNumber') === 14) {
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'))
.on('click', function (e) {
e.preventDefault();
mw.loader.using(['jquery.ui', 'ext.gadget.libGlobalReplace',
'ext.gadget.libAPI', 'ext.wikiEditor'], function () { init.call(AQD); });
});
}
});
}
});
}(mediaWiki, jQuery));
// </nowiki> EOF