Convert `Acp/Ui/User/Editor` to TypeScript
authorAlexander Ebert <ebert@woltlab.com>
Sat, 5 Dec 2020 21:51:13 +0000 (22:51 +0100)
committerAlexander Ebert <ebert@woltlab.com>
Sat, 5 Dec 2020 21:51:13 +0000 (22:51 +0100)
wcfsetup/install/files/js/WoltLabSuite/Core/Acp/Ui/User/Editor.js
wcfsetup/install/files/ts/WoltLabSuite/Core/Acp/Ui/User/Editor.js [deleted file]
wcfsetup/install/files/ts/WoltLabSuite/Core/Acp/Ui/User/Editor.ts [new file with mode: 0644]

index a7af3b7086e46d04fce36b12af95c1b63dde2d7d..6b1aa1a2cbb90e664fc64072d2829942023ac33a 100644 (file)
 /**
  * User editing capabilities for the user list.
  *
- * @author     Alexander Ebert
- * @copyright  2001-2019 WoltLab GmbH
- * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- * @module     WoltLabSuite/Core/Acp/Ui/User/Editor
+ * @author  Alexander Ebert
+ * @copyright  2001-2019 WoltLab GmbH
+ * @license  GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module  WoltLabSuite/Core/Acp/Ui/User/Editor
  * @since       3.1
  */
-define(['Ajax', 'Core', 'EventHandler', 'Language', 'Ui/Notification', 'Ui/SimpleDropdown', 'WoltLabSuite/Core/Acp/Ui/User/Content/Remove/Handler'], function (Ajax, Core, EventHandler, Language, UiNotification, UiSimpleDropdown, RemoveContentHandler) {
+define(["require", "exports", "tslib", "./Content/Remove/Handler", "../../../Ajax", "../../../Core", "../../../Event/Handler", "../../../Language", "../../../Ui/Notification", "../../../Ui/Dropdown/Simple", "../../../Dom/Util"], function (require, exports, tslib_1, Handler_1, Ajax, Core, EventHandler, Language, UiNotification, Simple_1, Util_1) {
     "use strict";
-    /**
-     * @exports     WoltLabSuite/Core/Acp/Ui/User/Editor
-     */
-    return {
+    Handler_1 = tslib_1.__importDefault(Handler_1);
+    Ajax = tslib_1.__importStar(Ajax);
+    Core = tslib_1.__importStar(Core);
+    EventHandler = tslib_1.__importStar(EventHandler);
+    Language = tslib_1.__importStar(Language);
+    UiNotification = tslib_1.__importStar(UiNotification);
+    Simple_1 = tslib_1.__importDefault(Simple_1);
+    Util_1 = tslib_1.__importDefault(Util_1);
+    class AcpUiUserEditor {
         /**
          * Initializes the edit dropdown for each user.
          */
-        init: function () {
-            elBySelAll('.jsUserRow', undefined, this._initUser.bind(this));
-            EventHandler.add('com.woltlab.wcf.acp.user', 'refresh', this._refreshUsers.bind(this));
-        },
+        constructor() {
+            document.querySelectorAll(".jsUserRow").forEach((userRow) => this.initUser(userRow));
+            EventHandler.add("com.woltlab.wcf.acp.user", "refresh", (data) => this.refreshUsers(data));
+        }
         /**
          * Initializes the edit dropdown for a user.
-         *
-         * @param       {Element}       userRow
-         * @protected
          */
-        _initUser: function (userRow) {
-            var userId = ~~elData(userRow, 'object-id');
-            var dropdownMenu = UiSimpleDropdown.getDropdownMenu('userListDropdown' + userId);
-            var legacyButtonContainer = elBySel('.jsLegacyButtons', userRow);
+        initUser(userRow) {
+            const userId = ~~userRow.dataset.objectId;
+            const dropdownId = `userListDropdown${userId}`;
+            const dropdownMenu = Simple_1.default.getDropdownMenu(dropdownId);
+            const legacyButtonContainer = userRow.querySelector(".jsLegacyButtons");
             if (dropdownMenu.childElementCount === 0 && legacyButtonContainer.childElementCount === 0) {
-                elBySel('.dropdownToggle', userRow).classList.add('disabled');
+                const toggleButton = userRow.querySelector(".dropdownToggle");
+                toggleButton.classList.add("disabled");
                 return;
             }
-            UiSimpleDropdown.registerCallback('userListDropdown' + userId, (function (identifier, action) {
-                if (action === 'open') {
-                    this._rebuild(userId, dropdownMenu, legacyButtonContainer);
+            Simple_1.default.registerCallback(dropdownId, (identifier, action) => {
+                if (action === "open") {
+                    this.rebuild(dropdownMenu, legacyButtonContainer);
                 }
-            }).bind(this));
-            var editLink = elBySel('.jsEditLink', dropdownMenu);
+            });
+            const editLink = dropdownMenu.querySelector(".jsEditLink");
             if (editLink !== null) {
-                elBySel('.dropdownToggle', userRow).addEventListener('dblclick', function (event) {
+                const toggleButton = userRow.querySelector(".dropdownToggle");
+                toggleButton.addEventListener("dblclick", (event) => {
                     event.preventDefault();
                     editLink.click();
                 });
             }
-            var sendNewPassword = elBySel('.jsSendNewPassword', dropdownMenu);
+            const sendNewPassword = dropdownMenu.querySelector(".jsSendNewPassword");
             if (sendNewPassword !== null) {
-                sendNewPassword.addEventListener('click', function (event) {
+                sendNewPassword.addEventListener("click", (event) => {
                     event.preventDefault();
                     // emulate clipboard selection
-                    EventHandler.fire('com.woltlab.wcf.clipboard', 'com.woltlab.wcf.user', {
+                    EventHandler.fire("com.woltlab.wcf.clipboard", "com.woltlab.wcf.user", {
                         data: {
-                            actionName: 'com.woltlab.wcf.user.sendNewPassword',
+                            actionName: "com.woltlab.wcf.user.sendNewPassword",
                             parameters: {
-                                confirmMessage: Language.get('wcf.acp.user.action.sendNewPassword.confirmMessage'),
-                                objectIDs: [userId]
-                            }
+                                confirmMessage: Language.get("wcf.acp.user.action.sendNewPassword.confirmMessage"),
+                                objectIDs: [userId],
+                            },
                         },
                         responseData: {
-                            actionName: 'com.woltlab.wcf.user.sendNewPassword',
-                            objectIDs: [userId]
-                        }
+                            actionName: "com.woltlab.wcf.user.sendNewPassword",
+                            objectIDs: [userId],
+                        },
                     });
                 });
             }
-            var deleteContent = elBySel('.jsDeleteContent', dropdownMenu);
+            const deleteContent = dropdownMenu.querySelector(".jsDeleteContent");
             if (deleteContent !== null) {
-                new RemoveContentHandler(deleteContent, userId);
+                new Handler_1.default(deleteContent, userId);
             }
-            var toggleConfirmEmail = elBySel('.jsConfirmEmailToggle', dropdownMenu);
+            const toggleConfirmEmail = dropdownMenu.querySelector(".jsConfirmEmailToggle");
             if (toggleConfirmEmail !== null) {
-                toggleConfirmEmail.addEventListener('click', function (event) {
+                toggleConfirmEmail.addEventListener("click", (event) => {
                     event.preventDefault();
                     Ajax.api({
-                        _ajaxSetup: function () {
+                        _ajaxSetup: () => {
+                            const isEmailConfirmed = Core.stringToBool(userRow.dataset.emailConfirmed);
                             return {
                                 data: {
-                                    actionName: (elDataBool(userRow, 'email-confirmed') ? 'un' : '') + 'confirmEmail',
-                                    className: 'wcf\\data\\user\\UserAction',
-                                    objectIDs: [userId]
-                                }
+                                    actionName: (isEmailConfirmed ? "un" : "") + "confirmEmail",
+                                    className: "wcf\\data\\user\\UserAction",
+                                    objectIDs: [userId],
+                                },
                             };
-                        }
-                    }, null, function (data) {
-                        elBySelAll('.jsUserRow', undefined, function (userRow) {
-                            var userId = parseInt(elData(userRow, 'object-id'));
-                            if (data.objectIDs.indexOf(userId) !== -1) {
-                                var confirmEmailButton = elBySel('.jsConfirmEmailToggle', UiSimpleDropdown.getDropdownMenu('userListDropdown' + userId));
+                        },
+                    }, undefined, (data) => {
+                        document.querySelectorAll(".jsUserRow").forEach((userRow) => {
+                            const userId = ~~userRow.dataset.objectId;
+                            if (data.objectIDs.includes(userId)) {
+                                const confirmEmailButton = dropdownMenu.querySelector(".jsConfirmEmailToggle");
                                 switch (data.actionName) {
-                                    case 'confirmEmail':
-                                        elData(userRow, 'email-confirmed', 'true');
-                                        confirmEmailButton.textContent = elData(confirmEmailButton, 'unconfirm-email-message');
+                                    case "confirmEmail":
+                                        userRow.dataset.emailConfirmed = "true";
+                                        confirmEmailButton.textContent = confirmEmailButton.dataset.unconfirmEmailMessage;
                                         break;
-                                    case 'unconfirmEmail':
-                                        elData(userRow, 'email-confirmed', 'false');
-                                        confirmEmailButton.textContent = elData(confirmEmailButton, 'confirm-email-message');
+                                    case "unconfirmEmail":
+                                        userRow.dataset.emailEonfirmed = "false";
+                                        confirmEmailButton.textContent = confirmEmailButton.dataset.confirmEmailMessage;
                                         break;
                                     default:
                                         throw new Error("Unreachable");
@@ -107,99 +113,93 @@ define(['Ajax', 'Core', 'EventHandler', 'Language', 'Ui/Notification', 'Ui/Simpl
                     });
                 });
             }
-        },
+        }
         /**
          * Rebuilds the dropdown by adding wrapper links for legacy buttons,
          * that will eventually receive the click event.
-         *
-         * @param       {int}           userId
-         * @param       {Element}       dropdownMenu
-         * @param       {Element}       legacyButtonContainer
-         * @protected
          */
-        _rebuild: function (userId, dropdownMenu, legacyButtonContainer) {
-            elBySelAll('.jsLegacyItem', dropdownMenu, elRemove);
+        rebuild(dropdownMenu, legacyButtonContainer) {
+            dropdownMenu.querySelectorAll(".jsLegacyItem").forEach((element) => element.remove());
             // inject buttons
-            var button, item, link;
-            var items = [];
-            var deleteButton = null;
-            for (var i = 0, length = legacyButtonContainer.childElementCount; i < length; i++) {
-                button = legacyButtonContainer.children[i];
-                if (button.classList.contains('jsDeleteButton')) {
+            const items = [];
+            let deleteButton = null;
+            Array.from(legacyButtonContainer.children).forEach((button) => {
+                if (button.classList.contains("jsDeleteButton")) {
                     deleteButton = button;
-                    continue;
+                    return;
                 }
-                item = elCreate('li');
-                item.className = 'jsLegacyItem';
+                const item = document.createElement("li");
+                item.className = "jsLegacyItem";
                 item.innerHTML = '<a href="#"></a>';
-                link = item.children[0];
-                link.textContent = elData(button, 'tooltip') || button.title;
-                (function (button) {
-                    link.addEventListener('click', function (event) {
-                        event.preventDefault();
-                        // forward click onto original button
-                        if (button.nodeName === 'A')
-                            button.click();
-                        else
-                            Core.triggerEvent(button, 'click');
-                    });
-                })(button);
+                const link = item.children[0];
+                link.textContent = button.dataset.tooltip || button.title;
+                link.addEventListener("click", (event) => {
+                    event.preventDefault();
+                    // forward click onto original button
+                    if (button.nodeName === "A") {
+                        button.click();
+                    }
+                    else {
+                        Core.triggerEvent(button, "click");
+                    }
+                });
                 items.push(item);
-            }
-            while (items.length) {
-                dropdownMenu.insertBefore(items.pop(), dropdownMenu.firstElementChild);
-            }
+            });
+            items.forEach((item) => {
+                dropdownMenu.insertAdjacentElement("afterbegin", item);
+            });
             if (deleteButton !== null) {
-                elBySel('.jsDispatchDelete', dropdownMenu).addEventListener('click', function (event) {
+                const dispatchDeleteButton = dropdownMenu.querySelector(".jsDispatchDelete");
+                dispatchDeleteButton.addEventListener("click", (event) => {
                     event.preventDefault();
-                    Core.triggerEvent(deleteButton, 'click');
+                    deleteButton.click();
                 });
             }
             // check if there are visible items before each divider
-            for (i = 0, length = dropdownMenu.childElementCount; i < length; i++) {
-                elShow(dropdownMenu.children[i]);
-            }
-            var hasItem = false;
-            for (i = 0, length = dropdownMenu.childElementCount; i < length; i++) {
-                item = dropdownMenu.children[i];
-                if (item.classList.contains('dropdownDivider')) {
-                    if (!hasItem)
-                        elHide(item);
+            const listItems = Array.from(dropdownMenu.children);
+            listItems.forEach((element) => Util_1.default.show(element));
+            let hasItem = false;
+            listItems.forEach((item) => {
+                if (item.classList.contains("dropdownDivider")) {
+                    if (!hasItem) {
+                        Util_1.default.hide(item);
+                    }
                 }
                 else {
                     hasItem = true;
                 }
-            }
-        },
-        _refreshUsers: function (data) {
-            elBySelAll('.jsUserRow', undefined, function (userRow) {
-                var userId = parseInt(elData(userRow, 'object-id'));
-                if (data.userIds.indexOf(userId) !== -1) {
-                    var userStatusIcons = elBySel('.userStatusIcons', userRow);
-                    var banned = elDataBool(userRow, 'banned');
-                    var iconBanned = elBySel('.jsUserStatusBanned', userRow);
+            });
+        }
+        refreshUsers(data) {
+            document.querySelectorAll(".jsUserRow").forEach((userRow) => {
+                const userId = ~~userRow.dataset.objectId;
+                if (data.userIds.includes(userId)) {
+                    const userStatusIcons = userRow.querySelector(".userStatusIcons");
+                    const banned = Core.stringToBool(userRow.dataset.banned);
+                    let iconBanned = userRow.querySelector(".jsUserStatusBanned");
                     if (banned && iconBanned === null) {
-                        iconBanned = elCreate('span');
-                        iconBanned.className = 'icon icon16 fa-lock jsUserStatusBanned jsTooltip';
-                        iconBanned.title = Language.get('wcf.user.status.banned');
-                        userStatusIcons.insertBefore(iconBanned, null);
+                        iconBanned = document.createElement("span");
+                        iconBanned.className = "icon icon16 fa-lock jsUserStatusBanned jsTooltip";
+                        iconBanned.title = Language.get("wcf.user.status.banned");
+                        userStatusIcons.appendChild(iconBanned);
                     }
                     else if (!banned && iconBanned !== null) {
-                        elRemove(iconBanned);
+                        iconBanned.remove();
                     }
-                    var isDisabled = elDataBool(userRow, 'enabled') === false;
-                    var iconIsDisabled = elBySel('.jsUserStatusIsDisabled', userRow);
+                    const isDisabled = !Core.stringToBool(userRow.dataset.enabled);
+                    let iconIsDisabled = userRow.querySelector(".jsUserStatusIsDisabled");
                     if (isDisabled && iconIsDisabled === null) {
-                        iconIsDisabled = elCreate('span');
-                        iconIsDisabled.className = 'icon icon16 fa-power-off jsUserStatusIsDisabled jsTooltip';
-                        iconIsDisabled.title = Language.get('wcf.user.status.isDisabled');
+                        iconIsDisabled = document.createElement("span");
+                        iconIsDisabled.className = "icon icon16 fa-power-off jsUserStatusIsDisabled jsTooltip";
+                        iconIsDisabled.title = Language.get("wcf.user.status.isDisabled");
                         userStatusIcons.appendChild(iconIsDisabled);
                     }
                     else if (!isDisabled && iconIsDisabled !== null) {
-                        elRemove(iconIsDisabled);
+                        iconIsDisabled.remove();
                     }
                 }
             });
         }
-    };
+    }
+    return AcpUiUserEditor;
 });
diff --git a/wcfsetup/install/files/ts/WoltLabSuite/Core/Acp/Ui/User/Editor.js b/wcfsetup/install/files/ts/WoltLabSuite/Core/Acp/Ui/User/Editor.js
deleted file mode 100644 (file)
index 7740d60..0000000
+++ /dev/null
@@ -1,232 +0,0 @@
-/**
- * User editing capabilities for the user list.
- * 
- * @author     Alexander Ebert
- * @copyright  2001-2019 WoltLab GmbH
- * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- * @module     WoltLabSuite/Core/Acp/Ui/User/Editor
- * @since       3.1
- */
-define(['Ajax', 'Core', 'EventHandler', 'Language', 'Ui/Notification', 'Ui/SimpleDropdown', 'WoltLabSuite/Core/Acp/Ui/User/Content/Remove/Handler'], function(Ajax, Core, EventHandler, Language, UiNotification, UiSimpleDropdown, RemoveContentHandler) {
-       "use strict";
-       
-       /**
-        * @exports     WoltLabSuite/Core/Acp/Ui/User/Editor
-        */
-       return {
-               /**
-                * Initializes the edit dropdown for each user.
-                */
-               init: function () {
-                       elBySelAll('.jsUserRow', undefined, this._initUser.bind(this));
-                       
-                       EventHandler.add('com.woltlab.wcf.acp.user', 'refresh', this._refreshUsers.bind(this));
-               },
-               
-               /**
-                * Initializes the edit dropdown for a user.
-                * 
-                * @param       {Element}       userRow
-                * @protected
-                */
-               _initUser: function (userRow) {
-                       var userId = ~~elData(userRow, 'object-id');
-                       var dropdownMenu = UiSimpleDropdown.getDropdownMenu('userListDropdown' + userId);
-                       var legacyButtonContainer = elBySel('.jsLegacyButtons', userRow);
-                       
-                       if (dropdownMenu.childElementCount === 0 && legacyButtonContainer.childElementCount === 0) {
-                               elBySel('.dropdownToggle', userRow).classList.add('disabled');
-                               return;
-                       }
-                       
-                       UiSimpleDropdown.registerCallback('userListDropdown' + userId, (function (identifier, action) {
-                               if (action === 'open') {
-                                       this._rebuild(userId, dropdownMenu, legacyButtonContainer);
-                               }
-                       }).bind(this));
-                       
-                       var editLink = elBySel('.jsEditLink', dropdownMenu);
-                       if (editLink !== null) {
-                               elBySel('.dropdownToggle', userRow).addEventListener('dblclick', function (event) {
-                                       event.preventDefault();
-                                       
-                                       editLink.click();
-                               });
-                       }
-                       
-                       var sendNewPassword = elBySel('.jsSendNewPassword', dropdownMenu);
-                       if (sendNewPassword !== null) {
-                               sendNewPassword.addEventListener('click', function (event) {
-                                       event.preventDefault();
-                                       
-                                       // emulate clipboard selection
-                                       EventHandler.fire('com.woltlab.wcf.clipboard', 'com.woltlab.wcf.user', {
-                                               data: {
-                                                       actionName: 'com.woltlab.wcf.user.sendNewPassword',
-                                                       parameters: {
-                                                               confirmMessage: Language.get('wcf.acp.user.action.sendNewPassword.confirmMessage'),
-                                                               objectIDs: [userId]
-                                                       }
-                                               },
-                                               responseData: {
-                                                       actionName: 'com.woltlab.wcf.user.sendNewPassword',
-                                                       objectIDs: [userId]
-                                               }
-                                       });
-                               });
-                       }
-                       
-                       var deleteContent = elBySel('.jsDeleteContent', dropdownMenu);
-                       if (deleteContent !== null) {
-                               new RemoveContentHandler(deleteContent, userId);
-                       }
-                       
-                       var toggleConfirmEmail = elBySel('.jsConfirmEmailToggle', dropdownMenu);
-                       if (toggleConfirmEmail !== null) {
-                               toggleConfirmEmail.addEventListener('click', function (event) {
-                                       event.preventDefault();
-                                       
-                                       Ajax.api({
-                                               _ajaxSetup: function () {
-                                                       return {
-                                                               data: {
-                                                                       actionName: (elDataBool(userRow, 'email-confirmed') ? 'un' : '') + 'confirmEmail',
-                                                                       className: 'wcf\\data\\user\\UserAction',
-                                                                       objectIDs: [userId]
-                                                               }
-                                                       };
-                                               }
-                                       }, null, function (data) {
-                                               elBySelAll('.jsUserRow', undefined, function(userRow) {
-                                                       var userId = parseInt(elData(userRow, 'object-id'));
-                                                       if (data.objectIDs.indexOf(userId) !== -1) {
-                                                               var confirmEmailButton = elBySel('.jsConfirmEmailToggle', UiSimpleDropdown.getDropdownMenu('userListDropdown' + userId));
-                                                               
-                                                               switch (data.actionName) {
-                                                                       case 'confirmEmail':
-                                                                               elData(userRow, 'email-confirmed', 'true');
-                                                                               confirmEmailButton.textContent = elData(confirmEmailButton, 'unconfirm-email-message');
-                                                                               break;
-                                                                       
-                                                                       case 'unconfirmEmail':
-                                                                               elData(userRow, 'email-confirmed', 'false');
-                                                                               confirmEmailButton.textContent = elData(confirmEmailButton, 'confirm-email-message');
-                                                                               break;
-                                                                               
-                                                                       default: 
-                                                                               throw new Error("Unreachable");
-                                                               }
-                                                       }
-                                               });
-                                               
-                                               UiNotification.show();
-                                       });
-                               });
-                       }
-               },
-               
-               /**
-                * Rebuilds the dropdown by adding wrapper links for legacy buttons,
-                * that will eventually receive the click event.
-                * 
-                * @param       {int}           userId
-                * @param       {Element}       dropdownMenu
-                * @param       {Element}       legacyButtonContainer
-                * @protected
-                */
-               _rebuild: function (userId, dropdownMenu, legacyButtonContainer) {
-                       elBySelAll('.jsLegacyItem', dropdownMenu, elRemove);
-                       
-                       // inject buttons
-                       var button, item, link;
-                       var items = [];
-                       var deleteButton = null;
-                       for (var i = 0, length = legacyButtonContainer.childElementCount; i < length; i++) {
-                               button = legacyButtonContainer.children[i];
-                               if (button.classList.contains('jsDeleteButton')) {
-                                       deleteButton = button;
-                                       continue;
-                               }
-                               
-                               item = elCreate('li');
-                               item.className = 'jsLegacyItem';
-                               item.innerHTML = '<a href="#"></a>';
-                               
-                               link = item.children[0];
-                               link.textContent = elData(button, 'tooltip') || button.title;
-                               (function(button) {
-                                       link.addEventListener('click', function (event) {
-                                               event.preventDefault();
-                                               
-                                               // forward click onto original button
-                                               if (button.nodeName === 'A') button.click();
-                                               else Core.triggerEvent(button, 'click');
-                                       });
-                               })(button);
-                               
-                               items.push(item);
-                       }
-                       
-                       while (items.length) {
-                               dropdownMenu.insertBefore(items.pop(), dropdownMenu.firstElementChild);
-                       }
-                       
-                       if (deleteButton !== null) {
-                               elBySel('.jsDispatchDelete', dropdownMenu).addEventListener('click', function (event) {
-                                       event.preventDefault();
-                                       
-                                       Core.triggerEvent(deleteButton, 'click');
-                               });
-                       }
-                       
-                       // check if there are visible items before each divider
-                       for (i = 0, length = dropdownMenu.childElementCount; i < length; i++) {
-                               elShow(dropdownMenu.children[i]);
-                       }
-                       
-                       var hasItem = false;
-                       for (i = 0, length = dropdownMenu.childElementCount; i < length; i++) {
-                               item = dropdownMenu.children[i];
-                               if (item.classList.contains('dropdownDivider')) {
-                                       if (!hasItem) elHide(item);
-                               }
-                               else {
-                                       hasItem = true;
-                               }
-                       }
-               },
-               
-               _refreshUsers: function (data) {
-                       elBySelAll('.jsUserRow', undefined, function(userRow) {
-                               var userId = parseInt(elData(userRow, 'object-id'));
-                               if (data.userIds.indexOf(userId) !== -1) {
-                                       var userStatusIcons = elBySel('.userStatusIcons', userRow);
-                                       
-                                       var banned = elDataBool(userRow, 'banned');
-                                       var iconBanned = elBySel('.jsUserStatusBanned', userRow);
-                                       if (banned && iconBanned === null) {
-                                               iconBanned = elCreate('span');
-                                               iconBanned.className = 'icon icon16 fa-lock jsUserStatusBanned jsTooltip';
-                                               iconBanned.title = Language.get('wcf.user.status.banned');
-                                               userStatusIcons.insertBefore(iconBanned, null);
-                                       }
-                                       else if (!banned && iconBanned !== null) {
-                                               elRemove(iconBanned);
-                                       }
-                                       
-                                       var isDisabled = elDataBool(userRow, 'enabled') === false;
-                                       var iconIsDisabled = elBySel('.jsUserStatusIsDisabled', userRow);
-                                       if (isDisabled && iconIsDisabled === null) {
-                                               iconIsDisabled = elCreate('span');
-                                               iconIsDisabled.className = 'icon icon16 fa-power-off jsUserStatusIsDisabled jsTooltip';
-                                               iconIsDisabled.title = Language.get('wcf.user.status.isDisabled');
-                                               userStatusIcons.appendChild(iconIsDisabled);
-                                       }
-                                       else if (!isDisabled && iconIsDisabled !== null) {
-                                               elRemove(iconIsDisabled);
-                                       }
-                               }
-                       })
-               }
-       };
-});
diff --git a/wcfsetup/install/files/ts/WoltLabSuite/Core/Acp/Ui/User/Editor.ts b/wcfsetup/install/files/ts/WoltLabSuite/Core/Acp/Ui/User/Editor.ts
new file mode 100644 (file)
index 0000000..e5c0d20
--- /dev/null
@@ -0,0 +1,243 @@
+/**
+ * User editing capabilities for the user list.
+ *
+ * @author  Alexander Ebert
+ * @copyright  2001-2019 WoltLab GmbH
+ * @license  GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module  WoltLabSuite/Core/Acp/Ui/User/Editor
+ * @since       3.1
+ */
+
+import AcpUserContentRemoveHandler from "./Content/Remove/Handler";
+import * as Ajax from "../../../Ajax";
+import * as Core from "../../../Core";
+import * as EventHandler from "../../../Event/Handler";
+import * as Language from "../../../Language";
+import * as UiNotification from "../../../Ui/Notification";
+import UiDropdownSimple from "../../../Ui/Dropdown/Simple";
+import { AjaxCallbackObject, DatabaseObjectActionResponse } from "../../../Ajax/Data";
+import DomUtil from "../../../Dom/Util";
+
+interface RefreshUsersData {
+  userIds: number[];
+}
+
+class AcpUiUserEditor {
+  /**
+   * Initializes the edit dropdown for each user.
+   */
+  constructor() {
+    document.querySelectorAll(".jsUserRow").forEach((userRow: HTMLTableRowElement) => this.initUser(userRow));
+
+    EventHandler.add("com.woltlab.wcf.acp.user", "refresh", (data: RefreshUsersData) => this.refreshUsers(data));
+  }
+
+  /**
+   * Initializes the edit dropdown for a user.
+   */
+  private initUser(userRow: HTMLTableRowElement): void {
+    const userId = ~~userRow.dataset.objectId!;
+    const dropdownId = `userListDropdown${userId}`;
+    const dropdownMenu = UiDropdownSimple.getDropdownMenu(dropdownId)!;
+    const legacyButtonContainer = userRow.querySelector(".jsLegacyButtons") as HTMLElement;
+
+    if (dropdownMenu.childElementCount === 0 && legacyButtonContainer.childElementCount === 0) {
+      const toggleButton = userRow.querySelector(".dropdownToggle") as HTMLAnchorElement;
+      toggleButton.classList.add("disabled");
+
+      return;
+    }
+
+    UiDropdownSimple.registerCallback(dropdownId, (identifier, action) => {
+      if (action === "open") {
+        this.rebuild(dropdownMenu, legacyButtonContainer);
+      }
+    });
+
+    const editLink = dropdownMenu.querySelector(".jsEditLink") as HTMLAnchorElement;
+    if (editLink !== null) {
+      const toggleButton = userRow.querySelector(".dropdownToggle") as HTMLAnchorElement;
+      toggleButton.addEventListener("dblclick", (event) => {
+        event.preventDefault();
+
+        editLink.click();
+      });
+    }
+
+    const sendNewPassword = dropdownMenu.querySelector(".jsSendNewPassword") as HTMLAnchorElement;
+    if (sendNewPassword !== null) {
+      sendNewPassword.addEventListener("click", (event) => {
+        event.preventDefault();
+
+        // emulate clipboard selection
+        EventHandler.fire("com.woltlab.wcf.clipboard", "com.woltlab.wcf.user", {
+          data: {
+            actionName: "com.woltlab.wcf.user.sendNewPassword",
+            parameters: {
+              confirmMessage: Language.get("wcf.acp.user.action.sendNewPassword.confirmMessage"),
+              objectIDs: [userId],
+            },
+          },
+          responseData: {
+            actionName: "com.woltlab.wcf.user.sendNewPassword",
+            objectIDs: [userId],
+          },
+        });
+      });
+    }
+
+    const deleteContent = dropdownMenu.querySelector(".jsDeleteContent") as HTMLAnchorElement;
+    if (deleteContent !== null) {
+      new AcpUserContentRemoveHandler(deleteContent, userId);
+    }
+
+    const toggleConfirmEmail = dropdownMenu.querySelector(".jsConfirmEmailToggle") as HTMLAnchorElement;
+    if (toggleConfirmEmail !== null) {
+      toggleConfirmEmail.addEventListener("click", (event) => {
+        event.preventDefault();
+
+        Ajax.api(
+          {
+            _ajaxSetup: () => {
+              const isEmailConfirmed = Core.stringToBool(userRow.dataset.emailConfirmed!);
+
+              return {
+                data: {
+                  actionName: (isEmailConfirmed ? "un" : "") + "confirmEmail",
+                  className: "wcf\\data\\user\\UserAction",
+                  objectIDs: [userId],
+                },
+              };
+            },
+          } as AjaxCallbackObject,
+          undefined,
+          (data: DatabaseObjectActionResponse) => {
+            document.querySelectorAll(".jsUserRow").forEach((userRow: HTMLTableRowElement) => {
+              const userId = ~~userRow.dataset.objectId!;
+              if (data.objectIDs.includes(userId)) {
+                const confirmEmailButton = dropdownMenu.querySelector(".jsConfirmEmailToggle") as HTMLAnchorElement;
+
+                switch (data.actionName) {
+                  case "confirmEmail":
+                    userRow.dataset.emailConfirmed = "true";
+                    confirmEmailButton.textContent = confirmEmailButton.dataset.unconfirmEmailMessage!;
+                    break;
+
+                  case "unconfirmEmail":
+                    userRow.dataset.emailEonfirmed = "false";
+                    confirmEmailButton.textContent = confirmEmailButton.dataset.confirmEmailMessage!;
+                    break;
+
+                  default:
+                    throw new Error("Unreachable");
+                }
+              }
+            });
+
+            UiNotification.show();
+          },
+        );
+      });
+    }
+  }
+
+  /**
+   * Rebuilds the dropdown by adding wrapper links for legacy buttons,
+   * that will eventually receive the click event.
+   */
+  private rebuild(dropdownMenu: HTMLElement, legacyButtonContainer: HTMLElement): void {
+    dropdownMenu.querySelectorAll(".jsLegacyItem").forEach((element) => element.remove());
+
+    // inject buttons
+    const items: HTMLLIElement[] = [];
+    let deleteButton: HTMLAnchorElement | null = null;
+    Array.from(legacyButtonContainer.children).forEach((button: HTMLAnchorElement) => {
+      if (button.classList.contains("jsDeleteButton")) {
+        deleteButton = button;
+
+        return;
+      }
+
+      const item = document.createElement("li");
+      item.className = "jsLegacyItem";
+      item.innerHTML = '<a href="#"></a>';
+
+      const link = item.children[0] as HTMLAnchorElement;
+      link.textContent = button.dataset.tooltip || button.title;
+      link.addEventListener("click", (event) => {
+        event.preventDefault();
+
+        // forward click onto original button
+        if (button.nodeName === "A") {
+          button.click();
+        } else {
+          Core.triggerEvent(button, "click");
+        }
+      });
+
+      items.push(item);
+    });
+
+    items.forEach((item) => {
+      dropdownMenu.insertAdjacentElement("afterbegin", item);
+    });
+
+    if (deleteButton !== null) {
+      const dispatchDeleteButton = dropdownMenu.querySelector(".jsDispatchDelete") as HTMLAnchorElement;
+      dispatchDeleteButton.addEventListener("click", (event) => {
+        event.preventDefault();
+
+        deleteButton!.click();
+      });
+    }
+
+    // check if there are visible items before each divider
+    const listItems = Array.from(dropdownMenu.children) as HTMLElement[];
+    listItems.forEach((element) => DomUtil.show(element));
+
+    let hasItem = false;
+    listItems.forEach((item) => {
+      if (item.classList.contains("dropdownDivider")) {
+        if (!hasItem) {
+          DomUtil.hide(item);
+        }
+      } else {
+        hasItem = true;
+      }
+    });
+  }
+
+  private refreshUsers(data: RefreshUsersData): void {
+    document.querySelectorAll(".jsUserRow").forEach((userRow: HTMLTableRowElement) => {
+      const userId = ~~userRow.dataset.objectId!;
+      if (data.userIds.includes(userId)) {
+        const userStatusIcons = userRow.querySelector(".userStatusIcons") as HTMLElement;
+
+        const banned = Core.stringToBool(userRow.dataset.banned!);
+        let iconBanned = userRow.querySelector(".jsUserStatusBanned") as HTMLElement;
+        if (banned && iconBanned === null) {
+          iconBanned = document.createElement("span");
+          iconBanned.className = "icon icon16 fa-lock jsUserStatusBanned jsTooltip";
+          iconBanned.title = Language.get("wcf.user.status.banned");
+
+          userStatusIcons.appendChild(iconBanned);
+        } else if (!banned && iconBanned !== null) {
+          iconBanned.remove();
+        }
+
+        const isDisabled = !Core.stringToBool(userRow.dataset.enabled!);
+        let iconIsDisabled = userRow.querySelector(".jsUserStatusIsDisabled") as HTMLElement;
+        if (isDisabled && iconIsDisabled === null) {
+          iconIsDisabled = document.createElement("span");
+          iconIsDisabled.className = "icon icon16 fa-power-off jsUserStatusIsDisabled jsTooltip";
+          iconIsDisabled.title = Language.get("wcf.user.status.isDisabled");
+          userStatusIcons.appendChild(iconIsDisabled);
+        } else if (!isDisabled && iconIsDisabled !== null) {
+          iconIsDisabled.remove();
+        }
+      }
+    });
+  }
+}
+
+export = AcpUiUserEditor;