Jump to content

User:Ahecht/sandbox/Scripts/refresh.js

From Wikipedia, the free encyclopedia
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 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);
			});
	}
});