User:Ahecht/Scripts/refresh.js

// Add "refresh" option on category pages, template pages, and on

// "Special:WhatLinksHere". Makes forceupdate nulledit on all pages in the

// category, all transclusing pages, or all linked pages.

// Based on [https://phabricator.wikimedia.org/T170039#3473755] and :he:User:IKhitron/101.js

mw.loader.using( [ 'mediawiki.util', 'mediawiki.api' ] ).then( function() {

var api = new mw.Api( { userAgent: 'refresh/0.0.1' } );

var pageList = [];

const reuse = ( typeof refreshReuseBubble === 'undefined' ? false : refreshReuseBubble );

function shuffleArray(array) {

console.log('Shuffling array...');

for (let i = array.length - 1; i >= 0; i--) {

const j = Math.floor(Math.random() * (i + 1));

[array[i], array[j]] = [array[j], array[i]];

}

}

function getWait(d, totalCount) {

var wait={edit: 8000, purge: 2100};

if (d && d?.query?.userinfo) {

var ratelimits = d.query.userinfo?.ratelimits;

if (d.query.userinfo.rights

&& d.query.userinfo.rights.includes("noratelimit")) {

wait = {edit: 1, purge: 1};

} else if (ratelimits) {

['edit', 'purge'].forEach ( (v) => {

var u = ratelimits?.[v]?.user;

if (u && u?.hits && u?.seconds) {

console.log(v + " rate limit: hits=" + u.hits + ", seconds=" + u.seconds);

if (u.hits < totalCount) {

wait[v] = Math.ceil( (u.seconds/u.hits) * 1050 );

} else {

console.log(totalCount+" items to refresh is less than "+u.hits);

wait[v] = 1;

}

}

} );

}

}

console.log("Millisecond waits between queries:");console.log(wait);

return wait;

}

function doRefresh(action, count, totalCount, wait) {

function postDone() {

if (pageList.length > 0) {

setTimeout(function() {

doRefresh(action, count, totalCount, wait);

}, wait[action]);

} else if (confirm("Done!\n\nReload page?") == true) {

document.location.reload();

} else {

mw.notify("Done!", {type: 'success', tag: "bubble" + (reuse ? 0 : 'done')});

}

}

function postFail(code, error) {

console.error(error);

var err = error?.textStatus || code;

var errMsg;

//if (confirm("Error performing " + action + ": " + err + "!\n\nContinue?") == true) {

errMsg = "Continuing after "+err+" error...";

postDone();

//} else {

// errMsg = "Aborted due to "+err+" error!";

//}

mw.notify(errMsg, { tag: "bubble" + (reuse ? 0 : "error"), type: 'error',

autoHideSeconds: (action == 'purge') ? 'long' : 'short' } );

}

function postSuccess() {

count = count + ( (action == "purge") ? ((apiParams.titles.match(/\|/g) || []).length + 1) : 1 );

mw.notify(count + " of " + totalCount + " page(s) were " + ((action == "purge") ? "purged" : "edited"), {

tag: "bubble" + (reuse ? 0 : "count"),

autoHideSeconds: (action == 'purge') ? 'long' : 'short'

} );

postDone();

}

var apiParams = {action: action};

if (action == "purge") {

// 'purge' is always once every two second unless 'noratelimit',

// so give editors with higher 'edit' rate a boost.

// More than 3 at a time leads to timeouts.

var numPages = wait.edit < 1000 ? 3 : 1;

apiParams.titles = pageList.splice(0, numPages).join('|');

console.log(apiParams.titles);

apiParams.forcerecursivelinkupdate = "1";

//apiParams.forcelinkupdate = "1";

api.post(apiParams).fail(postFail).done(postSuccess);

} else {

apiParams.title = pageList.shift();

console.log(apiParams.title);

apiParams.watchlist = "nochange";

apiParams.nocreate = "1";

apiParams.appendtext = "";

api.postWithEditToken(apiParams).fail(postFail).done(postSuccess);

}

}

function getList(action, target, addParams) {

mw.notify("Fetching " + target.generator + "...", { tag: "bubble0" } );

var queryParams = $.extend({

action: 'query',

formatversion: '2',

prop: ''

},

target,

addParams);

var list = target?.list || "pages";

if (queryParams.cmsort && window.refreshCatSort) queryParams.cmsort = window.refreshCatSort;

if (queryParams.cmdir && window.refreshCatDir) queryParams.cmdir = window.refreshCatDir;

if (queryParams.srsort && window.refreshSearchSort) queryParams.srsort = window.refreshSearchSort;

console.log(queryParams);

api.get(queryParams).fail(function(code, error) {

console.error(error);

alert("Error fetching page titles: " + code + "!");

} ).done(function(q) {

if(q && q.warnings === undefined && q?.query?.[list] !== undefined) {

q.query[list].forEach(page => {

if (page.title) pageList.push(page.title);

});

if (q?.continue?.continue !== undefined) {

getList(action, target, q.continue);

} else {

if (window.refreshShuffle) shuffleArray(pageList);

console.log(pageList);

api.get( {

meta: 'userinfo',

uiprop: 'ratelimits|rights'

} ).fail( function(e) {

console.error(e);

doRefresh(action, 0, pageList.length, {edit: 8000, purge: 2100});

} ).done( function(ui) {

var len = pageList.length;

mw.notification.autoHideLimit = len;

mw.notify('Performing '+action+' on '+len+' page(s)...', {

autoHideSeconds: (action == 'purge') ? 'long' : 'short',

tage: 'bubble0'

} );

doRefresh(action, 0, len, getWait(ui, len));

} );

}

}

} );

}

var linkshere = mw.config.get("wgCanonicalSpecialPageName") == "Whatlinkshere";

var sparams = new URLSearchParams(window.location.search);

var search= (mw.config.get("wgCanonicalSpecialPageName") == "Search" &&

sparams.get('search') && sparams.get('search') != '');

if ( (mw.config.get('wgNamespaceNumber') == 10)

|| (mw.config.get('wgNamespaceNumber') == 14)

|| (mw.config.get('wgNamespaceNumber') == 828)

|| linkshere || search)

{

var linkTitle="linking pages", toolTipText="that link to this page.";

var target = mw.config.get("wgRelevantPageName").replace(/_/g, " ");

var targetNS = mw.Title.newFromText(target).getNamespaceId();

var query = {

generator: 'linkshere',

titles: target,

glhlimit: 'max'

};

if (search) {

linkTitle = "search results";

toolTipText = "in this search result.";

query = {

list: 'search',

srsearch: sparams.get('search'),

srlimit: 'max',

srsort: 'last_edit_desc',

srprop: ''

};

nslist = [];

for(const e of sparams.entries()) {

var nsp = e[0].match(/^ns(\d+)/);

if (nsp?.[1] && e[1] == 1) nslist.push(nsp[1]);

}

if (nslist.length > 0 ) query.srnamespace = nslist.join("|");

} else if ( (targetNS == 10) || (targetNS == 828) ){

if ( linkshere ) {

toolTipText = "that link to this template.";

} else {

query = {

generator: 'transcludedin',

titles: target,

gtilimit: 'max'

};

linkTitle = "transcluding pages";

toolTipText = "that transclude this template.";

}

} else if (targetNS == 14) {

if ( linkshere ) {

toolTipText = "that link to this category.";

} else {

query = {

list: 'categorymembers',

cmtitle: target,

cmlimit: 'max',

cmsort: 'timestamp',

cmdir: 'desc'

};

linkTitle = "category members";

toolTipText = "in this category.";

}

}

$(mw.util.addPortletLink('p-cactions', '#', 'Purge ' + linkTitle, 'pt-refresh-purge', 'Perform a "forcelinkupdate" purge on all pages ' + toolTipText))

.click(function() {

getList("purge", query);

});

$(mw.util.addPortletLink('p-cactions', '#', 'Null edit ' + linkTitle, 'pt-refresh-null', 'Perform a null edit on all pages ' + toolTipText))

.click(function() {

getList("edit", query);

});

}

});