User:Ahecht/sandbox/Scripts/refresh.js

Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
// 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 pageList = [];
	
	function getWait(d, type, totalCount) {
		var wait=2000;
		if (d && d.query && d.query.userinfo) {
			if (d.query.userinfo.rights 
				&& d.query.userinfo.rights.includes("noratelimit")) {
				wait = 1;
			} else if (d.query.userinfo.ratelimits
				&& d.query.userinfo.ratelimits[type]
				&& d.query.userinfo.ratelimits[type].user
				&& d.query.userinfo.ratelimits[type].user.hits
				&& d.query.userinfo.ratelimits[type].user.seconds)
			{
				var hits = d.query.userinfo.ratelimits[type].user.hits;
				var seconds = d.query.userinfo.ratelimits[type].user.seconds;
				console.log(type + " rate limit: hits=" + hits + ", seconds=" + seconds);
				if (hits < totalCount) {
					wait = Math.ceil( (seconds/hits) * 1000 );
				} else {
					console.log(totalCount+" items to refresh is less than "+hits);
					wait = 1;
				}
			}
		}
		console.log("Using " + wait + " milliseconds wait between queries");
		return wait;
	}
	
	function doRefresh(action, count, totalCount, wait) {
		function postFail(code, error) {
			console.error(error);
			alert("Error performing " + action + ":" + code + "!");
		}
		
		function postDone(p) {
			mw.notify((count + 1) + " of " + totalCount + " page(s) were updated", { tag: "bubble"+count } );
			count += 1;
			
			if (pageList.length > 0) {
				setTimeout(function() {
					doRefresh(action, count, totalCount, wait);
				}, wait);
			} else {
				if (confirm("Done!\n\nReload page?") == true) {
					document.location.reload();
				}
			}
		}
		
		var apiParams = {
			title: pageList.shift(),
			action: action
		};
		
		if (action == "purge") {
			apiParams.forcerecursivelinkupdate = "1";
			new mw.Api().post(apiParams).fail(postFail).done(postDone);
		} else {
			apiParams.watchlist = "nochange";
			apiParams.nocreate = "1";
			apiParams.appendtext = "";
			new mw.Api().postWithEditToken(apiParams).fail(postFail).done(postDone);
		}
	}
	
	function getList(action, target, addParams) {
		mw.notify("Fetching " + target.generator + "...", { tag: "bubble0" } );
		var queryParams = $.extend({
				action: 'query', 
				formatversion: '2',
				prop: ''
			},
			target,
			addParams);
		new mw.Api().post(queryParams).fail(function(code, error) {
			console.error(error);
			alert("Error fetching page titles: " + code + "!");
		} ).done(function(q) {
			if(q && q.warnings === undefined && q.query && q.query.pages) {
				for (var page in q.query.pages) {
					if (q.query.pages[page].title) {
						pageList.push(q.query.pages[page].title);
					}
				}
				if (q["continue"] !== undefined	
					&& (q["continue"].gticontinue
						|| q["continue"].gcmcontinue
						|| q["continue"].glhcontinue
					)
				) {
					getList(action, target, q["continue"]);
				} else {
					console.log(pageList);
					new mw.Api().get( {
						meta: 'userinfo',
						uiprop: 'ratelimits'
					} ).fail( function(e) {
						console.error(e);
						doRefresh(action, 0, pageList.length, 1000);
					} ).done( function(ui) {
						doRefresh(action, 0, pageList.length, getWait(ui, action, pageList.length));
					} );
				}
			}
		} );
	}
	
	if ( (mw.config.get('wgNamespaceNumber') == 10) 
		|| (mw.config.get('wgNamespaceNumber') == 14) 
		|| (mw.config.get('wgNamespaceNumber') == 828) 
		|| (mw.config.get("wgCanonicalSpecialPageName") == "Whatlinkshere") )
	{
		var linkTitle="", toolTipText="";
		var target = mw.config.get("wgRelevantPageName").replace(/_/g, " ");
		if ( (mw.config.get('wgNamespaceNumber') == 10) || (mw.config.get('wgNamespaceNumber') == 828) ){
			target = {
				generator: 'transcludedin',
				titles: target,
				gtilimit: 'max'
			};
			linkTitle = "transcluding pages";
			toolTipText = "that transclude this template.";
		} else if (mw.config.get('wgNamespaceNumber') == 14) {
			target = {
				generator: 'categorymembers',
				gcmtitle: target,
				gcmlimit: 'max'
			};
			linkTitle = "category members";
			toolTipText = "in this category.";
		} else {
			target = {
				generator: 'linkshere',
				titles: target,
				glhlimit: 'max'
			};
			linkTitle = "linking pages";
			toolTipText = "that link to this page.";
		}
		$(mw.util.addPortletLink('p-cactions', '#', 'Purge ' + linkTitle, 'pt-refresh-purge', 'Perform a "forcelinkupdate" purge on all pages ' + toolTipText))
			.click(function() {
				getList("purge", target);
			});
		$(mw.util.addPortletLink('p-cactions', '#', 'Null edit ' + linkTitle, 'pt-refresh-null', 'Perform a null edit on all pages ' + toolTipText))
			.click(function() {
				getList("edit", target);
			});
	}
});