User:Pan Tau/justReplace.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 just replace a file global (without move).
* @usage / configuration:
	// Sets a prefilled custom reason (e.g. duplicate)
	window.GlobalReplaceDefaultReason = "duplicate"; 

	// Sets the checkboxes for namespace exclusion default off
	window.aqdUserNsOptOut = 1;
	window.aqdProjectNsOptOut = 1;
	window.aqdTalkNsOptOut = 1;

* @revision: 17:19, 14 January 2018 (UTC)
* @author: User:Perhelion - 2017 (Commons lib, jsHint ready, regex support)
* @author: Adapted from [[MediaWiki:Gadget-libGlobalReplace.js]] and [[User:Sreejithk2000/JustReplace.js]]
* @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>
**/
/*global jQuery, mediaWiki*/
/*jshint*/

(function (mw, $) {
"use strict";
// Guard against multiple inclusions
if (mw.libs.JustReplace) return;

var AQD,
	pageName,
	file;

mw.libs.JustReplace = function (page) {
	file = page;
	// Parameter for external usage and also from non file namespace
	if (typeof file === 'string')
		pageName = file;
	else {
		// if (typeof file === 'boolean')
		AQD.initialize();
		file = false;
	}
	AQD.pageName = pageName;
	AQD.possibleReason = (window.GlobalReplaceDefaultReason || AQD.possibleReason || '').replace(/\"/, "&quot;");
	
	if (!file) {
		AQD.addTask('loadWMmessages');
		if (!mw.config.get('wgIsRedirect') || document.getElementById('file'))
			AQD.addTask('getMoveToken'); // only works if source file exists
		AQD.addTask('promptForReplaceTarget');
		AQD.addTask('getFileExtension');
		AQD.fileNameExistsCB = 'fileExists';
		AQD.addTask('doesFileExist'); // AQD.destination get's now file-namspace
		AQD.addTask('declineReplace');
	}
	AQD.addTask('replaceUsage');
	if (!file) {
		AQD.addTask('queryRedirects');
		AQD.addTask('replaceRedirects');
		AQD.addTask('reloadPage');
	}
	AQD.nextTask();

	AQD.replaceUsage = function (reasonShort) {
		AQD.showProgress(AQD.i18n.replacingUsage);
		reasonShort = (typeof reasonShort === 'string') ? reasonShort : ''; // TODO '[[COM:Duplicate|Duplicate]]:';
		var notOnNs = [];
		if (AQD.notUserNS) notOnNs.push(2);
		if (AQD.notProjectNS) notOnNs.push(4);
		if (AQD.notTalkNS) notOnNs = notOnNs.concat([1, 3, 5, 7, 9, 11, 13, 15]);
		if (!notOnNs.length) notOnNs = '';

		mw.loader.using(['ext.gadget.libGlobalReplace'], function () {
			if (AQD.replaceUsingCORS)
				mw.libs.globalReplace(pageName, AQD.destination, reasonShort,
					AQD.reason, AQD.replaceUsingCORS, notOnNs
				).fail(function (r) {
					AQD.showProgress(r);
					if (console.warn) console.warn(r);
				}).done(function (r) {
					console.log(r);
					if (!AQD.tasks.length) {
						// mw.log("Redirect replaced");
						AQD.addTask('reloadPage');
					}
					AQD.nextTask();
				}).progress(function (r) {
					console.log(r);
					AQD.showProgress(r);
				}).fail(function (r) {
					if (console.warn) console.warn("GlobalReplace fail, try again.", r);
					AQD.disableReport = true;
					// mw.notify( r, { title: "replaceUsingCORS", type: "warn" } );
					AQD.fail(r);
					AQD.showProgress();
					// throw new Error(r);
				});
			else
				mw.libs.globalReplaceDelinker(pageName, AQD.destination, reasonShort + ' ' + AQD.reason, function () {
					AQD.nextTask();
				}, function (r) {
					mw.notify(r, { title: "Global CommonsDelinker", type: "warn" });
					console.log(r);
				});
		});
	};

	/** TODO: replace with updateRedirects from AQD, getFileExtension is added **/
	AQD.replaceRedirectsCB = function (result) {
		this.redirectsToUpdate = 0;
		if (result.query && result.query.pages) {
			this.tasks.pop(); // remove last task, 'reloadPage'
			AQD.showProgress("Replacing redirects");
			$.each(result.query.pages, function (id, pg) {
				var rv = pg.revisions[0];
				if (!rv || !rv['*'])
					return;
				// Replace only redirects with different mimetype
				if (!AQD.checkFileExt(pg.title, AQD.destination, true))
					return mw.log("Redirect replace skipped, same mimetype", pg.title);
				AQD.addTask('updateRedirectsCB'); // go in race condition but buffer for next
				AQD.redirectsToUpdate++;
				console.log("Update redirects", pg, AQD.redirectsToUpdate, AQD.tasks);
				mw.libs.JustReplace(pg.title);
			});
			// AQD.addTask('reloadPage'); // add last task again!? but it comes to late
		}
		if (!AQD.redirectsToUpdate) {
			AQD.destination = undefined; // Reset?
			if (AQD.tasks.length)
				AQD.nextTask();
		}
	};

	/**
	 **  Replace also the redirects to the current page
	 **  when moving or processing dupes immediately
	 **/
	AQD.replaceRedirects = function () {
		AQD.queryRedirectsCB = 'replaceRedirectsCB';
		AQD.queryRedirects(); // now in replace mode
	};
};

function init(file) {
	AQD.loadWMmessages = function () {
		$.when(mw.loader.using(['mediawiki.api', 'mediawiki.jqueryMsg']), $.ready)
			.then(function () {
				return new mw.Api().loadMessagesIfMissing([
					'Translate-import-err-invalid-title',
					'Wikilove-err-gallery-again',
					// 'Whatlinkshere',
					'Createacct-reason'
				]);
			}).then(function () {
				AQD.nextTask();
		});
	};
	
	AQD.promptForReplaceTarget = function () {
		this.showProgress();
		this.prompt([{
					message: this.i18n.moveDestination,
					prefill: this.possibleDestination || this.destination || pageName,
					returnvalue: 'destination',
					cleanUp: false,
					noEmpty: true
				}, {
					message:  mw.msg('Createacct-reason'), // i18n
					prefill: $.trim((this.reason || this.possibleReason || '').replace(/'{2,}/g, '').replace(/\s{2,}/g, ' ')),
					returnvalue: 'reason',
					cleanUp: true,
					noEmpty: false
				}, {
					message: 'Ignore file check (be sure):',
					prefill: false,
					returnvalue: 'noFileCheck',
					type: 'checkbox'
				}, {
					message: 'Ignore User namespace:',
					prefill: !window.aqdUserNsOptOut,
					returnvalue: 'notUserNS',
					type: 'checkbox'
				}, {
					message: 'Ignore Project namespace:',
					prefill: !window.aqdProjectNsOptOut,
					returnvalue: 'notProjectNS',
					type: 'checkbox'
				}, {
					message: 'Ignore Talk namespace:',
					prefill: !window.aqdTalkNsOptOut,
					returnvalue: 'notTalkNS',
					type: 'checkbox'
				}, {
					message: this.i18n.useCORSForReplace,
					prefill: !window.aqdCORSOptOut,
					returnvalue: 'replaceUsingCORS',
					type: 'checkbox'
				}
			], 'JustReplace');
		setTimeout(function () {
			mw.hook('script-loaded.justreplace-prompt').fire();
		}, 200);
	};

	AQD.getFileExtension = function (fn) {
		// Hack: Overwrite mimetype from MoveToken
		// Needed for .doesFileExist (.cleanFileName) check
		if (this.noFileCheck) {
			this.tasks.shift(); // remove nextTask
			return this.fileExists();
		}
		var ext = (fn || this.destination || '').replace(/.*?\.(\w{2,5})$/, '$1').toLowerCase();
		if (ext) {
			if (fn)
				return ext;
			if (this.destination)
				this.mimeFileExtension = ext;
			else
				this.fail("No destination found!");
		} else
			this.fail("Can’t get file extension!");
		this.nextTask();
	};
	
	AQD.fileExists = function () {
		/* Only works if destination file has lowercase filename extension,
		but there is already a fileExists check in JustReplace */
		this.tasks.shift(); // remove declineReplace
		// if (!file) this.addTask('reloadPage');
		this.replaceUsage();
	};

	AQD.declineReplace = function () {
		this.possibleDestination = this.destination; // save for retry
		// i18ns
		this.i18n.moveDestination = mw.msg("Wikilove-err-gallery-again"); // Please again
		this.showProgress(mw.msg("Translate-import-err-invalid-title", this.destination)); // "Checking file: name is not valid!"
		mw.libs.JustReplace(file); // try again
	};

	mw.libs.JustReplace(file);
}

$(function () {
	if ($.inArray(mw.config.get('wgNamespaceNumber'), [6, 14]) !== -1)
		mw.loader.using(['mediawiki.util' , 'ext.gadget.AjaxQuickDelete'], function () {
			AQD = window.AjaxQuickDelete;
			pageName = AQD.pageName || window.pn || mw.config.get("wgPageName");
			var userGroups = [ 'sysop', 'filemover' ];
			if (!AQD.userRights) { // Check user group
				$.each(userGroups, function (i, v) {
					if ($.inArray(v, mw.config.get('wgUserGroups')) !== -1) {
						AQD.userRights = v;
						return false;
					}
				});
			}
			if ($.inArray(AQD.userRights, userGroups) !== -1) {
				if(!$('#ca-justreplace').length)
					$(mw.util.addPortletLink('p-cactions', '#', 'Global replace', 'ca-justreplace', 'Replace stuff'))
						.on('click', init);
					mw.hook('script-loaded.justreplace').fire();
			}
		});
});
}
(mediaWiki, jQuery));
// </nowiki> EOF