Jump to content

User:Novem Linguae/Scripts/Links.js

From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by Novem Linguae (talk | contribs) at 00:41, 19 January 2023 (debug). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.
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.
// <nowiki>

/*
	This script adds a left menu below the toolbox called "More tools", and includes some links:

	- userspace only
		- common.js
		- global.js
		- vector.js
		- common.css
		- central auth (good for seeing what global permissions people have)
		- rename log
		- global lock log
		- Twinkle CSD log
		- Twinkle PROD log
		- Twinkle XfD log
		- Draftify log

	- all namespaces
		- subpages
		
	This script also adds "Pending changes" to the left main menu.
*/

$(async function() {
	class Links {
		async execute() {
			this._addPendingChangesToLeftMenu();
			this.pageName = mw.config.get('wgPageName');
			this._generateLinksForUserSpace();
			await this._generateLinksForAllNameSpaces();
			this._insertLinksInLeftMenu();
		}

		_insertLinksInLeftMenu() {
			let menuTitle = 'More tools';
			let html = '';
			let skin = mw.config.get('skin');
			switch ( skin ) {
				case 'minerva':
					html = `
						<ul id="p-npp-links">
							${this.userLinks}
							${this.allPageLinks}
						</ul>`;

					html = html.replace(/<li>/g, '<li class="menu__item--preferences">');
					html = html.replace(/(<a[^>]*>)/g, '$1<span class="mw-ui-icon"></span><span>');
					html = html.replace(/<\/a>/g, '<span></a>');

					break;
				case 'monobook':
					html = `
						<div role="navigation" class="portlet mw-portlet mw-portlet-npp-links" id="p-npp-links" aria-labelledby="p-npp-links-label">
							<h3 id="p-npp-links-label">
								${menuTitle}
							</h3>
							<div class="pBody">
								<ul>
									${this.userLinks}
									${this.allPageLinks}
								</ul>
							</div>
						</div>`;
					break;
				case 'modern':
					html = `
						<div class="portlet mw-portlet mw-portlet-npp-links" d="p-npp-links" role="navigation">
							<h3 id="p-npp-links-label" lang="en" dir="ltr">
								${menuTitle}
							</h3>
							<div class="mw-portlet-body">
								<ul lang="en" dir="ltr">
									${this.userLinks}
									${this.allPageLinks}
								</ul>
							</div>
						</div>`;
					break;
				case 'timeless':
					html = `
						<div role="navigation" class="mw-portlet" id="p-npp-links" aria-labelledby="p-npp-links-label">
							<h3 id="p-npp-links-label" lang="en" dir="ltr">
								${menuTitle}
							</h3>
							<div class="mw-portlet-body">
								<ul lang="en" dir="ltr">
									${this.userLinks}
									${this.allPageLinks}
								</ul>
							</div>
						</div>`;
					break;
				case 'vector-2022':
					html = `
						<nav id="p-npp-links" class="vector-main-menu-group vector-menu mw-portlet mw-portlet-interaction">
							<div class="vector-menu-heading">
								<span class="vector-menu-heading-label">
									${menuTitle}
								</span>
							</div>
							<div class="vector-menu-content">
								<ul class="vector-menu-content-list">
									${this.userLinks}
									${this.allPageLinks}
								</ul>
							</div>
						</nav>`;
					break;
				case 'vector':
				default:
					html = `
						<nav id="p-npp-links" class="mw-portlet mw-portlet-npp-links vector-menu vector-menu-portal portal" aria-labelledby="p-npp-links-label" role="npp-links">
							<h3 id="p-npp-links-label" class="vector-menu-heading">
								${menuTitle}
							</h3>
							<div class="vector-menu-content">
								<ul class="vector-menu-content-list">
									${this.userLinks}
									${this.allPageLinks}
								</ul>
							</div>
						</nav>`;
					break;
			}
			$('#p-navigation').after(html);
		}

		_generateLinksForAllNameSpaces() {
			let parentName = this.pageName + '/';
			this.allPageLinks = '';
			if ( this.pageName.includes('/') ) {
				parentName = this._getFirstMatch(this.pageName, /^([^\/]+\/)/);
			}
			this.allPageLinks += `<li><a href="/wiki/Special:PrefixIndex/${parentName}">Subpages</a></li>`;
		}

		async _generateLinksForUserSpace() {
			this.username = this._getFirstMatch(this.pageName, /(?:User:|User_talk:)([^/]+).*/);
			this.userLinks = '';
			if ( this.username ) {
				this.username = 'User:' + this.username;
				this.usernameURI = encodeURIComponent(this.username.replace(/_/g, ' ').replace(/^User:/, ''));

				this._generateUserSpaceJsCssLinks();
				this._generateCentralAuthLinks();
				this._generateUserRightsLinks();
				this._generateRenameLogLinks();
				this._generateGlobalLockLinks();
				await this._generateTwinkleLogLinks();
			}
		}
		
		async _generateTwinkleLogLinks() {
			// twinkle logs (csd, prod, xfd) and draftify log
			// check if they exist with an API query before adding links
			let logPages = await this._pagesExist([
				`${this.username}/CSD log`,
				`${this.username}/PROD log`,
				`${this.username}/XfD log`,
				`${this.username}/Draftify log`,
			]);
			for ( let title of logPages ) {
				let shortTitle = title.replace(/^.*\//, '');
				this.userLinks += `<li><a href="https://en.wikipedia.org/wiki/${title}">${shortTitle}</a></li>`;
			}
		}
		
		_generateGlobalLockLinks() {
			this.userLinks += `<li><a href="https://meta.wikimedia.org/wiki/Special:Log?page=User%3A${this.usernameURI}%40global">Global lock log</a></li>`;
		}

		_generateRenameLogLinks() {
			// All modern renames seem to be put into both en:Special:Log->User rename log AND meta:Special:Log->User rename log.
			// One older rename was put only into meta:Special:Log->User rename log.
			// Another older rename was put only into en:Special:Log->User rename log, and was complicated by the fact that ~enwiki had been added to the end of it.
			// Spaces vs underscores don't seem to matter. User: or no User: doesn't seem to matter.
			this.userLinks += `<li><a href="https://meta.wikimedia.org/wiki/Special:Log?type=renameuser&user=&page=${this.usernameURI}">Rename log</a></li>`;
			this.userLinks += `<li><a href="https://en.wikipedia.org/wiki/Special:Log?type=renameuser&user=&page=${this.usernameURI}%7Eenwiki">Rename log (~enwiki)</a></li>`;
		}

		_generateUserRightsLinks() {
			this.userLinks += `<li><a href="/wiki/Special:Log?type=rights&user=&page=${this.usernameURI}">User rights log</a></li>`;
			this.userLinks += `<li><a href="https://meta.wikimedia.org/wiki/Special:Log?type=rights&user=&page=${this.usernameURI}@enwiki">User rights log (meta)</a></li>`;
		}
		
		_generateCentralAuthLinks() {
			this.userLinks += `<li><a href="/wiki/Special:CentralAuth?target=${this.usernameURI}">Central auth</a></li>`;
		}

		/** common.js and similar */
		_generateUserSpaceJsCssLinks() {
			this.userLinks += `<li><a href="/wiki/${this.username}/common.js">common.js</a></li>`;
			this.userLinks += `<li><a href="https://meta.wikimedia.org/wiki/${this.username}/global.js">global.js</a></li>`;
			this.userLinks += `<li><a href="/wiki/${this.username}/vector.js">vector.js</a></li>`;
			this.userLinks += `<li><a href="/wiki/${this.username}/common.css">common.css</a></li>`;
		}

		_addPendingChangesToLeftMenu() {
			mw.util.addPortletLink(
				'p-navigation',
				mw.util.getUrl('Special:PendingChanges'),
				'Pending changes'	// can't put comma here, silent error
			);
		}

		_getFirstMatch(string, regex) {
			let matches = string.match(regex);
			if ( matches && matches[1] ) {
				return matches[1];
			}
			return '';
		}

		/**
			@param {array} titles
		*/
		async _pagesExist(titles) {
			let api = new mw.Api();
			let response = await api.get( {
				action: 'query',
				format: 'json',
				prop: 'revisions',
				titles: titles.join('|'),
			} );

			/*
			Example format if exists:

			"66442411": {
				"pageid": 66442411,
				"ns": 2,
				"title": "User:Novem Linguae/CSD log",
				"revisions": [
					{
						"revid": 1091295780,
						"parentid": 1091255744,
						"user": "Novem Linguae",
						"timestamp": "2022-06-03T11:38:00Z",
						"comment": "Logging speedy deletion nomination of [[:Liquorose]]."
					}
				]
			},

			Example format if doesn't exist:

			"-1": {
				"ns": 2,
				"title": "User:Jmcclaskey54/CSD log",
				"missing": ""
			},
			*/
			response = response.query.pages;
			let pages = [];
			for ( let key in response ) {
				if ( parseInt(key) !== NaN && parseInt(key) > 0 ) {
					pages.push(response[key]['title']);
				}
			}

			return pages;
		}
	}

	$(async function() {
		let links = new Links();
		await links.execute();
	});
});

// </nowiki>