2 * User editing capabilities for the user list.
4 * @author Alexander Ebert
5 * @copyright 2001-2019 WoltLab GmbH
6 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
7 * @module WoltLabSuite/Core/Acp/Ui/User/Editor
11 import AcpUserContentRemoveHandler from "./Content/Remove/Handler";
12 import * as Ajax from "../../../Ajax";
13 import * as Core from "../../../Core";
14 import * as EventHandler from "../../../Event/Handler";
15 import * as Language from "../../../Language";
16 import * as UiNotification from "../../../Ui/Notification";
17 import UiDropdownSimple from "../../../Ui/Dropdown/Simple";
18 import { AjaxCallbackObject, DatabaseObjectActionResponse } from "../../../Ajax/Data";
19 import DomUtil from "../../../Dom/Util";
21 interface RefreshUsersData {
25 class AcpUiUserEditor {
27 * Initializes the edit dropdown for each user.
30 document.querySelectorAll(".jsUserRow").forEach((userRow: HTMLTableRowElement) => this.initUser(userRow));
32 EventHandler.add("com.woltlab.wcf.acp.user", "refresh", (data: RefreshUsersData) => this.refreshUsers(data));
36 * Initializes the edit dropdown for a user.
38 private initUser(userRow: HTMLTableRowElement): void {
39 const userId = ~~userRow.dataset.objectId!;
40 const dropdownId = `userListDropdown${userId}`;
41 const dropdownMenu = UiDropdownSimple.getDropdownMenu(dropdownId)!;
42 const legacyButtonContainer = userRow.querySelector(".jsLegacyButtons") as HTMLElement;
44 if (dropdownMenu.childElementCount === 0 && legacyButtonContainer.childElementCount === 0) {
45 const toggleButton = userRow.querySelector(".dropdownToggle") as HTMLAnchorElement;
46 toggleButton.classList.add("disabled");
51 UiDropdownSimple.registerCallback(dropdownId, (identifier, action) => {
52 if (action === "open") {
53 this.rebuild(dropdownMenu, legacyButtonContainer);
57 const editLink = dropdownMenu.querySelector(".jsEditLink") as HTMLAnchorElement;
58 if (editLink !== null) {
59 const toggleButton = userRow.querySelector(".dropdownToggle") as HTMLAnchorElement;
60 toggleButton.addEventListener("dblclick", (event) => {
61 event.preventDefault();
67 const sendNewPassword = dropdownMenu.querySelector(".jsSendNewPassword") as HTMLAnchorElement;
68 if (sendNewPassword !== null) {
69 sendNewPassword.addEventListener("click", (event) => {
70 event.preventDefault();
72 // emulate clipboard selection
73 EventHandler.fire("com.woltlab.wcf.clipboard", "com.woltlab.wcf.user", {
75 actionName: "com.woltlab.wcf.user.sendNewPassword",
77 confirmMessage: Language.get("wcf.acp.user.action.sendNewPassword.confirmMessage"),
82 actionName: "com.woltlab.wcf.user.sendNewPassword",
89 const deleteContent = dropdownMenu.querySelector(".jsDeleteContent") as HTMLAnchorElement;
90 if (deleteContent !== null) {
91 new AcpUserContentRemoveHandler(deleteContent, userId);
94 const toggleConfirmEmail = dropdownMenu.querySelector(".jsConfirmEmailToggle") as HTMLAnchorElement;
95 if (toggleConfirmEmail !== null) {
96 toggleConfirmEmail.addEventListener("click", (event) => {
97 event.preventDefault();
102 const isEmailConfirmed = Core.stringToBool(userRow.dataset.emailConfirmed!);
106 actionName: (isEmailConfirmed ? "un" : "") + "confirmEmail",
107 className: "wcf\\data\\user\\UserAction",
112 } as AjaxCallbackObject,
114 (data: DatabaseObjectActionResponse) => {
115 document.querySelectorAll(".jsUserRow").forEach((userRow: HTMLTableRowElement) => {
116 const userId = ~~userRow.dataset.objectId!;
117 if (data.objectIDs.includes(userId)) {
118 const confirmEmailButton = dropdownMenu.querySelector(".jsConfirmEmailToggle") as HTMLAnchorElement;
120 switch (data.actionName) {
122 userRow.dataset.emailConfirmed = "true";
123 confirmEmailButton.textContent = confirmEmailButton.dataset.unconfirmEmailMessage!;
126 case "unconfirmEmail":
127 userRow.dataset.emailEonfirmed = "false";
128 confirmEmailButton.textContent = confirmEmailButton.dataset.confirmEmailMessage!;
132 throw new Error("Unreachable");
137 UiNotification.show();
145 * Rebuilds the dropdown by adding wrapper links for legacy buttons,
146 * that will eventually receive the click event.
148 private rebuild(dropdownMenu: HTMLElement, legacyButtonContainer: HTMLElement): void {
149 dropdownMenu.querySelectorAll(".jsLegacyItem").forEach((element) => element.remove());
152 const items: HTMLLIElement[] = [];
153 let deleteButton: HTMLAnchorElement | null = null;
154 Array.from(legacyButtonContainer.children).forEach((button: HTMLAnchorElement) => {
155 if (button.classList.contains("jsDeleteButton")) {
156 deleteButton = button;
161 const item = document.createElement("li");
162 item.className = "jsLegacyItem";
163 item.innerHTML = '<a href="#"></a>';
165 const link = item.children[0] as HTMLAnchorElement;
166 link.textContent = button.dataset.tooltip || button.title;
167 link.addEventListener("click", (event) => {
168 event.preventDefault();
170 // forward click onto original button
171 if (button.nodeName === "A") {
174 Core.triggerEvent(button, "click");
181 items.forEach((item) => {
182 dropdownMenu.insertAdjacentElement("afterbegin", item);
185 if (deleteButton !== null) {
186 const dispatchDeleteButton = dropdownMenu.querySelector(".jsDispatchDelete") as HTMLAnchorElement;
187 dispatchDeleteButton.addEventListener("click", (event) => {
188 event.preventDefault();
190 deleteButton!.click();
194 // check if there are visible items before each divider
195 const listItems = Array.from(dropdownMenu.children) as HTMLElement[];
196 listItems.forEach((element) => DomUtil.show(element));
199 listItems.forEach((item) => {
200 if (item.classList.contains("dropdownDivider")) {
210 private refreshUsers(data: RefreshUsersData): void {
211 document.querySelectorAll(".jsUserRow").forEach((userRow: HTMLTableRowElement) => {
212 const userId = ~~userRow.dataset.objectId!;
213 if (data.userIds.includes(userId)) {
214 const userStatusIcons = userRow.querySelector(".userStatusIcons") as HTMLElement;
216 const banned = Core.stringToBool(userRow.dataset.banned!);
217 let iconBanned = userRow.querySelector(".jsUserStatusBanned") as HTMLElement;
218 if (banned && iconBanned === null) {
219 iconBanned = document.createElement("span");
220 iconBanned.className = "icon icon16 fa-lock jsUserStatusBanned jsTooltip";
221 iconBanned.title = Language.get("wcf.user.status.banned");
223 userStatusIcons.appendChild(iconBanned);
224 } else if (!banned && iconBanned !== null) {
228 const isDisabled = !Core.stringToBool(userRow.dataset.enabled!);
229 let iconIsDisabled = userRow.querySelector(".jsUserStatusIsDisabled") as HTMLElement;
230 if (isDisabled && iconIsDisabled === null) {
231 iconIsDisabled = document.createElement("span");
232 iconIsDisabled.className = "icon icon16 fa-power-off jsUserStatusIsDisabled jsTooltip";
233 iconIsDisabled.title = Language.get("wcf.user.status.isDisabled");
234 userStatusIcons.appendChild(iconIsDisabled);
235 } else if (!isDisabled && iconIsDisabled !== null) {
236 iconIsDisabled.remove();
243 export = AcpUiUserEditor;