MediaWiki:Common.js/User:Fumple/PatrolTool.js

function patroltool_error(data) { $("#funpl-patroltool").css("display", "none"); $("#funpl-patroltool-error").css("display", ""); $("#funpl-patroltool-error-text").text(data); console.error(data); throw new Error(data); }

$(function {    try { //try catch all the lines!

// Utility functions function disable(el)         { setDisabled(el, true)  } function enable(el)          { setDisabled(el, false) } function setDisabled(el, val) { el.disabled = val; el.updateDisabled }

var namespaceselect = null, ignoreuserselect = null, ignorepageselect = null, continuebutton = null, patrolbutton = null, cignorebutton = null, loadPreviewbutton = null, ignoreUserbutton = null, ignorePagebutton = null, cblayout = null, finalchanges = [], currentchange = -1, ignoreUsers = [], ignorePages = [], mwApi = new mw.Api;

mwApi.get({           "action": "query",            "format": "json",            "meta": "userinfo",            "uiprop": "rights"        }).done((data) => {            console.log("[PatrolTool] Rights", data);            if (data.query.userinfo.rights.includes("patrol")) {                mw.loader.using("oojs-ui-widgets", loadFilters)            }            else if (data.query.userinfo.id != 0) {                $("#funpl-patroltool").css("display", "none");                $("#funpl-patroltool-error").css("display", "");                $("#funpl-patroltool-error-text").html("You can't use this tool as you don't have the correct rights");            }            else {                $("#funpl-patroltool").css("display", "none");                $("#funpl-patroltool-error").css("display", "");                $("#funpl-patroltool-error-text").html("You can't use this tool as you don't have the correct rights You're also logged out, maybe you want to log in"); }       }).catch(patroltool_error);

async function loadFilters { try { $("#funpl-patroltool-header").text("Loading namespaces..."); mwApi.get({                   "action": "query",                    "format": "json",                    "meta": "siteinfo",                    "siprop": "namespaces"                }).done((data) => {                    console.log("namespaces", data);                    namespaceselect = new OO.ui.MenuTagMultiselectWidget({ "options": [{ "data": "*", "label": "All namespaces" }]                   });                    ignoreuserselect = new OO.ui.TagMultiselectWidget({ allowArbitrary: true, placeholder: "Type in an username and press enter" });                   ignorepageselect = new OO.ui.TagMultiselectWidget({ allowArbitrary: true, placeholder: "Type in a name and press enter" });                   let layout = new OO.ui.FieldLayout(namespaceselect, { "align": 'top', "label": 'Namespaces', "id": "funpl-patroltool-nss" });                   let layout2 = new OO.ui.FieldLayout(ignoreuserselect, { "align": 'top', "label": 'Ignore edits by users', "id": "funpl-patroltool-ius" });                   let layout3 = new OO.ui.FieldLayout(ignorepageselect, { "align": 'top', "label": 'Ignore edits to pages', "id": "funpl-patroltool-ips" });                   continuebutton = new OO.ui.ButtonWidget({ label: 'Continue', icon: 'arrowNext', accessKey: 'd'                   });                    cblayout = new OO.ui.FieldLayout(continuebutton, { "align": 'top', "label": '', "id": "funpl-patroltool-cb", "notices": ["Pro tip: Want to save your filters? Bookmark this page after you click continue"] });                   var url = new URL(window.location);                    if (url.searchParams.has("namespaces"))                        var urlns = url.searchParams.get("namespaces").split('|');                    else                        var urlns = [];                    if (url.searchParams.has("ignoreusers"))                        var urliu = url.searchParams.get("ignoreusers").split('|');                    else                        var urliu = [];                    if (url.searchParams.has("ignorepages"))                        var urlip = url.searchParams.get("ignorepages").split('|');                    else                        var urlip = [];

var selected = []; if (urlns.includes("*")) selected = selected.concat({                           "data": "*",                            "label": "All namespaces"                        }); for (var nsid in data.query.namespaces) { namespaceselect.addOptions([{                           "data": nsid,                            "label": data.query.namespaces[nsid]["*"] != "" ? data.query.namespaces[nsid]["*"] : "(Main)"                        }]) if (urlns.includes(nsid)) selected = selected.concat({                               "data": nsid,                                "label": data.query.namespaces[nsid]["*"] != "" ? data.query.namespaces[nsid]["*"] : "(Main)"                            }); }                   namespaceselect.setValue(selected); ignoreuserselect.setValue(urliu); ignorepageselect.setValue(urlip); $("#funpl-patroltool-header").text("Select filters"); $("#funpl-patroltool-loading").css("display", "none"); $("#funpl-patroltool").append(layout.$element); $("#funpl-patroltool").append(layout2.$element); $("#funpl-patroltool").append(layout3.$element); $("#funpl-patroltool").append(cblayout.$element); continuebutton.on("click", function {                        disable(continuebutton);                        disable(namespaceselect);                        disable(ignoreuserselect);                        disable(ignorepageselect);                        $("#funpl-patroltool-header").text("Loading changes...");                        cblayout.setNotices(["Loading... This might take a second"]);                        $("#funpl-patroltool-loading").css("display", "");

ignoreUsers = ignoreUsers.concat(...ignoreuserselect.getValue) ignorePages = ignorePages.concat(...ignorepageselect.getValue)

var url = new URL(window.location);

var namespace = namespaceselect.getValue.includes("*") ? "*" : namespaceselect.getValue.join('|'); if (namespace == "") namespace = "*" url.searchParams.set("namespaces", namespace);

var ignoreuserstxt = ignoreuserselect.getValue.join('|'); if (ignoreuserstxt != "") url.searchParams.set("ignoreusers", ignoreuserstxt); else url.searchParams.delete("ignoreusers")

var ignorepagestxt = ignorepageselect.getValue.join('|'); if (ignorepagestxt != "") url.searchParams.set("ignorepages", ignorepagestxt); else url.searchParams.delete("ignorepages")

window.history.pushState("", "", url.toString);

loadChanges; })               }).catch(patroltool_error); }           catch (ex) { patroltool_error(ex); }       }

async function loadChanges(rccontinue) { return new Promise(function (k, no) {               try {                    var namespace = namespaceselect.getValue.includes("*") ? "*" : namespaceselect.getValue.join('|');                    if (namespace == "") namespace = "*"

var options = { "action": "query", "format": "json", "list": "recentchanges", "rcnamespace": namespace, "rcprop": "title|timestamp|ids|user|parsedcomment", "rcshow": "unpatrolled", "rcdir": "newer", "rclimit": "max" };                   if (rccontinue != null) options.rccontinue = rccontinue; mwApi.get(options).done(async function (data) {                       console.log("rcdata", data);                        var changes = data.query.recentchanges;                        if (data.continue != null) {                            var cchanges = await loadChanges(data.continue.rccontinue);                            changes = changes.concat(cchanges);                        }                        if (rccontinue != null) {                            k(changes);                            return;                        }                        console.log(changes.length, changes);                        finalchanges = changes;                        window.patroltoolchanges = finalchanges;                        $("#funpl-patroltool-header").text("Ready!");                        cblayout.setNotices(["Found " + changes.length + " changes", "Press continue to load the first edit"]); $("#funpl-patroltool-loading").css("display", "none"); enable(continuebutton); continuebutton.off("click"); continuebutton.on("click", function {                            continuebutton.off("click");                            disable(continuebutton);

$("#funpl-patroltool-header").text("Preparing..."); cblayout.setNotices([]); $("#funpl-patroltool-loading").css("display", ""); $("#funpl-patroltool-nss").remove; $("#funpl-patroltool-ius").remove; $("#funpl-patroltool-ips").remove; $("#funpl-patroltool-cb").remove; $("#funpl-patroltool").append(' '); $("#funpl-patroltool").append('Diff '); $("#funpl-patroltool").append(' '); patrolbutton = new OO.ui.ButtonWidget({                               "label": "Mark as patrolled and continue",                                "icon": 'arrowNext',                                "disabled": true,                                "flags": ["primary", "progressive"],                                "accessKey": "d"                            }); cignorebutton = new OO.ui.ButtonWidget({                               "label": 'Ignore and continue',                                "icon": 'arrowNext',                                "disabled": true,                                "flags": ["progressive"],                                "accessKey": "s"                            }); loadPreviewbutton = new OO.ui.ButtonWidget({                               "label": 'Load preview',                                "icon": 'article',                                "disabled": true,                                "accessKey": "a"                            }); ignoreUserbutton = new OO.ui.ButtonWidget({                               "label": 'Ignore edits done by this user',                                "icon": 'clear',                                "disabled": true                            }); ignorePagebutton = new OO.ui.ButtonWidget({                               "label": 'Ignore edits to this page',                                "icon": 'articleRedirect',                                "disabled": true,                                "accessKey": "a"                            }); var playout = new OO.ui.HorizontalLayout({                               items: [                                    patrolbutton, cignorebutton, loadPreviewbutton                                ]                            }); var playout2i = new OO.ui.Widget({                               content: [new OO.ui.HorizontalLayout({ items: [ ignoreUserbutton, ignorePagebutton ]                               })]                            });                            var playout2 = new OO.ui.FieldLayout(playout2i, {                                align: 'top',                                helpInline: true,                                help: "These buttons only apply to the current patrolling session"                            }) $("#funpl-patroltool").append(playout.$element); $("#funpl-patroltool").append(playout2.$element); $("#funpl-patroltool").append('Page preview '); $("#funpl-patroltool").append(' '); loadNextChange; })                   }).catch(patroltool_error).catch(no); }               catch (ex) { patroltool_error(ex); }           });        }        function loadNextChange {            if (finalchanges[currentchange + 1] == null) {                $("#funpl-patroltool-header").text("All done!");                $("#funpl-patroltool-loading").css("display", "none");                $("#funpl-patroltool-desc").html('You reached the end of the queue!\nIf you want to load new changes in, refresh this page');                $("#funpl-patroltool-diff-header").remove;                $("#funpl-patroltool-diff").remove;                $("#funpl-patroltool-preview-header").remove;                $("#funpl-patroltool-preview").remove;            }            else                loadChange(currentchange + 1);        }        function loadChange(i) {            try {                if (finalchanges[i] == null) {                    return patroltool_error("Change not found")                } $("#funpl-patroltool-header").text("Loading..."); $("#funpl-patroltool-loading").css("display", ""); $("#funpl-patroltool-desc").html(''); $("#funpl-patroltool-diff").html(''); $("#funpl-patroltool-preview").html(''); currentchange = i; //and this is the place where we load the changes. how exciting var change = finalchanges[currentchange]; console.log("[PatrolTool] Loading change ", change); console.log("[PatrolTool] " + (i + 1) + "/" + finalchanges.length); console.log("[PatrolTool] Checking filters"); if (ignoreUsers.includes(change.user)) { console.log("[PatrolTool] Skipping due to ignored user"); loadNextChange; return; }               if (ignorePages.includes(change.title)) { console.log("[PatrolTool] Skipping due to ignored page"); loadNextChange; return; }               console.log("[PatrolTool] Checking whether change " + i + " is patrolled");

mwApi.get({ //Try to find the edit in the unpatrolled section, if it isn't found, skip this edit as someone patrolled it                   "action": "query",                    "format": "json",                    "list": "recentchanges",                    "rcstart": finalchanges[currentchange].timestamp,                    "rcend": finalchanges[currentchange].timestamp,                    "rcshow": "unpatrolled"                }).done((data) => {                    console.log("rccheck", data);                    if (data.query.recentchanges.length > 0) {                        console.log("[PatrolTool] Change " + i + " is not patrolled, loading it");                        $("#funpl-patroltool-header").text('[' + change.type + '] ' + change.title);                        if (change.type == "edit") {                            mwApi.get({ //Get the diff "action": "compare", "format": "json", "fromrev": change.old_revid, "torev": change.revid, "prop": "diff|ids|title|user|parsedcomment" }).done((data) => { console.log("compare", data); let compare = data.compare; console.log("[PatrolTool] Loaded compare for change " + i); $("#funpl-patroltool-desc").html(` `) $("#funpl-patroltool-diff").html(compare["*"]); }).catch(patroltool_error);                       }                        else {                            console.log("[PatrolTool] Ignored compare for change " + i);                            $("#funpl-patroltool-desc").html(` `)                            $("#funpl-patroltool-diff").html("No diff data available");                        }

enable(patrolbutton); patrolbutton.off("click"); patrolbutton.on("click", function {                            patrolbutton.off("click");                            disable(patrolbutton);                            cignorebutton.off("click");                            disable(cignorebutton);                            disable(loadPreviewbutton);                            $("#funpl-patroltool-header").text("Loading...");                            $("#funpl-patroltool-loading").css("display", "");                            $("#funpl-patroltool-desc").html();                            $("#funpl-patroltool-diff").html();                            $("#funpl-patroltool-preview").html('');                            mwApi.postWithToken("patrol", { "action": "patrol", "format": "json", "rcid": change.rcid }).done(function { loadNextChange; }).catch(function (err) { if (err == "notpatrollable") { alert("Failed to patrol edit due to it being not patrolable"); loadNextChange; }                               else { patroltool_error(err); }                           });                        });

enable(cignorebutton); cignorebutton.off("click"); cignorebutton.on("click", function {                            patrolbutton.off("click");                            disable(patrolbutton)                            cignorebutton.off("click");                            disable(cignorebutton)                            disable(loadPreviewbutton)                            loadNextChange;                        }); enable(loadPreviewbutton); loadPreviewbutton.on("click", function {                            disable(patrolbutton);                            disable(cignorebutton);                            disable(loadPreviewbutton);                            loadPreview(currentchange);                            enable(patrolbutton);                            enable(cignorebutton);                        });

enable(ignoreUserbutton); ignoreUserbutton.off("click"); ignoreUserbutton.on("click", function {                            disable(ignoreUserbutton);                            if (!ignoreUsers.includes(change.user))                                ignoreUsers = ignoreUsers.concat(change.user);                        });

enable(ignorePagebutton); ignorePagebutton.off("click"); ignorePagebutton.on("click", function {                            disable(ignorePagebutton);                            if (!ignorePages.includes(change.title))                                ignorePages = ignorePages.concat(change.title); //Yes, I intentionally used the name instead of the ID // Comment from 2023 Fumple: Ok, but why?                        }); $("#funpl-patroltool-loading").css("display", "none"); }                   else { console.log("[PatrolTool] Change " + i + " is patrolled, skipping"); loadNextChange; }               }).catch(patroltool_error);            }            catch (ex) {                patroltool_error(ex);            }        }

function loadPreview(i) { $("#funpl-patroltool-loading").css("display", ""); var pchange = finalchanges[i]; mwApi.get({               "action": "parse",                "format": "json",                "oldid": pchange.revid            }).done((parse) => {                console.log("[PatrolTool] parse", parse);                $("#funpl-patroltool-preview").html(parse.parse.text["*"]);                $("#funpl-patroltool-preview").append(' ');                let cattext = "";                for (var cat of parse.parse.categories) {                    cattext += '' + cat["*"].replace(/_/g, " ").encodeHTML + '';                }                if (cattext != "") {                    $("#funpl-patroltool-categories").html("Categories:" + cattext + "");                }                $("#funpl-patroltool-loading").css("display", "none");            }).catch(function (err) {                if (err == "nosuchrevid") { $("#funpl-patroltool-preview").html("You can't preview this edit"); $("#funpl-patroltool-loading").css("display", "none"); }               else { patroltool_error(err); }           });        }

window.loadChange = loadChange; if (!String.prototype.encodeHTML) { //https://stackoverflow.com/a/7918944 String.prototype.encodeHTML = function { return this.replace(/&/g, '&amp;') .replace(//g, '&gt;') .replace(/"/g, '&quot;')                   .replace(/'/g, '&apos;');            };        }

if (!String.prototype.decodeHTML) { //https://stackoverflow.com/a/7918944 String.prototype.decodeHTML = function { return this.replace(/&apos;/g, "'") .replace(/&quot;/g, '"')                   .replace(/&gt;/g, '>')                    .replace(/&lt;/g, '<')                    .replace(/&amp;/g, '&');            };        }

function encode(str) { return str.replace(/"/g, "\"") .replace(/\\/g, "\\\\"); }

function urlSafe(str) { return encode(encodeURIComponent(str)); }   }    catch (err) { patroltool_error(err) } })