User:Ahecht/Scripts/pageswap 1.5.2.js
Appearance
< User:Ahecht | Scripts
Code that you insert on this page could contain malicious content capable of compromising your account. If you import a script from another page with "importScript", "mw.loader.load", "iusc", or "lusc", take note that this causes you to dynamically load a remote script, which could be changed by others. Editors are responsible for all edits and actions they perform, including by scripts. User scripts are not centrally supported and may malfunction or become inoperable due to software changes. A guide to help you find broken scripts is available. If you are unsure whether code you are adding to this page is safe, you can ask at the appropriate village pump. This code will be executed when previewing this page. |
![]() | Documentation for this user script can be added at User:Ahecht/Scripts/pageswap 1.5.2. |
// <syntaxhighlight lang="javascript">
// [[WP:PMRC#4]] round-robin history swap
// Based on [[:en:User:Andy M. Wang/pageswap.js]] by [[User:Andy M. Wang]] 1.6.1.2018.0920
// Modified by [[User:Ahecht]] -- v1.5.2
/*jshint esversion: 6 */
/**
* Initialize variables
*/
if (typeof pagemoveDoPostMoveCleanup === 'undefined') { pagemoveDoPostMoveCleanup = true; }
var pagemoveLink = "[[:en:User:Ahecht/Scripts/pageswap|pageswap]]";
$(document).ready(function() {
mw.loader.using( [
'mediawiki.api',
'mediawiki.util',
] ).then( function() {
"use strict";
/**
* If user is able to perform swaps
*/
function checkUserPermissions() {
var ret = {};
ret.canSwap = true;
var reslt = JSON.parse($.ajax({
url: mw.util.wikiScript('api'), async:false,
error: function (jsondata) { mw.notify("Swapping pages unavailable.", { title: 'Page Swap Error', type: 'error' }); return ret; },
data: { action:'query', format:'json', meta:'userinfo', uiprop:'rights' }
}).responseText).query.userinfo;
// check userrights for suppressredirect and move-subpages
var rightslist = reslt.rights;
ret.canSwap =
$.inArray('suppressredirect', rightslist) > -1 &&
$.inArray('move-subpages', rightslist) > -1;
ret.allowSwapTemplates =
$.inArray('templateeditor', rightslist) > -1;
return ret;
}
/**
* Given namespace data, title, title namespace, returns expected title of page
* Along with title without prefix
* Precondition, title, titleNs is a subject page!
*/
function getTalkPageName(nsData, title, titleNs) {
var ret = {};
var prefixLength = nsData['' + titleNs]['*'].length === 0 ?
0 : nsData['' + titleNs]['*'].length + 1;
ret.titleWithoutPrefix = title.substring(prefixLength, title.length);
ret.talkTitle = nsData['' + (titleNs + 1)]['*'] + ':' +
ret.titleWithoutPrefix;
return ret;
}
/**
* Given two (normalized) titles, find their namespaces, if they are redirects,
* if have a talk page, whether the current user can move the pages, suggests
* whether movesubpages should be allowed, whether talk pages need to be checked
*/
function swapValidate(titleOne, titleTwo, pagesData, nsData, uPerms) {
var ret = {};
ret.valid = true;
if (titleOne === null || titleTwo === null || pagesData === null) {
ret.valid = false;
ret.invalidReason = "Unable to validate swap.";
return ret;
}
ret.allowMoveSubpages = true;
ret.checkTalk = true;
var count = 0;
for (var k in pagesData) {
++count;
if (k == "-1" || pagesData[k].ns < 0) {
ret.valid = false;
ret.invalidReason = ("Page " + pagesData[k].title + " does not exist.");
return ret;
}
// enable only in ns 0..5,12,13,118,119 (Main,Talk,U,UT,WP,WT,H,HT,D,DT)
if ((pagesData[k].ns >= 6 && pagesData[k].ns <= 9) ||
(pagesData[k].ns >= 10 && pagesData[k].ns <= 11 && !uPerms.allowSwapTemplates) ||
(pagesData[k].ns >= 14 && pagesData[k].ns <= 117) ||
(pagesData[k].ns >= 120)) {
ret.valid = false;
ret.invalidReason = ("Namespace of " + pagesData[k].title + " (" +
pagesData[k].ns + ") not supported.\n\nLikely reasons:\n" +
"- Names of pages in this namespace relies on other pages\n" +
"- Namespace features heavily-transcluded pages\n" +
"- Namespace involves subpages: swaps produce many redlinks\n" +
"\n\nIf the move is legitimate, consider a careful manual swap.");
return ret;
}
if (titleOne == pagesData[k].title) {
ret.currTitle = pagesData[k].title;
ret.currNs = pagesData[k].ns;
ret.currTalkId = pagesData[k].talkid; // could be undefined
ret.currCanMove = pagesData[k].actions.move === '';
ret.currIsRedir = pagesData[k].redirect === '';
}
if (titleTwo == pagesData[k].title) {
ret.destTitle = pagesData[k].title;
ret.destNs = pagesData[k].ns;
ret.destTalkId = pagesData[k].talkid; // could be undefined
ret.destCanMove = pagesData[k].actions.move === '';
ret.destIsRedir = pagesData[k].redirect === '';
}
}
if (!ret.valid) return ret;
if (!ret.currCanMove) {
ret.valid = false;
ret.invalidReason = ('' + ret.currTitle + " is immovable. Aborting");
return ret;
}
if (!ret.destCanMove) {
ret.valid = false;
ret.invalidReason = ('' + ret.destTitle + " is immovable. Aborting");
return ret;
}
if (ret.currNs % 2 !== ret.destNs % 2) {
ret.valid = false;
ret.invalidReason = "Namespaces don't match: one is a talk page.";
return ret;
}
if (count !== 2) {
ret.valid = false;
ret.invalidReason = "Pages have the same title. Aborting.";
return ret;
}
ret.currNsAllowSubpages = nsData['' + ret.currNs].subpages !== '';
ret.destNsAllowSubpages = nsData['' + ret.destNs].subpages !== '';
// if same namespace (subpages allowed), if one is subpage of another,
// disallow movesubpages
if (ret.currTitle.startsWith(ret.destTitle + '/') ||
ret.destTitle.startsWith(ret.currTitle + '/')) {
if (ret.currNs !== ret.destNs) {
ret.valid = false;
ret.invalidReason = "Strange.\n" + ret.currTitle + " in ns " +
ret.currNs + "\n" + ret.destTitle + " in ns " + ret.destNs +
". Disallowing.";
return ret;
}
ret.allowMoveSubpages = ret.currNsAllowSubpages;
if (!ret.allowMoveSubpages)
ret.addlInfo = "One page is a subpage. Disallowing move-subpages";
}
if (ret.currNs % 2 === 1) {
ret.checkTalk = false; // no need to check talks, already talk pages
} else { // ret.checkTalk = true;
var currTPData = getTalkPageName(nsData, ret.currTitle, ret.currNs);
ret.currTitleWithoutPrefix = currTPData.titleWithoutPrefix;
ret.currTalkName = currTPData.talkTitle;
var destTPData = getTalkPageName(nsData, ret.destTitle, ret.destNs);
ret.destTitleWithoutPrefix = destTPData.titleWithoutPrefix;
ret.destTalkName = destTPData.talkTitle;
// possible: ret.currTalkId undefined, but subject page has talk subpages
}
return ret;
}
/**
* Given two talk page titles (may be undefined), retrieves their pages for comparison
* Assumes that talk pages always have subpages enabled.
* Assumes that pages are not identical (subject pages were already verified)
* Assumes namespaces are okay (subject pages already checked)
* (Currently) assumes that the malicious case of subject pages
* not detected as subpages and the talk pages ARE subpages
* (i.e. A and A/B vs. Talk:A and Talk:A/B) does not happen / does not handle
* Returns structure indicating whether move talk should be allowed
*/
function talkValidate(checkTalk, talk1, talk2) {
var ret = {};
ret.allowMoveTalk = true;
if (!checkTalk) { return ret; } // currTitle destTitle already talk pages
if (talk1 === undefined || talk2 === undefined) {
mw.notify("Unable to validate talk. Disallowing movetalk to be safe", { title: 'Page Swap Error', type: 'warn' });
ret.allowMoveTalk = false;
return ret;
}
ret.currTDNE = true;
ret.destTDNE = true;
ret.currTCanCreate = true;
ret.destTCanCreate = true;
var talkTitleArr = [talk1, talk2];
if (talkTitleArr.length !== 0) {
var talkData = JSON.parse($.ajax({
url: mw.util.wikiScript('api'), async:false,
error: function (jsondata) { mw.notify("Unable to get info on talk pages.", { title: 'Page Swap Error', type: 'error' }); return ret; },
data: { action:'query', format:'json', prop:'info',
intestactions:'move|create', titles:talkTitleArr.join('|') }
}).responseText).query.pages;
for (var id in talkData) {
if (talkData[id].title === talk1) {
ret.currTDNE = talkData[id].invalid === '' || talkData[id].missing === '';
ret.currTTitle = talkData[id].title;
ret.currTCanMove = talkData[id].actions.move === '';
ret.currTCanCreate = talkData[id].actions.create === '';
ret.currTalkIsRedir = talkData[id].redirect === '';
} else if (talkData[id].title === talk2) {
ret.destTDNE = talkData[id].invalid === '' || talkData[id].missing === '';
ret.destTTitle = talkData[id].title;
ret.destTCanMove = talkData[id].actions.move === '';
ret.destTCanCreate = talkData[id].actions.create === '';
ret.destTalkIsRedir = talkData[id].redirect === '';
} else {
mw.notify("Found pageid ("+talkData[id].title+") not matching given ids ("+talk1+" and "+talk2+").", { title: 'Page Swap Error', type: 'error' }); return {};
}
}
}
ret.allowMoveTalk = (ret.currTCanCreate && ret.currTCanMove) &&
(ret.destTCanCreate && ret.destTCanMove);
return ret;
}
/**
* Given existing title (not prefixed with "/"), optionally searching for talk,
* finds subpages (incl. those that are redirs) and whether limits are exceeded
* As of 2016-08, uses 2 api get calls to get needed details:
* whether the page can be moved, whether the page is a redirect
*/
function getSubpages(nsData, title, titleNs, isTalk) {
if ((!isTalk) && nsData['' + titleNs].subpages !== '') { return { data:[] }; }
var titlePageData = getTalkPageName(nsData, title, titleNs);
var subpages = JSON.parse($.ajax({
url: mw.util.wikiScript('api'), async:false,
error: function (jsondata) { return { error:"Unable to search for subpages. They may exist" }; },
data: { action:'query', format:'json', list:'allpages',
apnamespace:(isTalk ? (titleNs + 1) : titleNs),
apfrom:(titlePageData.titleWithoutPrefix + '/'),
apto:(titlePageData.titleWithoutPrefix + '0'),
aplimit:101 }
}).responseText).query.allpages;
// put first 50 in first arr (need 2 queries due to api limits)
var subpageids = [[],[]];
for (var idx in subpages) {
subpageids[idx < 50 ? 0 : 1].push( subpages[idx].pageid );
}
if (subpageids[0].length === 0) { return { data:[] }; }
if (subpageids[1].length === 51) { return { error:"100+ subpages. Aborting" }; }
var dataret = [];
var subpageData0 = JSON.parse($.ajax({
url: mw.util.wikiScript('api'), async:false,
error: function (jsondata) {
return { error:"Unable to fetch subpage data." }; },
data: { action:'query', format:'json', prop:'info', intestactions:'move|create',
pageids:subpageids[0].join('|') }
}).responseText).query.pages;
for (var k0 in subpageData0) {
dataret.push({
title:subpageData0[k0].title,
isRedir:subpageData0[k0].redirect === '',
canMove:subpageData0[k0].actions.move === ''
});
}
if (subpageids[1].length === 0) { return { data:dataret }; }
var subpageData1 = JSON.parse($.ajax({
url: mw.util.wikiScript('api'), async: false,
error: function (jsondata) {
return { error:"Unable to fetch subpage data." }; },
data: { action:'query', format:'json', prop:'info', intestactions:'move|create',
pageids:subpageids[1].join('|') }
}).responseText).query.pages;
for (var k1 in subpageData1) {
dataret.push({
title:subpageData1[k1].title,
isRedir:subpageData1[k1].redirect === '',
canMove:subpageData1[k1].actions.move === ''
});
}
return { data:dataret };
}
/**
* Prints subpage data given retrieved subpage information returned by getSubpages
* Returns a suggestion whether movesubpages should be allowed
*/
function printSubpageInfo(basepage, currSp) {
var ret = {};
var currSpArr = [];
var currSpCannotMove = [];
var redirCount = 0;
for (var kcs in currSp.data) {
if (!currSp.data[kcs].canMove) {
currSpCannotMove.push(currSp.data[kcs].title);
}
currSpArr.push((currSp.data[kcs].isRedir ? "(R) " : " ") +
currSp.data[kcs].title);
if (currSp.data[kcs].isRedir)
redirCount++;
}
if (currSpArr.length > 0) {
alert((currSpCannotMove.length > 0 ?
"Disabling move-subpages.\n" +
"The following " + currSpCannotMove.length + " (of " +
currSpArr.length + ") total subpages of " +
basepage + " CANNOT be moved:\n\n " +
currSpCannotMove.join("\n ") + '\n\n'
: (currSpArr.length + " total subpages of " + basepage + ".\n" +
(redirCount !== 0 ? ('' + redirCount + " redirects, labeled (R)\n") : '') +
'\n' + currSpArr.join('\n'))));
}
ret.allowMoveSubpages = currSpCannotMove.length === 0;
ret.noNeed = currSpArr.length === 0;
ret.spArr = currSpArr;
return ret;
}
function doDoneMsg(doneMsg, vData) {
if (/failed/ig.test(doneMsg)) {
mw.notify(doneMsg, { tag: 'status', title: 'Page Swap Status', type: 'warn' });
} else {
mw.notify(doneMsg, { tag: 'status', title: 'Page Swap Status', type: 'success' });
}
var spString = "";
if (vData.allSpArr.length) {
spString = "\nThe following subpages were moved, and may need new or updated redirects:\n " +
vData.allSpArr.join("\n ") + "\n";
}
setTimeout(() => {
if(confirm(doneMsg +
"\nPlease create new red-linked talk pages/subpages if there are " +
"incoming links (check your contribs for \"Talk:\" redlinks), " +
"correct any moved redirects, and do post-move cleanup if necessary.\n" +
spString + "\nOpen contribs page?")) {
window.open(mw.util.getUrl("Special:Contributions")+'/'+mw.util.wikiUrlencode(mw.user.getName()));
location.reload();
} else {
location.reload();
}
}, 250);
}
function createMissingTalk(movedTalk, movedSubpages, vData, vTData, doneMsg) {
if (movedTalk) {
var fromTalk, toTalk;
if (vTData.currTDNE && !vTData.destTDNE) {
fromTalk = vData.destTalkName;
toTalk = vData.currTalkName;
} else if (vTData.destTDNE && !vTData.currTDNE) {
fromTalk = vData.currTalkName;
toTalk = vData.destTalkName;
}
if (fromTalk && toTalk) {
mw.notify("Talk page moved...", { tag: 'status', title: 'Page Swap Status' });
setTimeout(() => {
if (confirm(doneMsg + "\nCreate redirect " + fromTalk +
"\n→ " + toTalk + " if possible?")) {
var talkRedirect = {
action:'edit',
title:fromTalk,
createonly: true,
text: "#REDIRECT [[" + toTalk + "]]\n{{R from move}}",
summary: "Create redirect to [[" + toTalk + "]] using " + pagemoveLink,
watchlist:"unwatch"
};
mw.notify("Creating talk page redirect...", { tag: 'status', title: 'Page Swap Status' });
new mw.Api().postWithToken("csrf", talkRedirect).done(function (resltc) {
doDoneMsg(doneMsg + "Redirect " + fromTalk +
"\n→ " +toTalk + " created.\n", vData);
}).fail(function (resltc) {
doDoneMsg(doneMsg + "Failed to create redirect: " + resltc + ".\n", vData);
});
} else { doDoneMsg("", vData); }
}, 250);
} else { doDoneMsg(doneMsg, vData); }
} else { doDoneMsg(doneMsg, vData); }
}
/**
* After successful page swap, post-move cleanup:
* Make talk page redirect
* TODO more reasonable cleanup/reporting as necessary
* vData.(curr|dest)IsRedir
*/
/** TO DO:
*Check if talk is self redirect
*/
function doPostMoveCleanup(movedTalk, movedSubpages, vData, vTData, doneMsg, current = "currTitle", destination = "destTitle") {
if (typeof doneMsg === 'undefined') {
doneMsg = "Moves completed successfully.\n";
mw.notify(doneMsg, { tag: 'status', title: 'Page Swap Status', type: 'success' });
}
// Check for self redirect
var rData = JSON.parse($.ajax({
url: mw.util.wikiScript('api'), async:false,
error: function (jsondata) { mw.notify("Unable to get info about " + vData[current] + ".\n", { title: 'Page Swap Error', type: 'error' }); },
data: { action:'query', format:'json', redirects:'true', titles: vData[current] }
}).responseText).query;
if (rData && rData.redirects && rData.redirects[0].from == rData.redirects[0].to){
var parseData = JSON.parse($.ajax({
url: mw.util.wikiScript('api'), async:false,
error: function (jsondata) { mw.notify("Unable to fetch contents of " + vData[current] + ".\n", { title: 'Page Swap Error', type: 'error' }); },
data: {action:'parse', format:'json', prop:'wikitext', page: vData[current] }
}).responseText).parse;
if (parseData) {
var newWikitext = parseData.wikitext['*'].replace(/^\s*#REDIRECT +\[\[ *.* *\]\]/i, ('#REDIRECT [[' + vData[destination] + ']]'));
if (newWikitext != parseData.wikitext['*']) {
mw.notify("Retargeting redirect at " + vData[current] + " to " + vData[destination] + "...", { tag: 'status', title: 'Page Swap Status' });
new mw.Api().postWithToken("csrf", {
action:'edit',
title: vData[current],
text: newWikitext,
summary : "Retarget redirect to [[" +
vData[destination] + "]] using " +
pagemoveLink,
watchlist: "unwatch"
} ).done(function (resltc) {
doneMsg = doneMsg + "Redirect at " +
vData[current] + " retargeted to " +
vData[destination] + ".\n";
if (current == "currTitle") {
doPostMoveCleanup(movedTalk, movedSubpages, vData, vTData, doneMsg, "currTalkName", "destTalkName");
} else {
createMissingTalk(movedTalk, movedSubpages, vData, vTData, doneMsg);
}
} ).fail(function (resltc) {
doneMsg = doneMsg + "Failed to retarget redirect at " +
vData[current] + " to " +
vData[destination] + ": " + resltc + ".\n";
if (current == "currTitle") {
doPostMoveCleanup(movedTalk, movedSubpages, vData, vTData, doneMsg, "currTalkName", "destTalkName");
} else {
createMissingTalk(movedTalk, movedSubpages, vData, vTData, doneMsg);
}
} );
return;
} else {
doneMsg = doneMsg + "Failed to retarget redirect at " +
vData[current] + " to " + vData[destination] +
": String not found.\n";
}
} else {
doneMsg = doneMsg + "Failed to check contents of" +
vData[current] + ": " + err + ".\n";
}
}
if (current == "currTitle") {
doPostMoveCleanup(movedTalk, movedSubpages, vData, vTData, doneMsg, "currTalkName", "destTalkName");
} else {
createMissingTalk(movedTalk, movedSubpages, vData, vTData, doneMsg);
}
}
/**
* Swaps the two pages (given all prerequisite checks)
* Optionally moves talk pages and subpages
*/
function swapPages(titleOne, titleTwo, moveReason, intermediateTitlePrefix,
moveTalk, moveSubpages, vData, vTData) {
if (titleOne === null || titleTwo === null ||
moveReason === null || moveReason === '') {
mw.notify("Titles are null, or move reason given was empty. Swap not done", { title: 'Page Swap Error', type: 'error' });
return false;
}
var intermediateTitle = intermediateTitlePrefix + titleOne;
var pOne = { action:'move', from:titleTwo, to:intermediateTitle,
reason:"[[WP:PMRC#4|Round-robin history swap]] step 1 using " + pagemoveLink,
watchlist:"unwatch", noredirect:1 };
var pTwo = { action:'move', from:titleOne, to:titleTwo,
reason:moveReason,
watchlist:"unwatch", noredirect:1 };
var pTre = { action:'move', from:intermediateTitle, to:titleOne,
reason:"[[WP:PMRC#4|Round-robin history swap]] step 3 using " + pagemoveLink,
watchlist:"unwatch", noredirect:1 };
if (moveTalk) {
pOne.movetalk = 1; pTwo.movetalk = 1; pTre.movetalk = 1;
}
if (moveSubpages) {
pOne.movesubpages = 1; pTwo.movesubpages = 1; pTre.movesubpages = 1;
}
mw.notify("Doing round-robin history swap step 1...", { tag: 'status', title: 'Page Swap Status' });
new mw.Api().postWithToken("csrf", pOne).done(function (reslt1) {
mw.notify("Doing round-robin history swap step 2...", { tag: 'status', title: 'Page Swap Status' });
new mw.Api().postWithToken("csrf", pTwo).done(function (reslt2) {
mw.notify("Doing round-robin history swap step 3...", { tag: 'status', title: 'Page Swap Status' });
new mw.Api().postWithToken("csrf", pTre).done(function (reslt3) {
if (pagemoveDoPostMoveCleanup) {
doPostMoveCleanup(moveTalk, moveSubpages, vData, vTData);
} else {
doDoneMsg("Moves completed successfully.\n", vData);
}
}).fail(function (reslt3) {
doDoneMsg("Fail on third move " + intermediateTitle + " → " + titleOne + "\n", vData);
});
}).fail(function (reslt2) {
doDoneMsg("Fail on second move " + titleOne + " → " + titleTwo + "\n", vData);
});
}).fail(function (reslt1) {
doDoneMsg("Fail on first move " + titleTwo + " → " + intermediateTitle + "\n", vData);
});
}
/**
* Given two titles, normalizes, does prerequisite checks for talk/subpages,
* prompts user for config before swapping the titles
*/
function roundrobin(uPerms, currNs, currTitle, destTitle, intermediateTitlePrefix) {
// get ns info (nsData.query.namespaces)
var nsData = JSON.parse($.ajax({
url: mw.util.wikiScript('api'), async:false,
error: function (jsondata) { mw.notify("Unable to get info about namespaces", { title: 'Page Swap Error', type: 'error' }); },
data: { action:'query', format:'json', meta:'siteinfo', siprop:'namespaces' }
}).responseText).query.namespaces;
// get page data, normalize titles
var relevantTitles = currTitle + "|" + destTitle;
var pagesData = JSON.parse($.ajax({
url: mw.util.wikiScript('api'), async:false,
error: function (jsondata) {
mw.notify("Unable to get info about " + currTitle + " or " + destTitle, { title: 'Page Swap Error', type: 'error' });
},
data: { action:'query', format:'json', prop:'info', inprop:'talkid',
intestactions:'move|create', titles:relevantTitles }
}).responseText).query;
for (var kp in pagesData.normalized) {
if (currTitle == pagesData.normalized[kp].from) { currTitle = pagesData.normalized[kp].to; }
if (destTitle == pagesData.normalized[kp].from) { destTitle = pagesData.normalized[kp].to; }
}
// validate namespaces, not identical, can move
var vData = swapValidate(currTitle, destTitle, pagesData.pages, nsData, uPerms);
if (!vData.valid) { mw.notify(vData.invalidReason, { title: 'Page Swap Error', type: 'error' }); return; }
if (vData.addlInfo !== undefined) { mw.notify(vData.addlInfo, { title: 'Page Swap Error', type: 'error' }); }
// subj subpages
var currSp = getSubpages(nsData, vData.currTitle, vData.currNs, false);
if (currSp.error !== undefined) { mw.notify(currSp.error, { title: 'Page Swap Error', type: 'error' }); return; }
var currSpFlags = printSubpageInfo(vData.currTitle, currSp);
var destSp = getSubpages(nsData, vData.destTitle, vData.destNs, false);
if (destSp.error !== undefined) { mw.notify(destSp.error, { title: 'Page Swap Error', type: 'error' }); return; }
var destSpFlags = printSubpageInfo(vData.destTitle, destSp);
var vTData = talkValidate(vData.checkTalk, vData.currTalkName, vData.destTalkName);
// future goal: check empty subpage DESTINATIONS on both sides (subj, talk)
// for create protection. disallow move-subpages if any destination is salted
var currTSp = getSubpages(nsData, vData.currTitle, vData.currNs, true);
if (currTSp.error !== undefined) { mw.notify(currTSp.error, { title: 'Page Swap Error', type: 'error' }); return; }
var currTSpFlags = printSubpageInfo(vData.currTalkName, currTSp);
var destTSp = getSubpages(nsData, vData.destTitle, vData.destNs, true);
if (destTSp.error !== undefined) { mw.notify(destTSp.error, { title: 'Page Swap Error', type: 'error' }); return; }
var destTSpFlags = printSubpageInfo(vData.destTalkName, destTSp);
var noSubpages = currSpFlags.noNeed && destSpFlags.noNeed &&
currTSpFlags.noNeed && destTSpFlags.noNeed;
// If one ns disables subpages, other enables subpages, AND HAS subpages,
// consider abort. Assume talk pages always safe (TODO fix)
var subpageCollision = (vData.currNsAllowSubpages && !destSpFlags.noNeed) ||
(vData.destNsAllowSubpages && !currSpFlags.noNeed);
var moveSubpages = false;
// TODO future: currTSpFlags.allowMoveSubpages && destTSpFlags.allowMoveSubpages
// needs to be separate check. If talk subpages immovable, should not affect subjspace
if (!subpageCollision && !noSubpages && vData.allowMoveSubpages &&
(currSpFlags.allowMoveSubpages && destSpFlags.allowMoveSubpages) &&
(currTSpFlags.allowMoveSubpages && destTSpFlags.allowMoveSubpages)) {
moveSubpages = confirm("Move subpages? (OK for yes, Cancel for no)");
} else if (subpageCollision) {
mw.notify("One namespace does not have subpages enabled. Disallowing move subpages", { title: 'Page Swap Error', type: 'error' });
}
if (moveSubpages) {
vData.allSpArr = currSpFlags.spArr.concat(destSpFlags.spArr, currTSpFlags.spArr, destTSpFlags.spArr);
} else {
vData.allSpArr = [];
}
var moveTalk = false;
// TODO: count subpages and make restrictions?
if (vData.checkTalk && (!vTData.currTDNE || !vTData.destTDNE || moveSubpages)) {
if (vTData.allowMoveTalk) {
moveTalk = confirm("Move talk page(s)? (OK for yes, Cancel for no)");
} else {
alert("Disallowing moving talk. " +
(!vTData.currTCanCreate ? (vData.currTalkName + " is create-protected")
: (!vTData.destTCanCreate ? (vData.destTalkName + " is create-protected")
: "Talk page is immovable")));
}
}
var moveReason = '';
var moveReasonPrompt = '';
if (typeof mw.util.getParamValue("wpReason") === 'string') {
moveReasonPrompt = mw.util.getParamValue("wpReason");
} else if (document.getElementsByName("wpReason")[0] && document.getElementsByName("wpReason")[0].value != '') {
moveReasonPrompt = document.getElementsByName("wpReason")[0].value;
} else if (typeof moveReasonDefault === 'string') {
moveReasonPrompt = moveReasonDefault;
}
moveReason = prompt("Move reason:", moveReasonPrompt);
var spToMoveString = "";
if (moveSubpages) {
spToMoveString = "Subpages to move:\n "+ vData.allSpArr.join("\n ") + "\n\n";
}
var confirmString = "Round-robin configuration:\n " +
currTitle + " → " + destTitle + "\n : " + moveReason +
"\n with movetalk:" + moveTalk + ", movesubpages:" + moveSubpages +
"\n\n" + spToMoveString +
"Proceed? (Cancel to abort)";
if (confirm(confirmString)) {
swapPages(currTitle, destTitle, moveReason, intermediateTitlePrefix,
moveTalk, moveSubpages, vData, vTData);
}
}
var currNs = mw.config.get("wgNamespaceNumber");
var wpOldTitle = mw.util.getParamValue("wpOldTitle");
if (!wpOldTitle && document.getElementsByName("wpOldTitle")[0] && document.getElementsByName("wpOldTitle")[0].value != ''){
wpOldTitle = document.getElementsByName("wpOldTitle")[0].value;
}
var wpNewTitle = mw.util.getParamValue("wpNewTitle");
if (!wpNewTitle && document.getElementsByName("wpNewTitleMain")[0] && document.getElementsByName("wpNewTitleMain")[0].value != '' && document.getElementsByName("wpNewTitleNs")[0]){
wpNewTitle = document.getElementsByName("wpNewTitleMain")[0].value;
var nsid = document.getElementsByName("wpNewTitleNs")[0].value;
if (nsid != 0) {
wpNewTitle = mw.config.get("wgFormattedNamespaces")[nsid] + ":" + wpNewTitle;
}
}
if (currNs < -1 || currNs >= 120 ||
(currNs >= 6 && currNs <= 9) ||
(currNs >= 14 && currNs <= 99) ||
(currNs == -1 && mw.config.get("wgCanonicalSpecialPageName") != "Movepage") ||
(mw.config.get("wgCanonicalSpecialPageName") == "Movepage" && !wpOldTitle)
)
return; // special/other page
var portletLink = mw.util.addPortletLink("p-cactions", "#", "Swap",
"ca-swappages", "Perform a revision history swap / round-robin move");
$( portletLink ).click(function(e) {
e.preventDefault();
var userPermissions = checkUserPermissions();
if (!userPermissions.canSwap) {
mw.notify("User rights insufficient for action.", { title: 'Page Swap Error', type: 'error' }); return;
}
var currTitle = wpOldTitle || mw.config.get("wgPageName");
var destTitle = wpNewTitle || prompt("Swap \"" + (currTitle.replace(/_/g, ' ')) + "\" with:", (currTitle.replace(/_/g, ' ')));
return roundrobin(userPermissions, currNs, currTitle, destTitle, "Draft:Move/");
});
if (mw.config.get("wgCanonicalSpecialPageName") == "Movepage" &&
$( "div.cdx-message--error" ).find( "p" ).eq(1).is( ":contains('name already exists')" ) &&
wpOldTitle)
{
$( "div.cdx-message--error" ).find( "p" ).eq(2).html( 'Please choose another name, or perform a <a title="Perform a revision history swap / round-robin move" href="#" id="pageswapLink">swap</a>.' );
$( "#pageswapLink" ).click(function(e) {
e.preventDefault();
$( portletLink ).click();
});
}
});
});
// </syntaxhighlight>