User:PhantomTech/scripts/MoveToDraft.js

/*************************************************************************************************** MoveToDraft fork by PhantomTech Forked from en:User:Evad37/MoveToDraft.js - Version 3.0.0 - A script to move pages to draft space, including cleanup and author notification. - Moves page to draftspace - Checks if any files used are non-free - Checks if any redirects pointed to the page - Comments out non-free files, turn categories into links, add afc draft template, add redirects - Adds notification message on author talk page - Updates talk page banners - Logs draftification in user subpage - Allows viewing of pages a user moved to the draft namespace at Special:BlankPage/Draftify_log

***************************************************************************************************/ /* jshint laxbreak: true, undef: true, maxerr:999 */ /* globals console, window, document, $, mw, OO, extraJs */ // $.when(   // Resource loader modules    mw.loader.using(['mediawiki.util', 'mediawiki.api', 'mediawiki.Title', 'ext.gadget.libExtraUtil']),    // Page ready    $.ready ).then(function  {    /* ========== Config ============================================================================ */    var config = {        // Script info        script: {            advert: ' (MoveToDraft)', // For edit summaries            version: '2.6.0'        },        // MediaWiki configuration values        mw: mw.config.get([ 'wgArticleId', 'wgCurRevisionId', 'wgPageName', 'wgUserGroups', 'wgUserName', 'wgMonthNames', 'wgNamespaceNumber', 'wgTitle' ])   };

/* ========== API =============================================================================== */ var API = new mw.Api({       ajax: {            headers: {                'Api-User-Agent': 'MoveToDraft/' + config.script.version +                    ' ( https://en.wikipedia.org/wiki/User:PhantomTech/scripts/MoveToDraft.js )'            }        }    });

var moveToDraft = function moveToDraft(ns) {

/* ========== Additional config ================================================================= */ // Wikitext strings config.wikitext = { 'rationale': window.m2d_rationale || 'Not ready for mainspace, incubate in draftspace', 'editsummary': window.m2d_editsummary || window.m2d_rationale || 'Add AFC draft template', 'notification_heading': '$1 moved to $2', 'notification': window.m2d_notification || "An article you recently created, $1, is not suitable as written to remain published. It needs more citations from reliable, independent sources. (?) Information that can't be referenced should be removed (verifiability is of central importance on Wikipedia). I've moved your draft to draftspace (with a prefix of \" \" before the article title) where you can incubate the article with minimal disruption. When you feel the article meets Wikipedia's general notability guideline and thus is ready for mainspace, please click on the \"Submit your draft for review!\" button at the top of the page. ~", 'logMsg': '#$1 moved to $2 at ' };       if (ns === 2 || ns === 3) { config.wikitext['rationale'] = 'Stale draft'; config.wikitext['notification'] = '$2 ~'; }       config.doNotLog = window.m2d_doNotLog ? true : false; // Page data -- to be retreived later from api config.pagedata = {};

// Helper functions // - prettify an encoded page title (or at least replace underscores with spaces) var getPageText = function (p) { var t = mw.Title.newFromText(decodeURIComponent(p)); if (t) { return t.getPrefixedText; } else { return p.replace(/_/g, " "); }       };

/* ========== Tasks ============================================================================= */

// Grab page data - initial author, current wikitext, any redirects, if Draft: page already exists var grabPageData = function {

var patt_isRedirect = /^\s*#redirect/i;

var checkedPageTriageStatus = false;

// Function to check if all done var checkPageData = function { if (                   config.pagedata.author != null &&                    config.pagedata.oldwikitext != null &&                    config.pagedata.redirects != null &&                    checkedPageTriageStatus                ) { //all done - go to next screen screen1; }           };

/* -- Initial author */

/* Try making an api call for just the first revision - but if that is a redirect, then get 'max' number of revisions, and look for first non-redirect revision - use this as the initial author, not the creator of the redirect. */           var processMaxRvAuthorQuery = function (result) { var revisions = result.query.pages[config.mw.wgArticleId].revisions; for (var i = 1; i < revisions.length; i++) { if (!patt_isRedirect.test(revisions[i]['*'])) { config.pagedata.author = revisions[i].user; break; }               }                //Check that we actually found an author (i.e. not all revisions were redirects                if (config.pagedata.author == null) {                    API.abort;                    var retry = confirm("Could not retrieve page author:\n" + extraJs.makeErrorMsg(c, r) + "\n\nTry again?");                    if (retry) {                        screen0;                    } else {                        $("#M2D-modal").remove;                    }                }

checkPageData; };

var processAuthorQuery = function (result) { // Check if page is currently a redirect if (result.query.pages[config.mw.wgArticleId].redirect) { API.abort; alert("Error: " + config.mw.wgPageName + " is a redirect"); return; }               // Check if first revision is a redirect rvwikitext = result.query.pages[config.mw.wgArticleId].revisions[0]['*']; if (patt_isRedirect.test(rvwikitext)) { // query to look for first non-redirect revision API.get({                       action: 'query',                        pageids: config.mw.wgArticleId,                        prop: 'revisions',                        rvprop: ['user', 'content'],                        rvlimit: 'max',                        rvdir: 'newer'                    }) .done(processMaxRvAuthorQuery) .fail(function (c, r) {                           if (r.textStatus === 'abort') {                                return;                            }

API.abort; var retry = confirm("Could not retrieve page author:\n" + extraJs.makeErrorMsg(c, r) + "\n\nTry again?"); if (retry) { screen0; } else { $("#M2D-modal").remove; }                       });                    return;                }

config.pagedata.author = result.query.pages[config.mw.wgArticleId].revisions[0].user; checkPageData; };

//Get author API.get({               action: 'query',                pageids: config.mw.wgArticleId,                prop: ['revisions', 'info'],                rvprop: ['user', 'content'],                rvlimit: 1,                rvdir: 'newer'            }) .done(processAuthorQuery) .fail(function (c, r) {                   if (r.textStatus === 'abort') {                        return;                    }

API.abort; var retry = confirm("Could not retrieve page author:\n" + extraJs.makeErrorMsg(c, r) + "\n\nTry again?"); if (retry) { screen0; } else { $("#M2D-modal").remove; }               });

/* -- Current wikitext -- */

API.get({               action: 'query',                pageids: config.mw.wgArticleId,                prop: 'revisions',                rvprop: 'content'            }) .done(function (result) {                   config.pagedata.oldwikitext = result.query.pages[config.mw.wgArticleId].revisions[0]['*'];                    checkPageData;                }) .fail(function (c, r) {                   if (r.textStatus === 'abort') {                        return;                    }

API.abort; var retry = confirm("Could not retrieve page wikitext:\n" + extraJs.makeErrorMsg(c, r) + "\n\nTry again?"); if (retry) { screen0; } else { $("#M2D-modal").remove; }               });

//TODO(?): also get proposed Draft: page (to check if it is empty or not)

/* -- Redirects - */ var redirectTitles = [];

var processRedirectsQuery = function (result) { if (!result.query || !result.query.pages) { // No results config.pagedata.redirects = false; checkPageData; return; }               // Gather redirect titles into array $.each(result.query.pages, function (_id, info) {                   redirectTitles.push(info.title);                }); // Continue query if needed if (result.continue) { doRedirectsQuery($.extend(redirectsQuery, result.continue)); return; }

// Check if redirects were found if (redirectTitles.length === 0) { config.pagedata.redirects = false; checkPageData; return; }

// Set redirects config.pagedata.redirects = (redirectTitles.length === 0) ? false : redirectTitles; checkPageData; };

var redirectsQuery = { action: 'query', pageids: config.mw.wgArticleId, generator: 'redirects', grdlimit: 500 };           var doRedirectsQuery = function (q) { API.get(q) .done(processRedirectsQuery) .fail(function (c, r) {                       if (r.textStatus === 'abort') {                            return;                        }

API.abort; var retry = confirm("Could not retrieve redirects:\n" + extraJs.makeErrorMsg(c, r) +                           "\n\nTry again? (or Cancel to skip)"); if (retry) { screen0; } else { config.pagedata.redirects = false; checkPageData; }                   });            };            doRedirectsQuery(redirectsQuery);

/* -- Review (Page Triage) status - */

API.get({               action: 'pagetriagelist',                page_id: config.mw.wgArticleId            }) .done(function (result) {                   if (!result.pagetriagelist.pages.length) {                        var keepGoing = confirm('WARNING: Page has already been reviewed by a New Page Patroller. Are you sure you want to draftify this page?');                       if (!keepGoing) {                            API.abort;                            $("#M2D-modal").remove;                            return;                        }                    }                    checkedPageTriageStatus = true;                    checkPageData;                }) .fail(function (c, r) {                   if (r.textStatus === 'abort') {                        return;                    }

API.abort; var retry = confirm("Could not retrieve page triage status:\n" + extraJs.makeErrorMsg(c, r) + "\n\nTry again?"); if (retry) { screen0; } else { $("#M2D-modal").remove; }               });

};

//Move page var movePage = function { $("#M2D-task0").css({"color": "#00F", "font-weight": "bold"}); $("#M2D-status0").html("...");

// First check the page hasn't been draftified in the meantime API.get({               action: "query",                pageids: config.mw.wgArticleId,                format: "json",                formatversion: "2"            }).then(function (response) {                var page = response && response.query && response.query.pages && response.query.pages[0];                if (!page) {                    return $.Deferred.reject;                } else if (page.missing) {                    return $.Deferred.reject("moveToDraft-pagemissing");                } else if (page.ns === 118 /* Draft NS */) {                    return $.Deferred.reject("moveToDraft-alreadydraft");                } else if (page.ns !== config.mw.wgNamespaceNumber) {                    return $.Deferred.reject("moveToDraft-movednamespace");                }

let moveRequestParams = { action: 'move', fromid: config.mw.wgArticleId, to: config.inputdata.newTitle, reason: config.inputdata.rationale + config.script.advert }               if (config.mw.wgNamespaceNumber !== 2 && config.mw.wgNamespaceNumber !== 3) { moveRequestParams.movetalk = 1; moveRequestParams.noredirect = 1; }

return API.postWithToken('csrf', moveRequestParams); })               .done(function  { if (                       config.mw.wgNamespaceNumber === 2 ||                        config.mw.wgNamespaceNumber === 3                    ) { // Convert redirects in userspace to soft redirects softRedrect; return; } else if (                       -1 === $.inArray('sysop', config.mw.wgUserGroups) &&                        -1 === $.inArray('extendedmover', config.mw.wgUserGroups)                    ) { // Newly created redirect to be tagged for speedy deletion tagRedrect; return; }                   $("#M2D-task0").css({"color": "#000", "font-weight": ""}); $("#M2D-status0").html("Done!"); getImageInfo; })               .fail(function (c, r) { if (r && r.textStatus === 'abort') { return; } else if (c === "moveToDraft-pagemissing") { alert("The page no longer appears to exists. It may have been deleted."); $("#M2D-modal").remove; window.location.reload; return; } else if (c === "moveToDraft-alreadydraft") { alert("Aborted: The page has already been moved to draftspace."); $("#M2D-modal").remove; window.location.reload; return; } else if (c === "moveToDraft-alreadydraft") { alert("Aborted: The page has already been moved out of mainspace."); $("#M2D-modal").remove; window.location.reload; return; }

var retry = confirm("Could not move page:\n" + extraJs.makeErrorMsg(c, r) + "\n\nTry again?"); if (retry) { movePage; } else { screen1(true); }               });        };

var softRedrect = function { $("#M2D-status0").html("Done, Converting redirect to soft redirect..."); API.postWithToken('csrf', {               action: 'edit',                title: config.mw.wgPageName,                text: '',                summary: 'Soft redirecting to moved draft' + config.script.advert            }) .done(function {                    $("#M2D-task0").css({"color": "#000", "font-weight": ""});                    $("#M2D-status0").append(" Done!");                    getImageInfo;                }) .fail(function (c, r) {                   if (r.textStatus === 'abort') {                        return;                    }

var retry = confirm("Could not convert to soft redirect:\n" +                       extraJs.makeErrorMsg(c, r) + "\n\nTry again?"); if (retry) { softRedrect; } else { $("#M2D-task0").css({"color": "#F00", "font-weight": ""}); $("#M2D-status0").append(" Skipped"); getImageInfo; }               });        };

var tagRedrect = function { $("#M2D-status0").html("Done, Tagging redirect for speedy deletion..."); API.postWithToken('csrf', {               action: 'edit',                title: config.mw.wgPageName,                prependtext: '\n',                summary: 'R2 speedy deletion request (article moved to draftspace)' + config.script.advert            }) .done(function {                    $("#M2D-task0").css({"color": "#000", "font-weight": ""});                    $("#M2D-status0").append(" Done!");                    getImageInfo;                }) .fail(function (c, r) {                   if (r.textStatus === 'abort') {                        return;                    }

var retry = confirm("Could not tag redirect for speedy deletion:\n" +                       extraJs.makeErrorMsg(c, r) + "\n\nTry again?"); if (retry) { tagRedrect; } else { $("#M2D-task0").css({"color": "#F00", "font-weight": ""}); $("#M2D-status0").append(" Skipped"); getImageInfo; }               });        };

//Find which images are non-free var getImageInfo = function { $("#M2D-task1").css({"color": "#00F", "font-weight": "bold"}); $("#M2D-status1").html("...");

processImageInfo = function (result) { var nonfreefiles = []; if (result && result.query) { $.each(result.query.pages, function (id, page) {                       if (id > 0 && page.categories) {                            nonfreefiles.push(page.title);                        }                    }); }               editWikitext(nonfreefiles); };

API.get({               action: 'query',                pageids: config.mw.wgArticleId,                generator: 'images',                gimlimit: 'max',                prop: 'categories',                cllimit: 'max',                clcategories: 'Category:All non-free media',            }) .done(function (result) {                   $("#M2D-task1").css({"color": "#000", "font-weight": ""});                    $("#M2D-status1").html("Done!");                    processImageInfo(result);                }) .fail(function (c, r) {                   if (r.textStatus === 'abort') {                        return;                    }

var retry = confirm("Could not find if there are non-free files:\n" + extraJs.makeErrorMsg(c, r) + "\n\n[Okay] to try again, or [Cancel] to skip"); if (retry) { getImageInfo; } else { $("#M2D-task1").css({"color": "#F00", "font-weight": ""}); $("#M2D-status1").html("Skipped"); editWikitext([]); }               });

};

//Comment out non-free files, turn categories into links, add afc draft template, list any redirects var editWikitext = function (nonfreefiles) { $("#M2D-task2").css({"color": "#00F", "font-weight": "bold"}); $("#M2D-status2").html("...");

var redirectsList = (!config.pagedata.redirects) ? '' : '\n' + '\n';

var wikitext = "" + config.inputdata.authorName + "\n" + redirectsList + config.pagedata.oldwikitext.replace(/\[\[\s*[Cc]ategory\s*:/g, "[[:Category:")           if (config.mw.wgNamespaceNumber === 0) {                wikitext += "\n";            }

// non-free files // (derived from WP:XFDC - https://en.wikipedia.org/wiki/User:Evad37/XFDcloser.js ) if (nonfreefiles.length > 0) { // Start building regex strings normal_regex_str = "(";               gallery_regex_str = "("; free_regex_str = "(";               for (var i = 0; i < nonfreefiles.length; i++) {                    // Take off namespace prefix                    filename = nonfreefiles[i].replace(/^.*?:/, "");                    // For regex matching: first character can be either upper or lower case, special                    // characters need to be escaped, spaces can be either spaces or underscores                    filename_regex_str = "[" + mw.util.escapeRegExp(filename.slice(0, 1).toUpperCase) +                        mw.util.escapeRegExp(filename.slice(0, 1).toLowerCase) + "]" +                        mw.util.escapeRegExp(filename.slice(1)).replace(/ /g, "[ _]");                    // Add to regex strings                    normal_regex_str += "\\[\\[\\s*(?:[Ii]mage|[Ff]ile)\\s*:\\s*" + filename_regex_str +                        "\\s*\\|?.*?(?:(?:\\[\\[.*?\\]\\]).*?)*\\]\\]"; gallery_regex_str += "^\\s*(?:[Ii]mage|[Ff]ile):\\s*" + filename_regex_str + ".*?$"; free_regex_str += "\\|\\s*(?:[\\w\\s]+\\=)?\\s*(?:(?:[Ii]mage|[Ff]ile):\\s*)?" +                       filename_regex_str;

if (i + 1 === nonfreefiles.length) { normal_regex_str += ")(?![^<]*?-->)";                       gallery_regex_str += ")(?![^<]*?-->)"; free_regex_str += ")(?![^<]*?-->)";                   } else {                        normal_regex_str += "|";                        gallery_regex_str += "|";                        free_regex_str += "|";                    }                }

// Check for normal file usage, i.e.                var normal_regex = new RegExp(normal_regex_str, "g"); wikitext = wikitext.replace(normal_regex, "");

// Check for gallery usage, i.e. instances that must start on a new line, eventually // preceded with some space, and must include File: or Image: prefix var gallery_regex = new RegExp(gallery_regex_str, "mg"); wikitext = wikitext.replace(gallery_regex, "");

// Check for free usages, for example as template argument, might have the File: or Image: // prefix excluded, but must be preceeded by an | var free_regex = new RegExp(free_regex_str, "mg"); wikitext = wikitext.replace(free_regex, ""); }

API.postWithToken('csrf', {               action: 'edit',                pageid: config.mw.wgArticleId,                text: wikitext,                summary: config.wikitext.editsummary + config.script.advert            }) .done(function {                    $("#M2D-task2").css({"color": "#000", "font-weight": ""});                    $("#M2D-status2").html("Done!");                    notifyAuthor;                }) .fail(function (c, r) {                   if (r.textStatus === 'abort') {                        return;                    }

var retry = confirm("Could not edit draft artice:\n" + extraJs.makeErrorMsg(c, r) + "\n\n[Okay] to try again, or [Cancel] to skip"); if (retry) { editWikitext(nonfreefiles); } else { $("#M2D-task2").css({"color": "#F00", "font-weight": ""}); $("#M2D-status2").html("Skipped"); notifyAuthor; }               });

};

var notifyAuthor = function { if (!config.inputdata.notifyEnable) { updateTalk; return; }           $("#M2D-task3").css({"color": "#00F", "font-weight": "bold"}); $("#M2D-status3").html("...");

API.postWithToken('csrf', {               action: 'edit',                title: 'User talk:' + config.inputdata.authorName,                section: 'new',                sectiontitle: config.inputdata.notifyMsgHead,                text: config.inputdata.notifyMsg,            }) .done(function {                    $("#M2D-task3").css({"color": "#000", "font-weight": ""});                    $("#M2D-status3").html("Done!");                    updateTalk;                }) .fail(function (c, r) {                   if (r.textStatus === 'abort') {                        return;                    }

var retry = confirm("Could not edit author talk page:\n" + extraJs.makeErrorMsg(c, r) + "\n\n[Okay] to try again, or [Cancel] to skip"); if (retry) { notifyAuthor; } else { $("#M2D-task3").css({"color": "#F00", "font-weight": ""}); $("#M2D-status3").html("Skipped"); updateTalk; }               });        };

var updateTalk = function { $("#M2D-task4").css({"color": "#00F", "font-weight": "bold"}); $("#M2D-status4").html("...");

//if page exists, do a regex search/repace for class/importances parameters var processTalkWikitext = function (result) { var talk_id = result.query.pageids[0]; if (talk_id < 0) { $("#M2D-task4").css({"color": "#000", "font-weight": ""}); $("#M2D-status4").html("Done (talk page does not exist)"); draftifyLog; return; }               var old_talk_wikitext = result.query.pages[talk_id].revisions[0]['*']; var new_talk_wikitext = old_talk_wikitext.replace(/(\|\s*(?:class|importance)\s*=\s*)[^\|}]*(?=[^}]*}})/g, "$1"); if (new_talk_wikitext === old_talk_wikitext) { $("#M2D-task4").css({"color": "#000", "font-weight": ""}); $("#M2D-status4").html("Done (no changes needed)"); draftifyLog; return; }

API.postWithToken('csrf', {                   action: 'edit',                    pageid: talk_id,                    section: '0',                    text: new_talk_wikitext,                    summary: 'Remove class/importance from project banners' + config.script.advert                }) .done(function {                        $("#M2D-task4").css({"color": "#000", "font-weight": ""});                        $("#M2D-status4").html("Done!");                        draftifyLog;                    }) .fail(function (c, r) {                       if (r.textStatus === 'abort') {                            return;                        }

var retry = confirm("Could not edit draft's talk page:\n" + extraJs.makeErrorMsg(c, r) + "\n\n[Okay] to try again, or [Cancel] to skip"); if (retry) { updateTalk; } else { $("#M2D-task4").css({"color": "#F00", "font-weight": ""}); $("#M2D-status4").html("Skipped"); draftifyLog; }                   });

};

//get talk page wikitext (section 0) API.get({               action: 'query',                titles: config.inputdata.newTitle.replace("Draft:", "Draft talk:"),                prop: 'revisions',                rvprop: 'content',                rvsection: '0',                indexpageids: 1            }) .done(processTalkWikitext) .fail(function (c, r) {                   if (r.textStatus === 'abort') {                        return;                    }

var retry = confirm("Could not find draft's talk page:\n" + extraJs.makeErrorMsg(c, r) + "\n\n[Okay] to try again, or [Cancel] to skip"); if (retry) { updateTalk; } else { $("#M2D-task4").css({"color": "#F00", "font-weight": ""}); $("#M2D-status4").html("Skipped"); draftifyLog; }               });

};

var draftifyLog = function { if (config.doNotLog) { $("#M2D-finished, #M2D-abort").toggle; return; }

$("#M2D-task5").css({"color": "#00F", "font-weight": "bold"}); $("#M2D-status5").html("...");

var logpage = 'User:' + config.mw.wgUserName + '/Draftify_log'; var monthNames = config.mw.wgMonthNames.slice(1); var now = new Date; var heading = '== ' + monthNames[now.getUTCMonth] + ' ' + now.getUTCFullYear + ' =='; var headingPatt = RegExp(heading);

var processLogWikitext = function (result) { var logpage_wikitext = '';

var id = result.query.pageids[0]; if (id < 0) { var createlog = confirm('Log draftification (at ' + logpage + ') ?'); if (!createlog) { $("#M2D-task5").css({"color": "#F00", "font-weight": ""}); $("#M2D-status5").empty.append("Skipped"); $("#M2D-finished, #M2D-abort").toggle; return; }                   logpage_wikitext = 'This is a log of pages moved to draftspace using a MoveToDraft script.'; } else { logpage_wikitext = result.query.pages[id].revisions[0]['*'].trim; }

if (!headingPatt.test(logpage_wikitext)) { logpage_wikitext += '\n\n' + heading; }               logpage_wikitext += '\n' + config.inputdata.logMsg;

API.postWithToken('csrf', {                   action: 'edit',                    title: logpage,                    text: logpage_wikitext,                    summary: 'Logging ' + config.inputdata.newTitle + '' + config.script.advert                }) .done(function {                        $("#M2D-task5").css({"color": "#000", "font-weight": ""});                        $("#M2D-status5").html("Done!");                        $("#M2D-finished, #M2D-abort").toggle;                    }) .fail(function (c, r) {                       if (r.textStatus === 'abort') {                            return;                        }

var retry = confirm("Could not edit log page:\n" + extraJs.makeErrorMsg(c, r) + "\n\n[Okay] to try again, or [Cancel] to skip"); if (retry) { draftifyLog; } else { $("#M2D-task5").css({"color": "#F00", "font-weight": ""}); $("#M2D-status5").html("Skipped"); $("#M2D-finished, #M2D-abort").toggle; }                   });            };

//get log page wikitext API.get({               action: 'query',                titles: logpage,                prop: 'revisions',                rvprop: 'content',                indexpageids: 1            }) .done(processLogWikitext) .fail(function (c, r) {                   if (r.textStatus === 'abort') {                        return;                    }

var retry = confirm("Could not find log page:\n" + extraJs.makeErrorMsg(c, r) + "\n\n[Okay] to try again, or [Cancel] to skip"); if (retry) { draftifyLog; } else { $("#M2D-task5").css({"color": "#F00", "font-weight": ""}); $("#M2D-status5").html("Skipped"); $("#M2D-finished, #M2D-abort").toggle; }               });        };

// --- Interface screens --- //0) Initial screen       var screen0 = function  {            $("#M2D-interface-header, #M2D-interface-content, #M2D-interface-footer").empty;            $("#M2D-interface-header").text("Move To Draft...");            $("#M2D-interface-content").text("Loading...");            grabPageData;        };

//1) User inputs

/**        *         * @param {boolean} restoreValues Restore previously set values */       var screen1 = function (restoreValues) { $("#M2D-interface-header, #M2D-interface-content, #M2D-interface-footer").empty; $("#M2D-interface-header").text("Move To Draft: options");

$("#M2D-interface-content").append(               $(' ').css('margin-bottom', '0.5em').append( $(' ')                       .css({                            display: 'block',                            color: 'darkred'                        }).append(                        'Please ensure draftifying is appropriate per ',                        extraJs.makeLink("WP:DRAFTIFY"), ', ',                        extraJs.makeLink("WP:STALE"), ' or some other criteria'                    ), $(' ').attr('for', 'M2D-option-newtitle').append(                       'Move to ',                        $('').text('Draft:')                    ), $(' ').attr({'type': 'text', 'name': 'M2D-option-newtitle', 'id': 'M2D-option-newtitle'}) ),

$(' ').css('margin-bottom', '0.5em').append(                   $(' ').attr({'for': 'M2D-option-movelog', 'id': 'M2D-option-movelog-label'})                        .css('display', 'block').text('Reason for move (log summary):'),                    $(' ').attr({'rows': '1', 'name': 'M2D-option-movelog', 'id': 'M2D-option-movelog'})                        .css('width', '99%')                ),

$(' ').css('margin-bottom', '0.5em').append(                   $(' ').attr({'for': 'M2D-option-author', 'id': 'M2D-option-author-label'}).text('Author:'),                    $(' ').attr({'type': 'text', 'name': 'M2D-option-author', 'id': 'M2D-option-author'})                ),

$(' ').attr({'for': 'M2D-option-message-enable'}).append(                   $(' ').attr({'type': 'checkbox', 'id': 'M2D-option-message-enable'})                        .prop('checked', true),                    'Notify author'                ),

$(' ').attr({'for': 'M2D-option-message-head', 'id': 'M2D-option-message-head-label'}) .css({'display': 'block', 'margin-top': '0.5em'}).text('Notification heading'), $(' ').attr({'id': 'M2D-option-message-head', 'rows': '1'}) .css({'width': '99%', 'margin-bottom': '0.5em'}),

$(' ').attr({'for': 'M2D-option-message', 'id': 'M2D-option-message-label'}) .css('display', 'block').text('Notification message:'), $(' ').attr({'id': 'M2D-option-message', 'rows': '6'}) .css('width', '99%'), $(' ').attr({'display': 'block'}).css({'font-style': 'italic', 'color': 'darkred'}) .text("Please add to or replace the default notification text so the author understands your specific reasons for draftification!") );

$('#M2D-option-movelog').val(config.wikitext.rationale); $('#M2D-option-newtitle').val(getPageText(config.mw.wgTitle)).change(function {                $('#M2D-option-message-head').val( $('#M2D-option-message-head').val.trim .replace(/\[\[Draft\:.*?\|/, "[[Draft:" + $('#M2D-option-newtitle').val.trim + "|")               );                $('#M2D-option-message').val(                    $('#M2D-option-message').val.trim                        .replace(/\[\[Draft\:.*?\|/, "[[Draft:" + $('#M2D-option-newtitle').val.trim + "|")                );            });            $('#M2D-option-author').val(config.pagedata.author);            $('#M2D-option-message-enable').change(function  {                $('#M2D-option-message-head').prop('disabled', !this.checked);                $('#M2D-option-message').prop('disabled', !this.checked);            });            $('#M2D-option-message-head').val(config.wikitext.notification_heading.replace(/\$1/g, getPageText(config.mw.wgPageName)));            $('#M2D-option-message').val(config.wikitext.notification.replace(/\$1/g, getPageText(config.mw.wgPageName)));

$("#M2D-interface-footer").append(               $(' ').attr('id', 'M2D-next').text('Continue'),                $(' ').attr('id', 'M2D-cancel').css('margin-left', '3em').text('Cancel')            );

$("#M2D-cancel").click(function {                $("#M2D-modal").remove;            });

if (restoreValues) { $('#M2D-option-movelog').val(config.inputdata.rationale); $('#M2D-option-newtitle').val(config.inputdata.newTitle); $('#M2D-option-author').val(config.inputdata.authorName); $('#M2D-option-message-enable').prop('checked', config.inputdata.notifyEnable); $('#M2D-option-message-head').val(config.inputdata.notifyMsgHead); $('#M2D-option-message').val(config.inputdata.notifyMsg); }

$("#M2D-next").click(function {                //Gather inputs                config.inputdata = {                    rationale: $('#M2D-option-movelog').val.trim,                    newTitle: "Draft:" + $('#M2D-option-newtitle').val.trim,                    authorName: $('#M2D-option-author').val.trim,                    notifyEnable: $('#M2D-option-message-enable').prop('checked'),                    notifyMsgHead: $('#M2D-option-message-head').val.trim,                    notifyMsg: $('#M2D-option-message').val.trim                };                config.inputdata.notifyMsgHead = config.inputdata.notifyMsgHead.replace(/\$2/g, config.inputdata.newTitle)                config.inputdata.notifyMsg = config.inputdata.notifyMsg.replace(/\$2/g, config.inputdata.newTitle)                config.inputdata.logMsg = config.wikitext.logMsg                    .replace(/\$1/g, getPageText(config.mw.wgPageName)) .replace(/\$2/g, config.inputdata.newTitle);

//Verify inputs var errors = []; if (config.inputdata.newTitle.length === 0) { errors.push("Invalid draft title"); }               if (config.inputdata.authorName.length === 0) { errors.push("Invalid user name"); }               if (config.inputdata.rationale.length === 0) { errors.push("Move log reason is empty"); }               if (config.inputdata.notifyEnable) { if (config.inputdata.notifyMsgHead.length === 0) { errors.push("Notification heading is empty"); }                   if (config.inputdata.notifyMsg.length === 0) { errors.push("Notification message is empty"); }               }                if (errors.length >= 1) { alert("Error:\n\n" + errors.join(";\n")); return; }

//start process off screen2; });

};

//2) Progress indicators       var screen2 = function  {            $("#M2D-interface-header, #M2D-interface-content, #M2D-interface-footer").empty;            $("#M2D-interface-header").text("Move To Draft: In progress...");            $("#M2D-interface-content").append( $('').attr('id', 'M2D-tasks').css("color", "#888").append(                   $('').attr('id', 'M2D-task0').append( 'Moving page... ',                       $(' ').attr('id', 'M2D-status0').text('waiting') ),                   $('').attr('id', 'M2D-task1').append( 'Checking images... ',                       $(' ').attr('id', 'M2D-status1').text('waiting') ),                   $('').attr('id', 'M2D-task2').append( 'Editing page wikitext... ',                       $(' ').attr('id', 'M2D-status2').text('waiting') ),                   config.inputdata.notifyEnable ?                        $('').attr('id', 'M2D-task3').append( 'Notifying author... ',                           $(' ').attr('id', 'M2D-status3').text('waiting') )                       : '',                    $('').attr('id', 'M2D-task4').append( 'Updating talk page banners... ',                       $(' ').attr('id', 'M2D-status4').text('waiting') ),

$('').attr('id', 'M2D-task5').append(                       'Logging... ',                        config.doNotLog                            ? $(' ').attr('font-size', '90%').text('disabled')                            : $(' ').attr('id', 'M2D-status5').text('waiting')                    ) )           );

$("#M2D-interface-footer").append(               $(' ').attr('id', 'M2D-abort').text('Abort uncompleted tasks'),                $(' ').attr('id', 'M2D-finished').hide.append( 'Finished!', $(' ').attr('id', 'M2D-close').text('Close') )           );

$("#M2D-close").click(function {                $("#M2D-modal").remove;                window.location.reload;            }); $("M2D-abort").click(function {                API.abort;                $("#M2D-modal").remove;                window.location.reload;            });

//Start task 0. The rest are done sequentially as each task is completed (or skipped). movePage; };

// --- Add link to 'More' menu (or user-specified portlet) which starts everything --- mw.util.addPortletLink((window.m2d_portlet || 'p-cactions'), '#', 'Move to draft', 'ca-m2d', null, null, "#ca-move"); $('#ca-m2d').on('click', function (e) {           e.preventDefault;            // Add interface shell            $('body').prepend(' ' + ' ' +               ' ' + ' ' +               ' ' +                ' ' +                ' ' +                ' ' +                ' ');

// Interface styling $("#M2D-modal").css({               "position": "fixed",                "z-index": "1",                "left": "0",                "top": "0",                "width": "100%",                "height": "100%",                "overflow": "auto",                "background-color": "rgba(0,0,0,0.4)"            }); $("#M2D-interface").css({               "background-color": "#f0f0f0",                "margin": "15% auto",                "padding": "2px 20px",                "border": "1px solid #888",                "width": "80%",                "max-width": "60em",                "font-size": "90%"            }); $("#M2D-interface-content").css("min-height", "7em"); $("#M2D-interface-footor").css("min-height", "3em");

// Initial interface content screen0; });

// End of function moveToDraft };

/* ========== Log draftifications for a user ==================================================== */ function logDraftifications(username, fromDate) { var targetUser = username; if (!targetUser && targetUser !== "") { var pageNameParts = config.mw.wgPageName.split('/'); targetUser = (pageNameParts.length > 2) ? pageNameParts[2] : ''; }       $('#mw-content-text').empty; // TODO: Form for setting user var today = new Date.toISOString.slice(0, 10); var MoveToDraftEpoch = "2017-05-29"; $('#mw-content-text').append(           $(` User:  From date (and earlier)   `)       );        $('#draftifyUsername').val(targetUser); $('#draftifyLogForm').on('submit', function (e) {           e.preventDefault;            $('#draftifyLog, #draftifyLogWikitext').show;            logDraftifications($('#draftifyUsername').val, $('#draftifyFromDate').val);        });

$('#mw-content-text').append(           $(`  `)       );

$('#draftifyLogWikitext').val(`{|class="wikitable" !scope='col'|From !scope='col'|To !scope='col'|Time !scope='col'|User !scope='col'|Reason
 * }`);

var query = { action: "query", format: "json", list: "logevents", leprop: "title|timestamp|comment|details|user", letype: "move", lelimit: "500", lestart: (fromDate || today) + "T23:59:59Z" };       if (targetUser) { query.leuser = targetUser; }

var continueInfo = {};

function onLoadMoreClick(e) { e.preventDefault; $('#draftifyStatus').empty.text("Loading..."); searchAndShowResults; }

function parseLogTable(wikitext) { API.post({               "action": "parse",                "format": "json",                "text": wikitext,                "prop": "text",                "contentmodel": "wikitext"            }).then(function (response) {                $parsedLogTable = $(response.parse.text['*']);                $('#draftifyLog tbody').empty.append( $parsedLogTable.find('tr').slice(1) );           });        }

function searchAndShowResults { API.get($.extend({}, query, continueInfo)) .then(function (response) {                   // Store continuing info, if any                    continueInfo = response.continue || {};                    // Reset status, add a "Load more" if there are more results                    $('#draftifyStatus').empty.append( response.continue ? $('<a>').css("cursor", "pointer").text('Load more').click(onLoadMoreClick) : null );                   // Filter to only MoveToDraft script moves                    var draftifyEvents = response.query && response.query.logevents && response.query.logevents.filter(function (logevent) { return logevent.params.target_ns === 118; // Moved to Draft namespace });                   var noDraftifyEvents = !draftifyEvents || !draftifyEvents.length;

switch (true) { case noDraftifyEvents && !response.continue: $('#draftifyStatus').empty.text(                               $('#draftifyLog tbody tr').length == 0 ? "No results" : "No further results"                            ); break; case noDraftifyEvents: // Continue with next batch of results, otherwise table will initially have no results but a load more link, // or clicking "Load more" will appear to show "Loading..." but not actually add any results searchAndShowResults; break; case !response.continue: $('#draftifyStatus').empty.text("No further results"); /* falls through */ default: draftifyEvents.forEach(function (logevent) {                               var fromTitle = logevent.title;                                var toTitle = logevent.params.target_title;                                var timeOfMove = new Date(logevent.timestamp).toUTCString.replace("GMT", "(UTC)");                                var user = logevent.user;                                var comment = logevent.comment;                                var wikitext = $('#draftifyLogWikitext').val.replace("|}", `|- $('#draftifyLogWikitext').val(wikitext); parseLogTable(wikitext); });                   }                });        }
 * ${fromTitle}
 * ${toTitle}
 * ${timeOfMove}
 * ${user}
 * ${comment}
 * }`);

// Run by default, unless page loaded without a /username suffix if (username || username === "") { searchAndShowResults; } else { $('#draftifyLog, #draftifyLogWikitext').hide; }

// End of function logDraftifications }

/* ========== Setup ============================================================================= */ // Access draftifications using Special:BlankPage/Draftify_log/USER_NAME var isDraftifyLogPage = config.mw.wgPageName.indexOf("Special:BlankPage/Draftify_log") === 0; var isUserPage = config.mw.wgNamespaceNumber === 2 || config.mw.wgNamespaceNumber === 3; if (isDraftifyLogPage) { document.title = "Draftify log - Wikipedia"; $('h1').text("Draftify log"); $('#mw-content-text').empty .text("Loading...") .before(               $(' ').append( 'Note: This page only works with the ', $('<a>').attr('href', '/wiki/PhantomTech/scripts/MoveToDraft.js').text('MoveToDraft'), ' userscript installed.' ),               $(' ')            );        logDraftifications; } else if (isUserPage) { var user = config.mw.wgTitle.split('/')[0]; var url = mw.util.getUrl("Special:BlankPage/Draftify_log/" + user); mw.util.addPortletLink((window.m2d_portlet || 'p-cactions'), url, 'Draftify log', 'ca-m2dlog', null, null, "#ca-move"); }

// Only operate in allowed namespaces if (config.mw.wgNamespaceNumber === 0) { } else if (config.mw.wgNamespaceNumber === 2 || config.mw.wgNamespaceNumber === 3) { } else { return }

// Only operate for existing pages if (config.mw.wgCurRevisionId === 0) { return; }

moveToDraft(config.mw.wgNamespaceNumber);

}); //