diff --git a/app/assets/javascripts/admin/controllers/admin-user-index.js b/app/assets/javascripts/admin/controllers/admin-user-index.js index 7bd7abe341..f69993c185 100644 --- a/app/assets/javascripts/admin/controllers/admin-user-index.js +++ b/app/assets/javascripts/admin/controllers/admin-user-index.js @@ -9,6 +9,7 @@ import { popupAjaxError } from "discourse/lib/ajax-error"; import discourseComputed from "discourse-common/utils/decorators"; import { fmt } from "discourse/lib/computed"; import { htmlSafe } from "@ember/template"; +import showModal from "discourse/lib/show-modal"; export default Controller.extend(CanCheckEmails, { adminTools: service(), @@ -207,6 +208,27 @@ export default Controller.extend(CanCheckEmails, { } }, + promptTargetUser() { + showModal("admin-merge-users-prompt", { + admin: true, + model: this.model + }); + }, + + showMergeConfirmation(targetUsername) { + showModal("admin-merge-users-confirmation", { + admin: true, + model: { + username: this.model.username, + targetUsername: targetUsername + } + }); + }, + + merge(targetUsername) { + return this.model.merge({ targetUsername: targetUsername }); + }, + viewActionLogs() { this.adminTools.showActionLogs(this, { target_user: this.get("model.username") diff --git a/app/assets/javascripts/admin/controllers/modals/admin-merge-users-confirmation.js b/app/assets/javascripts/admin/controllers/modals/admin-merge-users-confirmation.js new file mode 100644 index 0000000000..2195c29631 --- /dev/null +++ b/app/assets/javascripts/admin/controllers/modals/admin-merge-users-confirmation.js @@ -0,0 +1,35 @@ +import Controller, { inject as controller } from "@ember/controller"; +import ModalFunctionality from "discourse/mixins/modal-functionality"; +import discourseComputed from "discourse-common/utils/decorators"; +import { alias } from "@ember/object/computed"; + +export default Controller.extend(ModalFunctionality, { + adminUserIndex: controller(), + username: alias("model.username"), + targetUsername: alias("model.targetUsername"), + + onShow() { + this.set("value", null); + }, + + @discourseComputed("username", "targetUsername") + text(username, targetUsername) { + return `transfer @${username} to @${targetUsername}`; + }, + + @discourseComputed("value", "text") + mergeDisabled(value, text) { + return !value || text !== value; + }, + + actions: { + merge() { + this.adminUserIndex.send("merge", this.targetUsername); + this.send("closeModal"); + }, + + cancel() { + this.send("closeModal"); + } + } +}); diff --git a/app/assets/javascripts/admin/controllers/modals/admin-merge-users-prompt.js b/app/assets/javascripts/admin/controllers/modals/admin-merge-users-prompt.js new file mode 100644 index 0000000000..535870cd6f --- /dev/null +++ b/app/assets/javascripts/admin/controllers/modals/admin-merge-users-prompt.js @@ -0,0 +1,29 @@ +import Controller, { inject as controller } from "@ember/controller"; +import ModalFunctionality from "discourse/mixins/modal-functionality"; +import discourseComputed from "discourse-common/utils/decorators"; +import { alias } from "@ember/object/computed"; + +export default Controller.extend(ModalFunctionality, { + adminUserIndex: controller(), + username: alias("model.username"), + + onShow() { + this.set("targetUsername", null); + }, + + @discourseComputed("username", "targetUsername") + mergeDisabled(username, targetUsername) { + return !targetUsername || username === targetUsername; + }, + + actions: { + merge() { + this.send("closeModal"); + this.adminUserIndex.send("showMergeConfirmation", this.targetUsername); + }, + + cancel() { + this.send("closeModal"); + } + } +}); diff --git a/app/assets/javascripts/admin/models/admin-user.js b/app/assets/javascripts/admin/models/admin-user.js index 25a822f388..454daf4b19 100644 --- a/app/assets/javascripts/admin/models/admin-user.js +++ b/app/assets/javascripts/admin/models/admin-user.js @@ -503,6 +503,43 @@ const AdminUser = User.extend({ bootbox.dialog(message, buttons, { classes: "delete-user-modal" }); }, + merge(opts) { + const user = this; + const location = document.location.pathname; + + bootbox.dialog(I18n.t("admin.user.merging_user")); + let formData = { context: location }; + + if (opts && opts.targetUsername) { + formData["target_username"] = opts.targetUsername; + } + + return ajax(`/admin/users/${user.get("id")}/merge.json`, { + type: "POST", + data: formData + }) + .then(function(data) { + if (data.merged) { + if (/^\/admin\/users\/list\//.test(location)) { + document.location = location; + } else { + document.location = Discourse.getURL( + `/admin/users/${data.user.id}/${data.user.username}` + ); + } + } else { + bootbox.alert(I18n.t("admin.user.merge_failed")); + if (data.user) { + user.setProperties(data.user); + } + } + }) + .catch(function() { + AdminUser.find(user.get("id")).then(u => user.setProperties(u)); + bootbox.alert(I18n.t("admin.user.merge_failed")); + }); + }, + loadDetails() { if (this.loadedDetails) { return Promise.resolve(this); diff --git a/app/assets/javascripts/admin/templates/modal/admin-merge-users-confirmation.hbs b/app/assets/javascripts/admin/templates/modal/admin-merge-users-confirmation.hbs new file mode 100644 index 0000000000..119cd3ad08 --- /dev/null +++ b/app/assets/javascripts/admin/templates/modal/admin-merge-users-confirmation.hbs @@ -0,0 +1,21 @@ +
{{html-safe (i18n "admin.user.merge.confirmation.description" username=username targetUsername=targetUsername text=text)}}
+ {{input type="text" value=value}} + {{/d-modal-body}} + + +{{html-safe (i18n "admin.user.merge.prompt.description" username=username)}}
+ {{user-selector single=true + placeholderKey="admin.user.merge.prompt.target_username_placeholder" + usernames=targetUsername + autocomplete="discourse"}} + {{/d-modal-body}} + + +Please choose a new owner for @%{username}'s content.
+ +All topics, posts, messages and other content created by @%{username} will be transferred.
+ + target_username_placeholder: "Username of new owner" + transfer_and_delete: "Transfer & Delete @%{username}" + cancel: "Cancel" + confirmation: + title: "Transfer & Delete @%{username}" + description: | +All of @%{username}'s content will be transferred and attributed to @%{targetUsername}. After the content is transferred, @%{username}'s account will be deleted.
+ +This can not be undone!
+ +To continue type: %{text}