-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(MarkRights): split modules; rewrite code (#1648)
* refactor(MarkRights): split modules; rewrite code
- Loading branch information
1 parent
54722a9
commit 9c01a79
Showing
5 changed files
with
294 additions
and
253 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,166 +1,85 @@ | ||
import {queryGlobalUserGroups, queryUserGroups} from './query'; | ||
import type {UserRights} from './types'; | ||
import {getMessage} from './i18n'; | ||
import {uniqueArray} from 'ext.gadget.Util'; | ||
import {appendUserRightsMark} from './util/appendUserRightsMark'; | ||
import {generateUserLinks} from './util/generateUserLinks'; | ||
|
||
const getUsername = (url: string): string => { | ||
if (!url) { | ||
return ''; | ||
} | ||
const markUserRights = ($content: JQuery): void => { | ||
const {users, $userLinks} = generateUserLinks($content); | ||
|
||
const username: string | null = mw.util.getParamValue('title', url); | ||
const promises: (() => Promise<void>)[] = []; | ||
|
||
const decode = (string: string, replace: (_string: string) => string): string => { | ||
return decodeURIComponent( | ||
((): string => { | ||
try { | ||
return decodeURIComponent(replace(string)); | ||
} catch { | ||
return replace(string).replace(/%(?!\d+)/g, '%25'); | ||
} | ||
})() | ||
); | ||
}; | ||
|
||
if (username) { | ||
return decode(username, (string: string): string => { | ||
return string.replace('User:', '').replace(/_/g, ' '); | ||
}); | ||
} | ||
const usernameMatch: RegExpMatchArray | null = url.match(/\/wiki\/User:(.+?)$/); | ||
if (usernameMatch?.[1]) { | ||
return decode(usernameMatch[1], (string: string): string => { | ||
return string.replace(/_/g, ' '); | ||
}); | ||
} | ||
return ''; | ||
}; | ||
|
||
const appendUserRightsMark = ( | ||
$userLinks: JQuery<HTMLElement>, | ||
{userGroupMap, globalUserGroupMap}: {userGroupMap: Map<string, string[]>; globalUserGroupMap: Map<string, string[]>} | ||
): void => { | ||
for (const element of $userLinks) { | ||
const $element: JQuery = $(element); | ||
if ($element.parents('li').find('.gadgets-markrights').length) { | ||
continue; | ||
} | ||
if ($element.siblings('.gadgets-markrights').length) { | ||
for (let i = 0; i < users.length; i++) { | ||
const ususers = users.splice(0, 25); | ||
if (!ususers.length) { | ||
continue; | ||
} | ||
const username: string = getUsername($element.attr('href') ?? ''); | ||
if (!username) { | ||
continue; | ||
} | ||
const groups = userGroupMap.get(username) ?? []; | ||
const globalGroups = globalUserGroupMap.get(username) ?? []; | ||
if (!groups) { | ||
continue; | ||
} | ||
const $sups: JQuery = $('<span>').addClass('gadgets-markrights'); | ||
for (const group of uniqueArray([...groups, ...globalGroups])) { | ||
const className: string = `gadgets-markrights__${group}`; | ||
if ($sups.find('sup').hasClass(className)) { | ||
continue; | ||
} | ||
$sups.append( | ||
// The following classes are used here: | ||
// * gadget-markrights__qiuwen | ||
// * gadget-markrights__steward | ||
// * gadget-markrights__checkuser | ||
// * gadget-markrights__suppress | ||
// * gadget-markrights__sysop | ||
// * gadget-markrights__interface-admin | ||
// * gadget-markrights__templateeditor | ||
// * gadget-markrights__transwiki | ||
// * gadget-markrights__patroller | ||
// * gadget-markrights__autoreviewer | ||
// * gadget-markrights__senioreditor | ||
// * gadget-markrights__eventsponsor | ||
// * gadget-markrights__massmessage-sender | ||
// * gadget-markrights__confirmed | ||
// * gadget-markrights__autoconfirmed | ||
// * gadget-markrights__bot | ||
// * gadget-markrights__flood | ||
// * gadget-markrights__ipblock-exempt | ||
// * gadget-markrights__rnrsverify-exempt | ||
$('<sup>') | ||
.addClass(className) | ||
.attr({ | ||
alt: getMessage(group as UserRights), | ||
title: getMessage(group as UserRights), | ||
}) | ||
); | ||
} | ||
$element.after($sups); | ||
} | ||
}; | ||
|
||
const markUserRights = async ($content: JQuery): Promise<void> => { | ||
const $userLinks: JQuery = $content.find('a.mw-userlink:not(.mw-anonuserlink)'); | ||
let users: string[] = []; | ||
const queue: Array<typeof users> = []; | ||
const userGroupMap: Map<string, string[]> = new Map(); | ||
const globalUserGroupMap: Map<string, string[]> = new Map(); | ||
promises[promises.length] = async (): Promise<void> => { | ||
const userGroups: Record<string, string[]> = {}; | ||
|
||
for (const element of $userLinks) { | ||
const $element: JQuery = $(element); | ||
if ($element.parents('li').find('.gadgets-markrights').length) { | ||
continue; | ||
} | ||
if ($element.siblings('.gadgets-markrights').length) { | ||
continue; | ||
} | ||
const {textContent} = element; | ||
const userLinkText: string | undefined = textContent?.toString(); | ||
if (userLinkText) { | ||
users[users.length] = userLinkText; // Replace `[].push()` to avoid polyfilling core-js | ||
} | ||
try { | ||
const response = await queryUserGroups(ususers); | ||
const {users: queryUsers} = response['query'] as { | ||
users: {groups: string[]; name: string}[]; | ||
}; | ||
|
||
// 用户名列表去重 | ||
users = uniqueArray(users); // Replace `[...new Set()]` to avoid polyfilling core-js | ||
for (const user of queryUsers) { | ||
if (!user?.name || !user?.groups) { | ||
continue; | ||
} | ||
|
||
if (users.length === 25) { | ||
queue[queue.length] = users; // Replace `[].push()` to avoid polyfilling core-js | ||
users = []; | ||
} | ||
} | ||
|
||
if (users.length > 0) { | ||
queue[queue.length] = users; // Replace `[].push()` to avoid polyfilling core-js | ||
} | ||
const {name, groups} = user; | ||
|
||
for (const ususers of queue) { | ||
try { | ||
const queryUserResponse = await queryUserGroups(ususers); | ||
const {users: queryUsers} = queryUserResponse['query'] as { | ||
users: {groups: string[]; name: string}[]; | ||
}; | ||
userGroups[name] ??= []; | ||
|
||
for (const user of queryUsers) { | ||
if (!user || !user.groups) { | ||
continue; | ||
userGroups[name] = [ | ||
...userGroups[name], | ||
...groups.filter((element) => { | ||
return element !== '*'; | ||
}), | ||
]; | ||
} | ||
userGroupMap.set( | ||
user.name, | ||
user.groups.filter((element) => { | ||
return element !== '*'; | ||
}) | ||
); | ||
} catch (error: unknown) { | ||
console.error('[MarkRights] Ajax error:', error); | ||
} | ||
|
||
appendUserRightsMark($userLinks, userGroups); | ||
}; | ||
|
||
promises[promises.length] = async (): Promise<void> => { | ||
const userGroups: Record<string, string[]> = {}; | ||
|
||
for (const user of ususers) { | ||
const queryGlobalUserInfoResponse = await queryGlobalUserGroups(user); | ||
if (queryGlobalUserInfoResponse['query'] && queryGlobalUserInfoResponse['query'].globaluserinfo) { | ||
const {groups: globalgroups}: {groups: string[]} = queryGlobalUserInfoResponse['query'] | ||
.globaluserinfo as {groups: string[]}; | ||
globalUserGroupMap.set(user, globalgroups); | ||
try { | ||
const response = await queryGlobalUserGroups(user); | ||
const {globaluserinfo} = response['query'] as { | ||
globaluserinfo: {groups: string[]}; | ||
}; | ||
|
||
if (!globaluserinfo?.groups) { | ||
continue; | ||
} | ||
|
||
const {groups} = globaluserinfo; | ||
|
||
userGroups[user] ??= []; | ||
|
||
userGroups[user] = [...userGroups[user], ...groups]; | ||
} catch (error: unknown) { | ||
console.error('[MarkRights] Ajax error:', error); | ||
} | ||
} | ||
|
||
appendUserRightsMark($userLinks, {userGroupMap, globalUserGroupMap}); | ||
} catch {} | ||
appendUserRightsMark($userLinks, userGroups); | ||
}; | ||
} | ||
|
||
void (async () => { | ||
for (const promise of promises) { | ||
try { | ||
await promise(); | ||
} catch {} | ||
} | ||
})(); | ||
}; | ||
|
||
export {markUserRights}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import type {UserRights} from '../types'; | ||
import {getMessage} from '../i18n'; | ||
import {getUserName} from './getUserName'; | ||
import {uniqueArray} from 'ext.gadget.Util'; | ||
|
||
const appendUserRightsMark = ($userLinks: JQuery<HTMLAnchorElement>[], userGroups: Record<string, string[]>): void => { | ||
for (const $element of $userLinks) { | ||
const username: string = getUserName($element.attr('href') ?? ''); | ||
if (!username) { | ||
continue; | ||
} | ||
|
||
userGroups ??= {}; | ||
const groups = userGroups[username] ?? []; | ||
|
||
let $sups: JQuery; | ||
if ($element.parents('li').find('.gadgets-markrights').length) { | ||
$sups = $element.siblings('.gadgets-markrights').eq(0); | ||
} else if ($element.siblings('.gadgets-markrights').length) { | ||
$sups = $element.parents('li').find('.gadgets-markrights').eq(0); | ||
} else { | ||
$sups = $('<span>').addClass('gadgets-markrights'); | ||
$element.after($sups); | ||
} | ||
|
||
for (const group of uniqueArray(groups)) { | ||
const className: string = `gadgets-markrights__${group}`; | ||
if ($sups.find('sup').hasClass(className)) { | ||
continue; | ||
} | ||
|
||
$sups.append( | ||
// The following classes are used here: | ||
// * see ../types.d.ts | ||
// * for more information | ||
$('<sup>') | ||
.addClass(className) | ||
.attr({ | ||
alt: getMessage(group as UserRights), | ||
title: getMessage(group as UserRights), | ||
}) | ||
); | ||
} | ||
} | ||
}; | ||
|
||
export {appendUserRightsMark}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
const generateUserLinks = ($content: JQuery): {users: string[]; $userLinks: JQuery<HTMLAnchorElement>[]} => { | ||
const users: string[] = []; | ||
const $userLinks: JQuery<HTMLAnchorElement>[] = []; | ||
const $elements = $content.find<HTMLAnchorElement>('a.mw-userlink:not(.mw-anonuserlink)'); | ||
|
||
for (const element of $elements) { | ||
const $element: JQuery<HTMLAnchorElement> = $(element); | ||
if ( | ||
$element.parents('li').find('.gadgets-markrights').length || | ||
$element.siblings('.gadgets-markrights').length | ||
) { | ||
continue; | ||
} | ||
|
||
$userLinks[$userLinks.length] = $element; | ||
|
||
const {textContent} = element; | ||
const user: string | undefined = textContent?.toString(); | ||
|
||
if (!user) { | ||
continue; | ||
} | ||
|
||
users[users.length] = user; | ||
} | ||
|
||
return {users, $userLinks}; | ||
}; | ||
|
||
export {generateUserLinks}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
const getUserName = (url: string): string => { | ||
if (!url) { | ||
return ''; | ||
} | ||
|
||
const username: string | null = mw.util.getParamValue('title', url); | ||
|
||
const decode = (string: string, replace: (_string: string) => string): string => { | ||
return decodeURIComponent( | ||
((): string => { | ||
try { | ||
return decodeURIComponent(replace(string)); | ||
} catch { | ||
return replace(string).replace(/%(?!\d+)/g, '%25'); | ||
} | ||
})() | ||
); | ||
}; | ||
|
||
if (username) { | ||
return decode(username, (string: string): string => { | ||
return string.replace('User:', '').replace(/_/g, ' '); | ||
}); | ||
} | ||
|
||
const usernameMatch: RegExpMatchArray | null = url.match(/\/wiki\/User:(.+?)$/); | ||
|
||
if (usernameMatch?.[1]) { | ||
return decode(usernameMatch[1], (string: string): string => { | ||
return string.replace(/_/g, ' '); | ||
}); | ||
} | ||
|
||
return ''; | ||
}; | ||
|
||
export {getUserName}; |