UX: separate custom from automatic groups in user admin

REFACTOR: some moar ES6 refactoring
This commit is contained in:
Régis Hanol
2015-03-17 22:59:05 +01:00
parent 56e01a766b
commit 9cbd0f8e78
5 changed files with 349 additions and 355 deletions

View File

@ -1,31 +1,36 @@
export default Ember.Component.extend({ export default Ember.Component.extend({
tagName: 'div', tagName: 'div',
didInsertElement: function(){ _init: function(){
this.$("input").select2({ this.$("input").select2({
multiple: true, multiple: true,
width: '100%', width: '100%',
query: function(opts){ query: function(opts) {
opts.callback({ opts.callback({ results: this.get("available").map(this._format) });
results: this.get("available").map(this._format)
});
}.bind(this) }.bind(this)
}).on("change", function(evt) { }).on("change", function(evt) {
if (evt.added){ if (evt.added){
this.triggerAction({action: "groupAdded", this.triggerAction({
actionContext: this.get("available" action: "groupAdded",
).findBy("id", evt.added.id)}); actionContext: this.get("available").findBy("id", evt.added.id)
});
} else if (evt.removed) { } else if (evt.removed) {
this.triggerAction({action:"groupRemoved", this.triggerAction({
actionContext: this.get("selected" action:"groupRemoved",
).findBy("id", evt.removed.id)}); actionContext: evt.removed.id
});
} }
}.bind(this)); }.bind(this));
this._refreshOnReset();
},
_format: function(item){ this._refreshOnReset();
return {"text": item.name, "id": item.id, "locked": item.automatic}; }.on("didInsertElement"),
_format(item) {
return {
"text": item.name,
"id": item.id,
"locked": item.automatic
};
}, },
_refreshOnReset: function() { _refreshOnReset: function() {

View File

@ -11,58 +11,61 @@ export default ObjectController.extend(CanCheckEmails, {
primaryGroupDirty: Discourse.computed.propertyNotEqual('originalPrimaryGroupId', 'primary_group_id'), primaryGroupDirty: Discourse.computed.propertyNotEqual('originalPrimaryGroupId', 'primary_group_id'),
custom_groups: Ember.computed.filter("model.groups", function(g){ automaticGroups: function() {
return (!g.automatic && g.visible); return this.get("model.automaticGroups").map((g) => g.name).join(", ");
}), }.property("model.automaticGroups"),
userFields: function() { userFields: function() {
var siteUserFields = this.site.get('user_fields'), const siteUserFields = this.site.get('user_fields'),
userFields = this.get('user_fields'); userFields = this.get('user_fields');
if (!Ember.isEmpty(siteUserFields)) { if (!Ember.isEmpty(siteUserFields)) {
return siteUserFields.map(function(uf) { return siteUserFields.map(function(uf) {
var value = userFields ? userFields[uf.get('id').toString()] : null; let value = userFields ? userFields[uf.get('id').toString()] : null;
return {name: uf.get('name'), value: value}; return { name: uf.get('name'), value: value };
}); });
} }
return []; return [];
}.property('user_fields.@each'), }.property('user_fields.@each'),
actions: { actions: {
toggleTitleEdit: function() { toggleTitleEdit() {
this.toggleProperty('editingTitle'); this.toggleProperty('editingTitle');
}, },
saveTitle: function() { saveTitle() {
Discourse.ajax("/users/" + this.get('username').toLowerCase(), { const self = this;
return Discourse.ajax("/users/" + this.get('username').toLowerCase(), {
data: {title: this.get('title')}, data: {title: this.get('title')},
type: 'PUT' type: 'PUT'
}).then(null, function(e){ }).catch(function(e) {
bootbox.alert(I18n.t("generic_error_with_reason", {error: "http: " + e.status + " - " + e.body})); bootbox.alert(I18n.t("generic_error_with_reason", {error: "http: " + e.status + " - " + e.body}));
}).finally(function() {
self.send('toggleTitleEdit');
}); });
this.send('toggleTitleEdit');
}, },
generateApiKey: function() { generateApiKey() {
this.get('model').generateApiKey(); this.get('model').generateApiKey();
}, },
groupAdded: function(added){ groupAdded(added) {
this.get('model').groupAdded(added).catch(function() { this.get('model').groupAdded(added).catch(function() {
bootbox.alert(I18n.t('generic_error')); bootbox.alert(I18n.t('generic_error'));
}); });
}, },
groupRemoved: function(removed){ groupRemoved(groupId) {
this.get('model').groupRemoved(removed).catch(function() { this.get('model').groupRemoved(groupId).catch(function() {
bootbox.alert(I18n.t('generic_error')); bootbox.alert(I18n.t('generic_error'));
}); });
}, },
savePrimaryGroup: function() { savePrimaryGroup() {
var self = this; const self = this;
Discourse.ajax("/admin/users/" + this.get('id') + "/primary_group", {
return Discourse.ajax("/admin/users/" + this.get('id') + "/primary_group", {
type: 'PUT', type: 'PUT',
data: {primary_group_id: this.get('primary_group_id')} data: {primary_group_id: this.get('primary_group_id')}
}).then(function () { }).then(function () {
@ -72,33 +75,41 @@ export default ObjectController.extend(CanCheckEmails, {
}); });
}, },
resetPrimaryGroup: function() { resetPrimaryGroup() {
this.set('primary_group_id', this.get('originalPrimaryGroupId')); this.set('primary_group_id', this.get('originalPrimaryGroupId'));
}, },
regenerateApiKey: function() { regenerateApiKey() {
var self = this; const self = this;
bootbox.confirm(I18n.t("admin.api.confirm_regen"), I18n.t("no_value"), I18n.t("yes_value"), function(result) {
if (result) { bootbox.confirm(
self.get('model').generateApiKey(); I18n.t("admin.api.confirm_regen"),
I18n.t("no_value"),
I18n.t("yes_value"),
function(result) {
if (result) { self.get('model').generateApiKey(); }
} }
}); );
}, },
revokeApiKey: function() { revokeApiKey() {
var self = this; const self = this;
bootbox.confirm(I18n.t("admin.api.confirm_revoke"), I18n.t("no_value"), I18n.t("yes_value"), function(result) {
if (result) { bootbox.confirm(
self.get('model').revokeApiKey(); I18n.t("admin.api.confirm_revoke"),
I18n.t("no_value"),
I18n.t("yes_value"),
function(result) {
if (result) { self.get('model').revokeApiKey(); }
} }
}); );
}, },
anonymize: function() { anonymize() {
this.get('model').anonymize(); this.get('model').anonymize();
}, },
destroy: function() { destroy() {
this.get('model').destroy(); this.get('model').destroy();
} }
} }

View File

@ -1,58 +1,36 @@
/** const AdminUser = Discourse.User.extend({
Our data model for dealing with users from the admin section.
@class AdminUser customGroups: Em.computed.filter("groups", (g) => !g.automatic && g.visible && Discourse.Group.create(g)),
@extends Discourse.Model automaticGroups: Em.computed.filter("groups", (g) => g.automatic && Discourse.Group.create(g)),
@namespace Discourse
@module Discourse
**/
Discourse.AdminUser = Discourse.User.extend({
/** generateApiKey() {
Generates an API key for the user. Will regenerate if they already have one. const self = this;
return Discourse.ajax("/admin/users/" + this.get('id') + "/generate_api_key", {
@method generateApiKey type: 'POST'
@returns {Promise} a promise that resolves to the newly generated API key }).then(function (result) {
**/ const apiKey = Discourse.ApiKey.create(result.api_key);
generateApiKey: function() {
var self = this;
return Discourse.ajax("/admin/users/" + this.get('id') + "/generate_api_key", {type: 'POST'}).then(function (result) {
var apiKey = Discourse.ApiKey.create(result.api_key);
self.set('api_key', apiKey); self.set('api_key', apiKey);
return apiKey; return apiKey;
}); });
}, },
groupAdded: function(added){ groupAdded(added) {
var self = this;
return Discourse.ajax("/admin/users/" + this.get('id') + "/groups", { return Discourse.ajax("/admin/users/" + this.get('id') + "/groups", {
type: 'POST', type: 'POST',
data: {group_id: added.id} data: { group_id: added.id }
}).then(function () { }).then(() => this.get('groups').pushObject(added));
self.get('groups').pushObject(added);
});
}, },
groupRemoved: function(removed){ groupRemoved(groupId) {
var self = this; return Discourse.ajax("/admin/users/" + this.get('id') + "/groups/" + groupId, {
return Discourse.ajax("/admin/users/" + this.get('id') + "/groups/" + removed.id, {
type: 'DELETE' type: 'DELETE'
}).then(function () { }).then(() => this.set('groups.[]', this.get('groups').rejectBy("id", groupId)));
self.set('groups.[]', self.get('groups').rejectBy("id", removed.id));
});
}, },
/** revokeApiKey() {
Revokes a user's current API key return Discourse.ajax("/admin/users/" + this.get('id') + "/revoke_api_key", {
type: 'DELETE'
@method revokeApiKey }).then(() => this.set('api_key', null));
@returns {Promise} a promise that resolves when the API key has been deleted
**/
revokeApiKey: function() {
var self = this;
return Discourse.ajax("/admin/users/" + this.get('id') + "/revoke_api_key", {type: 'DELETE'}).then(function () {
self.set('api_key', null);
});
}, },
deleteAllPostsExplanation: function() { deleteAllPostsExplanation: function() {
@ -70,10 +48,10 @@ Discourse.AdminUser = Discourse.User.extend({
} }
}.property('can_delete_all_posts', 'deleteForbidden'), }.property('can_delete_all_posts', 'deleteForbidden'),
deleteAllPosts: function() { deleteAllPosts() {
var user = this; const user = this,
var message = I18n.t('admin.user.delete_all_posts_confirm', {posts: user.get('post_count'), topics: user.get('topic_count')}); message = I18n.t('admin.user.delete_all_posts_confirm', { posts: user.get('post_count'), topics: user.get('topic_count') }),
var buttons = [{ buttons = [{
"label": I18n.t("composer.cancel"), "label": I18n.t("composer.cancel"),
"class": "cancel-inline", "class": "cancel-inline",
"link": true "link": true
@ -81,35 +59,39 @@ Discourse.AdminUser = Discourse.User.extend({
"label": '<i class="fa fa-exclamation-triangle"></i> ' + I18n.t("admin.user.delete_all_posts"), "label": '<i class="fa fa-exclamation-triangle"></i> ' + I18n.t("admin.user.delete_all_posts"),
"class": "btn btn-danger", "class": "btn btn-danger",
"callback": function() { "callback": function() {
Discourse.ajax("/admin/users/" + (user.get('id')) + "/delete_all_posts", {type: 'PUT'}).then(function(){ Discourse.ajax("/admin/users/" + user.get('id') + "/delete_all_posts", {
user.set('post_count', 0); type: 'PUT'
}); }).then(() => user.set('post_count', 0));
} }
}]; }];
bootbox.dialog(message, buttons, {"classes": "delete-all-posts"}); bootbox.dialog(message, buttons, { "classes": "delete-all-posts" });
}, },
// Revoke the user's admin access revokeAdmin() {
revokeAdmin: function() { const self = this;
this.set('admin', false); return Discourse.ajax("/admin/users/" + this.get('id') + "/revoke_admin", {
this.set('can_grant_admin', true); type: 'PUT'
this.set('can_revoke_admin', false); }).then(function() {
return Discourse.ajax("/admin/users/" + (this.get('id')) + "/revoke_admin", {type: 'PUT'}); self.setProperties({
admin: false,
can_grant_admin: true,
can_revoke_admin: false
});
});
}, },
grantAdmin: function() { grantAdmin() {
this.set('admin', true); const self = this;
this.set('can_grant_admin', false); return Discourse.ajax("/admin/users/" + this.get('id') + "/grant_admin", {
this.set('can_revoke_admin', true); type: 'PUT'
var self = this; }).then(function() {
self.setProperties({
Discourse.ajax("/admin/users/" + (this.get('id')) + "/grant_admin", {type: 'PUT'}) admin: true,
.then(null, function(e) { can_grant_admin: false,
self.set('admin', false); can_revoke_admin: true
self.set('can_grant_admin', true); });
self.set('can_revoke_admin', false); }).catch(function(e) {
let error;
var error;
if (e.responseJSON && e.responseJSON.error) { if (e.responseJSON && e.responseJSON.error) {
error = e.responseJSON.error; error = e.responseJSON.error;
} }
@ -118,26 +100,31 @@ Discourse.AdminUser = Discourse.User.extend({
}); });
}, },
// Revoke the user's moderation access revokeModeration() {
revokeModeration: function() { const self = this;
this.set('moderator', false); return Discourse.ajax("/admin/users/" + this.get('id') + "/revoke_moderation", {
this.set('can_grant_moderation', true); type: 'PUT'
this.set('can_revoke_moderation', false); }).then(function() {
return Discourse.ajax("/admin/users/" + (this.get('id')) + "/revoke_moderation", {type: 'PUT'}); self.setProperties({
moderator: false,
can_grant_moderation: true,
can_revoke_moderation: false
});
});
}, },
grantModeration: function() { grantModeration() {
this.set('moderator', true); const self = this;
this.set('can_grant_moderation', false); return Discourse.ajax("/admin/users/" + this.get('id') + "/grant_moderation", {
this.set('can_revoke_moderation', true); type: 'PUT'
var self = this; }).then(function() {
Discourse.ajax("/admin/users/" + (this.get('id')) + "/grant_moderation", {type: 'PUT'}) self.setProperties({
.then(null, function(e) { moderator: true,
self.set('moderator', false); can_grant_moderation: false,
self.set('can_grant_moderation', true); can_revoke_moderation: true
self.set('can_revoke_moderation', false); });
}).catch(function(e) {
var error; let error;
if (e.responseJSON && e.responseJSON.error) { if (e.responseJSON && e.responseJSON.error) {
error = e.responseJSON.error; error = e.responseJSON.error;
} }
@ -146,23 +133,26 @@ Discourse.AdminUser = Discourse.User.extend({
}); });
}, },
refreshBrowsers: function() { refreshBrowsers() {
Discourse.ajax("/admin/users/" + (this.get('id')) + "/refresh_browsers", {type: 'POST'}); return Discourse.ajax("/admin/users/" + this.get('id') + "/refresh_browsers", {
bootbox.alert(I18n.t("admin.user.refresh_browsers_message")); type: 'POST'
}).finally(() => bootbox.alert(I18n.t("admin.user.refresh_browsers_message")));
}, },
approve: function() { approve() {
this.set('can_approve', false); const self = this;
this.set('approved', true); return Discourse.ajax("/admin/users/" + this.get('id') + "/approve", {
this.set('approved_by', Discourse.User.current()); type: 'PUT'
Discourse.ajax("/admin/users/" + (this.get('id')) + "/approve", {type: 'PUT'}); }).then(function() {
self.setProperties({
can_approve: false,
approved: true,
approved_by: Discourse.User.current()
});
});
}, },
username_lower: (function() { setOriginalTrustLevel() {
return this.get('username').toLowerCase();
}).property('username'),
setOriginalTrustLevel: function() {
this.set('originalTrustLevel', this.get('trust_level')); this.set('originalTrustLevel', this.get('trust_level'));
}, },
@ -172,16 +162,14 @@ Discourse.AdminUser = Discourse.User.extend({
dirty: Discourse.computed.propertyNotEqual('originalTrustLevel', 'trustLevel.id'), dirty: Discourse.computed.propertyNotEqual('originalTrustLevel', 'trustLevel.id'),
saveTrustLevel: function() { saveTrustLevel() {
Discourse.ajax("/admin/users/" + this.id + "/trust_level", { return Discourse.ajax("/admin/users/" + this.id + "/trust_level", {
type: 'PUT', type: 'PUT',
data: {level: this.get('trustLevel.id')} data: { level: this.get('trustLevel.id') }
}).then(function () { }).then(function() {
// succeeded
window.location.reload(); window.location.reload();
}, function(e) { }).catch(function(e) {
// failure let error;
var error;
if (e.responseJSON && e.responseJSON.errors) { if (e.responseJSON && e.responseJSON.errors) {
error = e.responseJSON.errors[0]; error = e.responseJSON.errors[0];
} }
@ -190,20 +178,18 @@ Discourse.AdminUser = Discourse.User.extend({
}); });
}, },
restoreTrustLevel: function() { restoreTrustLevel() {
this.set('trustLevel.id', this.get('originalTrustLevel')); this.set('trustLevel.id', this.get('originalTrustLevel'));
}, },
lockTrustLevel: function(locked) { lockTrustLevel(locked) {
Discourse.ajax("/admin/users/" + this.id + "/trust_level_lock", { return Discourse.ajax("/admin/users/" + this.id + "/trust_level_lock", {
type: 'PUT', type: 'PUT',
data: { locked: !!locked } data: { locked: !!locked }
}).then(function() { }).then(function() {
// succeeded
window.location.reload(); window.location.reload();
}, function(e) { }).catch(function(e) {
// failure let error;
var error;
if (e.responseJSON && e.responseJSON.errors) { if (e.responseJSON && e.responseJSON.errors) {
error = e.responseJSON.errors[0]; error = e.responseJSON.errors[0];
} }
@ -212,7 +198,7 @@ Discourse.AdminUser = Discourse.User.extend({
}); });
}, },
canLockTrustLevel: function(){ canLockTrustLevel: function() {
return this.get('trust_level') < 4; return this.get('trust_level') < 4;
}.property('trust_level'), }.property('trust_level'),
@ -220,51 +206,45 @@ Discourse.AdminUser = Discourse.User.extend({
canSuspend: Em.computed.not('staff'), canSuspend: Em.computed.not('staff'),
suspendDuration: function() { suspendDuration: function() {
var suspended_at = moment(this.suspended_at); const suspended_at = moment(this.suspended_at),
var suspended_till = moment(this.suspended_till); suspended_till = moment(this.suspended_till);
return suspended_at.format('L') + " - " + suspended_till.format('L'); return suspended_at.format('L') + " - " + suspended_till.format('L');
}.property('suspended_till', 'suspended_at'), }.property('suspended_till', 'suspended_at'),
suspend: function(duration, reason) { suspend(duration, reason) {
return Discourse.ajax("/admin/users/" + this.id + "/suspend", { return Discourse.ajax("/admin/users/" + this.id + "/suspend", {
type: 'PUT', type: 'PUT',
data: {duration: duration, reason: reason} data: { duration: duration, reason: reason }
}); });
}, },
unsuspend: function() { unsuspend() {
Discourse.ajax("/admin/users/" + this.id + "/unsuspend", { return Discourse.ajax("/admin/users/" + this.id + "/unsuspend", {
type: 'PUT' type: 'PUT'
}).then(function() { }).then(function() {
// succeeded
window.location.reload(); window.location.reload();
}, function(e) { }).catch(function(e) {
// failed
var error = I18n.t('admin.user.unsuspend_failed', { error: "http: " + e.status + " - " + e.body }); var error = I18n.t('admin.user.unsuspend_failed', { error: "http: " + e.status + " - " + e.body });
bootbox.alert(error); bootbox.alert(error);
}); });
}, },
log_out: function(){ log_out() {
Discourse.ajax("/admin/users/" + this.id + "/log_out", { return Discourse.ajax("/admin/users/" + this.id + "/log_out", {
type: 'POST', type: 'POST',
data: { username_or_email: this.get('username') } data: { username_or_email: this.get('username') }
}).then( }).then(function() {
function(){ bootbox.alert(I18n.t("admin.user.logged_out"));
bootbox.alert(I18n.t("admin.user.logged_out")); });
} },
);
}, impersonate() {
return Discourse.ajax("/admin/impersonate", {
impersonate: function() {
Discourse.ajax("/admin/impersonate", {
type: 'POST', type: 'POST',
data: { username_or_email: this.get('username') } data: { username_or_email: this.get('username') }
}).then(function() { }).then(function() {
// succeeded
document.location = "/"; document.location = "/";
}, function(e) { }).catch(function(e) {
// failed
if (e.status === 404) { if (e.status === 404) {
bootbox.alert(I18n.t('admin.impersonate.not_found')); bootbox.alert(I18n.t('admin.impersonate.not_found'));
} else { } else {
@ -273,56 +253,57 @@ Discourse.AdminUser = Discourse.User.extend({
}); });
}, },
activate: function() { activate() {
Discourse.ajax('/admin/users/' + this.id + '/activate', {type: 'PUT'}).then(function() { return Discourse.ajax('/admin/users/' + this.id + '/activate', {
// succeeded type: 'PUT'
}).then(function() {
window.location.reload(); window.location.reload();
}, function(e) { }).catch(function(e) {
// failed
var error = I18n.t('admin.user.activate_failed', { error: "http: " + e.status + " - " + e.body }); var error = I18n.t('admin.user.activate_failed', { error: "http: " + e.status + " - " + e.body });
bootbox.alert(error); bootbox.alert(error);
}); });
}, },
deactivate: function() { deactivate() {
Discourse.ajax('/admin/users/' + this.id + '/deactivate', {type: 'PUT'}).then(function() { return Discourse.ajax('/admin/users/' + this.id + '/deactivate', {
// succeeded type: 'PUT'
}).then(function() {
window.location.reload(); window.location.reload();
}, function(e) { }).catch(function(e) {
// failed
var error = I18n.t('admin.user.deactivate_failed', { error: "http: " + e.status + " - " + e.body }); var error = I18n.t('admin.user.deactivate_failed', { error: "http: " + e.status + " - " + e.body });
bootbox.alert(error); bootbox.alert(error);
}); });
}, },
unblock: function() { unblock() {
Discourse.ajax('/admin/users/' + this.id + '/unblock', {type: 'PUT'}).then(function() { return Discourse.ajax('/admin/users/' + this.id + '/unblock', {
// succeeded type: 'PUT'
}).then(function() {
window.location.reload(); window.location.reload();
}, function(e) { }).catch(function(e) {
// failed
var error = I18n.t('admin.user.unblock_failed', { error: "http: " + e.status + " - " + e.body }); var error = I18n.t('admin.user.unblock_failed', { error: "http: " + e.status + " - " + e.body });
bootbox.alert(error); bootbox.alert(error);
}); });
}, },
block: function() { block() {
Discourse.ajax('/admin/users/' + this.id + '/block', {type: 'PUT'}).then(function() { return Discourse.ajax('/admin/users/' + this.id + '/block', {
// succeeded type: 'PUT'
}).then(function() {
window.location.reload(); window.location.reload();
}, function(e) { }).catch(function(e) {
// failed
var error = I18n.t('admin.user.block_failed', { error: "http: " + e.status + " - " + e.body }); var error = I18n.t('admin.user.block_failed', { error: "http: " + e.status + " - " + e.body });
bootbox.alert(error); bootbox.alert(error);
}); });
}, },
sendActivationEmail: function() { sendActivationEmail() {
Discourse.ajax('/users/action/send_activation_email', {data: {username: this.get('username')}, type: 'POST'}).then(function() { return Discourse.ajax('/users/action/send_activation_email', {
// succeeded type: 'POST',
data: { username: this.get('username') }
}).then(function() {
bootbox.alert( I18n.t('admin.user.activation_email_sent') ); bootbox.alert( I18n.t('admin.user.activation_email_sent') );
}, function(e) { }).catch(function(e) {
// failed
var error = I18n.t('admin.user.send_activation_email_failed', { error: "http: " + e.status + " - " + e.body }); var error = I18n.t('admin.user.send_activation_email_failed', { error: "http: " + e.status + " - " + e.body });
bootbox.alert(error); bootbox.alert(error);
}); });
@ -330,11 +311,14 @@ Discourse.AdminUser = Discourse.User.extend({
anonymizeForbidden: Em.computed.not("can_be_anonymized"), anonymizeForbidden: Em.computed.not("can_be_anonymized"),
anonymize: function() { anonymize() {
var user = this; const user = this,
message = I18n.t("admin.user.anonymize_confirm");
var performAnonymize = function() { const performAnonymize = function() {
Discourse.ajax("/admin/users/" + user.get('id') + '/anonymize.json', {type: 'PUT'}).then(function(data) { return Discourse.ajax("/admin/users/" + user.get('id') + '/anonymize.json', {
type: 'PUT'
}).then(function(data) {
if (data.success) { if (data.success) {
if (data.username) { if (data.username) {
document.location = "/admin/users/" + data.username; document.location = "/admin/users/" + data.username;
@ -347,26 +331,22 @@ Discourse.AdminUser = Discourse.User.extend({
user.setProperties(data.user); user.setProperties(data.user);
} }
} }
}, function() { }).catch(function() {
bootbox.alert(I18n.t("admin.user.anonymize_failed")); bootbox.alert(I18n.t("admin.user.anonymize_failed"));
}); });
}; };
var message = I18n.t("admin.user.anonymize_confirm"); const buttons = [{
var buttons = [{
"label": I18n.t("composer.cancel"), "label": I18n.t("composer.cancel"),
"class": "cancel", "class": "cancel",
"link": true "link": true
}, { }, {
"label": '<i class="fa fa-exclamation-triangle"></i>' + I18n.t('admin.user.anonymize_yes'), "label": '<i class="fa fa-exclamation-triangle"></i>' + I18n.t('admin.user.anonymize_yes'),
"class": "btn btn-danger", "class": "btn btn-danger",
"callback": function(){ "callback": function() { performAnonymize(); }
performAnonymize();
}
}]; }];
bootbox.dialog(message, buttons, {"classes": "delete-user-modal"}); bootbox.dialog(message, buttons, { "classes": "delete-user-modal" });
}, },
deleteForbidden: Em.computed.not("canBeDeleted"), deleteForbidden: Em.computed.not("canBeDeleted"),
@ -383,12 +363,13 @@ Discourse.AdminUser = Discourse.User.extend({
} }
}.property('deleteForbidden'), }.property('deleteForbidden'),
destroy: function(opts) { destroy(opts) {
var user = this; const user = this,
var location = document.location.pathname; message = I18n.t("admin.user.delete_confirm"),
location = document.location.pathname;
var performDestroy = function(block) { const performDestroy = function(block) {
var formData = { context: location }; let formData = { context: location };
if (block) { if (block) {
formData["block_email"] = true; formData["block_email"] = true;
formData["block_urls"] = true; formData["block_urls"] = true;
@ -397,7 +378,7 @@ Discourse.AdminUser = Discourse.User.extend({
if (opts && opts.deletePosts) { if (opts && opts.deletePosts) {
formData["delete_posts"] = true; formData["delete_posts"] = true;
} }
Discourse.ajax("/admin/users/" + user.get('id') + '.json', { return Discourse.ajax("/admin/users/" + user.get('id') + '.json', {
type: 'DELETE', type: 'DELETE',
data: formData data: formData
}).then(function(data) { }).then(function(data) {
@ -413,47 +394,42 @@ Discourse.AdminUser = Discourse.User.extend({
user.setProperties(data.user); user.setProperties(data.user);
} }
} }
}, function() { }).catch(function() {
Discourse.AdminUser.find( user.get('username') ).then(function(u){ user.setProperties(u); }); Discourse.AdminUser.find( user.get('username') ).then(function(u){ user.setProperties(u); });
bootbox.alert(I18n.t("admin.user.delete_failed")); bootbox.alert(I18n.t("admin.user.delete_failed"));
}); });
}; };
var message = I18n.t("admin.user.delete_confirm"); const buttons = [{
var buttons = [{
"label": I18n.t("composer.cancel"), "label": I18n.t("composer.cancel"),
"class": "cancel", "class": "cancel",
"link": true "link": true
}, { }, {
"label": I18n.t('admin.user.delete_dont_block'), "label": I18n.t('admin.user.delete_dont_block'),
"class": "btn", "class": "btn",
"callback": function(){ "callback": function(){ performDestroy(false); }
performDestroy(false);
}
}, { }, {
"label": '<i class="fa fa-exclamation-triangle"></i>' + I18n.t('admin.user.delete_and_block'), "label": '<i class="fa fa-exclamation-triangle"></i>' + I18n.t('admin.user.delete_and_block'),
"class": "btn btn-danger", "class": "btn btn-danger",
"callback": function(){ "callback": function(){ performDestroy(true); }
performDestroy(true);
}
}]; }];
bootbox.dialog(message, buttons, {"classes": "delete-user-modal"}); bootbox.dialog(message, buttons, { "classes": "delete-user-modal" });
}, },
deleteAsSpammer: function(successCallback) { deleteAsSpammer(successCallback) {
var user = this; const user = this;
user.checkEmail().then(function() { user.checkEmail().then(function() {
var data = { const data = {
posts: user.get('post_count'), posts: user.get('post_count'),
topics: user.get('topic_count'), topics: user.get('topic_count'),
email: user.get('email') || I18n.t("flagging.hidden_email_address"), email: user.get('email') || I18n.t("flagging.hidden_email_address"),
ip_address: user.get('ip_address') || I18n.t("flagging.ip_address_missing") ip_address: user.get('ip_address') || I18n.t("flagging.ip_address_missing")
}; };
var message = I18n.t('flagging.delete_confirm', data);
var buttons = [{ const message = I18n.t('flagging.delete_confirm', data),
buttons = [{
"label": I18n.t("composer.cancel"), "label": I18n.t("composer.cancel"),
"class": "cancel-inline", "class": "cancel-inline",
"link": true "link": true
@ -461,7 +437,7 @@ Discourse.AdminUser = Discourse.User.extend({
"label": '<i class="fa fa-exclamation-triangle"></i> ' + I18n.t("flagging.yes_delete_spammer"), "label": '<i class="fa fa-exclamation-triangle"></i> ' + I18n.t("flagging.yes_delete_spammer"),
"class": "btn btn-danger", "class": "btn btn-danger",
"callback": function() { "callback": function() {
Discourse.ajax("/admin/users/" + user.get('id') + '.json', { return Discourse.ajax("/admin/users/" + user.get('id') + '.json', {
type: 'DELETE', type: 'DELETE',
data: { data: {
delete_posts: true, delete_posts: true,
@ -477,23 +453,24 @@ Discourse.AdminUser = Discourse.User.extend({
} else { } else {
bootbox.alert(I18n.t("admin.user.delete_failed")); bootbox.alert(I18n.t("admin.user.delete_failed"));
} }
}, function() { }).catch(function() {
bootbox.alert(I18n.t("admin.user.delete_failed")); bootbox.alert(I18n.t("admin.user.delete_failed"));
}); });
} }
}]; }];
bootbox.dialog(message, buttons, {"classes": "flagging-delete-spammer"}); bootbox.dialog(message, buttons, {"classes": "flagging-delete-spammer"});
}); });
}, },
loadDetails: function() { loadDetails() {
var model = this; const user = this;
if (model.get('loadedDetails')) { return Ember.RSVP.resolve(model); }
return Discourse.AdminUser.find(model.get('username_lower')).then(function (result) { if (user.get('loadedDetails')) { return Ember.RSVP.resolve(user); }
model.setProperties(result);
model.set('loadedDetails', true); return Discourse.AdminUser.find(user.get('username_lower')).then(function (result) {
user.setProperties(result);
user.set('loadedDetails', true);
}); });
}, },
@ -517,29 +494,25 @@ Discourse.AdminUser = Discourse.User.extend({
}); });
Discourse.AdminUser.reopenClass({ AdminUser.reopenClass({
bulkApprove: function(users) { bulkApprove(users) {
_.each(users, function(user) { _.each(users, function(user) {
user.set('approved', true); user.setProperties({
user.set('can_approve', false); approved: true,
return user.set('selected', false); can_approve: false,
selected: false
});
}); });
bootbox.alert(I18n.t("admin.user.approve_bulk_success"));
return Discourse.ajax("/admin/users/approve-bulk", { return Discourse.ajax("/admin/users/approve-bulk", {
type: 'PUT', type: 'PUT',
data: { data: { users: users.map((u) => u.id) }
users: users.map(function(u) { }).finally(() => bootbox.alert(I18n.t("admin.user.approve_bulk_success")));
return u.id;
})
}
});
}, },
bulkReject: function(users) { bulkReject(users) {
_.each(users, function(user){ _.each(users, function(user) {
user.set('can_approve', false); user.set('can_approve', false);
user.set('selected', false); user.set('selected', false);
}); });
@ -547,26 +520,26 @@ Discourse.AdminUser.reopenClass({
return Discourse.ajax("/admin/users/reject-bulk", { return Discourse.ajax("/admin/users/reject-bulk", {
type: 'DELETE', type: 'DELETE',
data: { data: {
users: users.map(function(u) { return u.id; }), users: users.map((u) => u.id),
context: window.location.pathname context: window.location.pathname
} }
}); });
}, },
find: function(username) { find(username) {
return Discourse.ajax("/admin/users/" + username + ".json").then(function (result) { return Discourse.ajax("/admin/users/" + username + ".json").then(function (result) {
result.loadedDetails = true; result.loadedDetails = true;
return Discourse.AdminUser.create(result); return Discourse.AdminUser.create(result);
}); });
}, },
findAll: function(query, filter) { findAll(query, filter) {
return Discourse.ajax("/admin/users/list/" + query + ".json", { return Discourse.ajax("/admin/users/list/" + query + ".json", {
data: filter data: filter
}).then(function(users) { }).then(function(users) {
return users.map(function(u) { return users.map((u) => Discourse.AdminUser.create(u));
return Discourse.AdminUser.create(u);
});
}); });
} }
}); });
export default AdminUser;

View File

@ -3,18 +3,18 @@
<div class='user-controls'> <div class='user-controls'>
{{#if active}} {{#if active}}
{{#link-to 'user' model class="btn"}} {{#link-to 'user' model class="btn"}}
<i class='fa fa-user'></i> {{fa-icon "user"}}
{{i18n 'admin.user.show_public_profile'}} {{i18n 'admin.user.show_public_profile'}}
{{/link-to}} {{/link-to}}
{{#if can_impersonate}} {{#if can_impersonate}}
<button class='btn btn-danger' {{action "impersonate" target="content"}} title="{{i18n 'admin.impersonate.help'}}"> <button class='btn btn-danger' {{action "impersonate" target="content"}} title="{{i18n 'admin.impersonate.help'}}">
<i class='fa fa-crosshairs'></i> {{fa-icon "crosshairs"}}
{{i18n 'admin.impersonate.title'}} {{i18n 'admin.impersonate.title'}}
</button> </button>
{{/if}} {{/if}}
{{#if currentUser.admin}} {{#if currentUser.admin}}
<button class='btn' {{action "log_out" target="content"}}> <button class='btn' {{action "log_out" target="content"}}>
<i class='fa fa-power-off'></i> {{fa-icon "power-off"}}
{{i18n 'admin.user.log_out'}} {{i18n 'admin.user.log_out'}}
</button> </button>
{{/if}} {{/if}}
@ -26,7 +26,7 @@
<div class='value'>{{username}}</div> <div class='value'>{{username}}</div>
<div class='controls'> <div class='controls'>
{{#link-to 'preferences.username' model class="btn"}} {{#link-to 'preferences.username' model class="btn"}}
<i class='fa fa-pencil'></i> {{fa-icon "pencil"}}
{{i18n 'user.change_username.title'}} {{i18n 'user.change_username.title'}}
{{/link-to}} {{/link-to}}
</div> </div>
@ -75,28 +75,32 @@
</div> </div>
<div class='controls'> <div class='controls'>
{{#if editingTitle}} {{#if editingTitle}}
<button class='btn' {{action "saveTitle"}}>{{i18n 'admin.user.save_title'}}</button> {{d-button action="saveTitle" label="admin.user.save_title"}}
<a href="#" {{action "toggleTitleEdit"}}>{{i18n 'cancel'}}</a> <a href {{action "toggleTitleEdit"}}>{{i18n 'cancel'}}</a>
{{else}} {{else}}
<button class='btn' {{action "toggleTitleEdit"}}><i class="fa fa-pencil"></i>{{i18n 'admin.user.edit_title'}}</button> {{d-button action="toggleTitleEdit" icon="pencil" label="admin.user.edit_title"}}
{{/if}} {{/if}}
</div> </div>
</div> </div>
{{#if currentUser.admin}} {{#if currentUser.admin}}
<div class='display-row'> <div class='display-row'>
<div class='field'>{{i18n 'admin.groups.title'}}</div> <div class='field'>{{i18n 'admin.groups.automatic.title'}}</div>
<div class='value'>{{automaticGroups}}</div>
</div>
<div class='display-row'>
<div class='field'>{{i18n 'admin.groups.custom.title'}}</div>
<div class='value'> <div class='value'>
{{admin-group-selector selected=model.groups available=availableGroups}} {{admin-group-selector selected=customGroups available=availableGroups}}
</div> </div>
<div class='controls'> <div class='controls'>
{{#if custom_groups}} {{#if customGroups}}
{{i18n 'admin.groups.primary'}} {{i18n 'admin.groups.primary'}}
{{combo-box content=custom_groups value=primary_group_id nameProperty="name" none="admin.groups.no_primary"}} {{combo-box content=customGroups value=primary_group_id nameProperty="name" none="admin.groups.no_primary"}}
{{/if}} {{/if}}
{{#if primaryGroupDirty}} {{#if primaryGroupDirty}}
<button class='btn ok no-text' {{action "savePrimaryGroup"}}><i class='fa fa-check'></i></button> {{d-button icon="check" class="ok no-text" action="savePrimaryGroup"}}
<button class='btn cancel no-text' {{action "resetPrimaryGroup"}}><i class='fa fa-times'></i></button> {{d-button icon="times" class="cancel no-text" action="resetPrimaryGroup"}}
{{/if}} {{/if}}
</div> </div>
</div> </div>
@ -132,7 +136,7 @@
{{i18n 'badges.badge_count' count=badge_count}} {{i18n 'badges.badge_count' count=badge_count}}
</div> </div>
<div class='controls'> <div class='controls'>
{{#link-to 'adminUser.badges' this class="btn"}}<i class="fa fa-certificate"></i>{{i18n 'admin.badges.edit_badges'}}{{/link-to}} {{#link-to 'adminUser.badges' this class="btn"}}{{fa-icon "certificate"}}{{i18n 'admin.badges.edit_badges'}}{{/link-to}}
</div> </div>
</div> </div>
{{/if}} {{/if}}
@ -165,13 +169,11 @@
<div class='value'> <div class='value'>
{{#if approved}} {{#if approved}}
{{i18n 'admin.user.approved_by'}} {{i18n 'admin.user.approved_by'}}
{{#link-to 'adminUser' approvedBy}}{{avatar approvedBy imageSize="small"}}{{/link-to}} {{#link-to 'adminUser' approvedBy}}{{avatar approvedBy imageSize="small"}}{{/link-to}}
{{#link-to 'adminUser' approvedBy}}{{approvedBy.username}}{{/link-to}} {{#link-to 'adminUser' approvedBy}}{{approvedBy.username}}{{/link-to}}
{{else}} {{else}}
{{i18n 'no_value'}} {{i18n 'no_value'}}
{{/if}} {{/if}}
</div> </div>
<div class='controls'> <div class='controls'>
{{#if approved}} {{#if approved}}
@ -179,7 +181,7 @@
{{else}} {{else}}
{{#if can_approve}} {{#if can_approve}}
<button class='btn' {{action "approve" target="content"}}> <button class='btn' {{action "approve" target="content"}}>
<i class='fa fa-check'></i> {{fa-icon "check"}}
{{i18n 'admin.user.approve'}} {{i18n 'admin.user.approve'}}
</button> </button>
{{/if}} {{/if}}
@ -206,13 +208,13 @@
{{else}} {{else}}
{{#if can_send_activation_email}} {{#if can_send_activation_email}}
<button class='btn' {{action "sendActivationEmail" target="content"}}> <button class='btn' {{action "sendActivationEmail" target="content"}}>
<i class='fa fa-envelope'></i> {{fa-icon "envelope"}}
{{i18n 'admin.user.send_activation_email'}} {{i18n 'admin.user.send_activation_email'}}
</button> </button>
{{/if}} {{/if}}
{{#if can_activate}} {{#if can_activate}}
<button class='btn' {{action "activate" target="content"}}> <button class='btn' {{action "activate" target="content"}}>
<i class='fa fa-check'></i> {{fa-icon "check"}}
{{i18n 'admin.user.activate'}} {{i18n 'admin.user.activate'}}
</button> </button>
{{/if}} {{/if}}
@ -222,19 +224,18 @@
<div class='display-row'> <div class='display-row'>
<div class='field'>{{i18n 'admin.api.key'}}</div> <div class='field'>{{i18n 'admin.api.key'}}</div>
{{#if api_key}} {{#if api_key}}
<div class='long-value'> <div class='long-value'>
{{api_key.key}} {{api_key.key}}
<button class='btn' {{action "regenerateApiKey"}}><i class="fa fa-undo"></i>{{i18n 'admin.api.regenerate'}}</button> {{d-button action="regenerateApiKey" icon="undo" label="admin.api.regenerate"}}
<button {{action "revokeApiKey"}} class="btn"><i class="fa fa-times"></i>{{i18n 'admin.api.revoke'}}</button> {{d-button action="revokeApiKey" icon="times" label="admin.api.revoke"}}
</div> </div>
{{else}} {{else}}
<div class='value'> <div class='value'>
&mdash; &mdash;
</div> </div>
<div class='controls'> <div class='controls'>
<button {{action "generateApiKey"}} class="btn"><i class="fa fa-key"></i>{{i18n 'admin.api.generate'}}</button> {{d-button action="generateApiKey" icon="key" label="admin.api.generate"}}
</div> </div>
{{/if}} {{/if}}
</div> </div>
@ -245,37 +246,36 @@
<div class='controls'> <div class='controls'>
{{#if can_revoke_admin}} {{#if can_revoke_admin}}
<button class='btn' {{action "revokeAdmin" target="content"}}> <button class='btn' {{action "revokeAdmin" target="content"}}>
<i class='fa fa-shield'></i> {{fa-icon "shield"}}
{{i18n 'admin.user.revoke_admin'}} {{i18n 'admin.user.revoke_admin'}}
</button> </button>
{{/if}} {{/if}}
{{#if can_grant_admin}} {{#if can_grant_admin}}
<button class='btn' {{action "grantAdmin" target="content"}}> <button class='btn' {{action "grantAdmin" target="content"}}>
<i class='fa fa-shield'></i> {{fa-icon "shield"}}
{{i18n 'admin.user.grant_admin'}} {{i18n 'admin.user.grant_admin'}}
</button> </button>
{{/if}} {{/if}}
</div> </div>
</div> </div>
<div class='display-row'> <div class='display-row'>
<div class='field'>{{i18n 'admin.user.moderator'}}</div> <div class='field'>{{i18n 'admin.user.moderator'}}</div>
<div class='value'>{{moderator}}</div> <div class='value'>{{moderator}}</div>
<div class='controls'> <div class='controls'>
{{#if can_revoke_moderation}} {{#if can_revoke_moderation}}
<button class='btn' {{action "revokeModeration" target="content"}}> <button class='btn' {{action "revokeModeration" target="content"}}>
<i class='fa fa-shield'></i> {{fa-icon "shield"}}
{{i18n 'admin.user.revoke_moderation'}} {{i18n 'admin.user.revoke_moderation'}}
</button> </button>
{{/if}} {{/if}}
{{#if can_grant_moderation}} {{#if can_grant_moderation}}
<button class='btn' {{action "grantModeration" target="content"}}> <button class='btn' {{action "grantModeration" target="content"}}>
<i class='fa fa-shield'></i> {{fa-icon "shield"}}
{{i18n 'admin.user.grant_moderation'}} {{i18n 'admin.user.grant_moderation'}}
</button> </button>
{{/if}} {{/if}}
</div> </div>
</div> </div>
<div class='display-row'> <div class='display-row'>
@ -284,8 +284,8 @@
{{combo-box content=trustLevels value=trust_level nameProperty="detailedName"}} {{combo-box content=trustLevels value=trust_level nameProperty="detailedName"}}
{{#if dirty}} {{#if dirty}}
<div> <div>
<button class='btn ok no-text' {{action "saveTrustLevel" target="content"}}><i class='fa fa-check'></i></button> <button class='btn ok no-text' {{action "saveTrustLevel" target="content"}}>{{fa-icon "check"}}</button>
<button class='btn cancel no-text' {{action "restoreTrustLevel" target="content"}}><i class='fa fa-times'></i></button> <button class='btn cancel no-text' {{action "restoreTrustLevel" target="content"}}>{{fa-icon "times"}}</button>
</div> </div>
{{/if}} {{/if}}
</div> </div>
@ -300,7 +300,6 @@
{{#if tl3Requirements}} {{#if tl3Requirements}}
{{#link-to 'adminUser.tl3Requirements' this class="btn"}}{{i18n 'admin.user.trust_level_3_requirements'}}{{/link-to}} {{#link-to 'adminUser.tl3Requirements' this class="btn"}}{{i18n 'admin.user.trust_level_3_requirements'}}{{/link-to}}
{{/if}} {{/if}}
</div> </div>
</div> </div>
@ -310,7 +309,7 @@
<div class='controls'> <div class='controls'>
{{#if isSuspended}} {{#if isSuspended}}
<button class='btn btn-danger' {{action "unsuspend" target="content"}}> <button class='btn btn-danger' {{action "unsuspend" target="content"}}>
<i class='fa fa-ban'></i> {{fa-icon "ban"}}
{{i18n 'admin.user.unsuspend'}} {{i18n 'admin.user.unsuspend'}}
</button> </button>
{{suspendDuration}} {{suspendDuration}}
@ -318,7 +317,7 @@
{{else}} {{else}}
{{#if canSuspend}} {{#if canSuspend}}
<button class='btn btn-danger' {{action "showSuspendModal" this}}> <button class='btn btn-danger' {{action "showSuspendModal" this}}>
<i class='fa fa-ban'></i> {{fa-icon "ban"}}
{{i18n 'admin.user.suspend'}} {{i18n 'admin.user.suspend'}}
</button> </button>
{{i18n 'admin.user.suspended_explanation'}} {{i18n 'admin.user.suspended_explanation'}}
@ -347,7 +346,7 @@
<div class='controls'> <div class='controls'>
{{#if blocked}} {{#if blocked}}
<button class='btn' {{action "unblock" target="content"}}> <button class='btn' {{action "unblock" target="content"}}>
<i class='fa fa-thumbs-o-up'></i> {{fa-icon "thumbs-o-up"}}
{{i18n 'admin.user.unblock'}} {{i18n 'admin.user.unblock'}}
</button> </button>
{{i18n 'admin.user.block_explanation'}} {{i18n 'admin.user.block_explanation'}}
@ -386,7 +385,7 @@
{{#if can_delete_all_posts}} {{#if can_delete_all_posts}}
{{#if post_count}} {{#if post_count}}
<button class='btn btn-danger' {{action "deleteAllPosts" target="content"}}> <button class='btn btn-danger' {{action "deleteAllPosts" target="content"}}>
<i class='fa fa-trash-o'></i> {{fa-icon "trash-o"}}
{{i18n 'admin.user.delete_all_posts'}} {{i18n 'admin.user.delete_all_posts'}}
</button> </button>
{{/if}} {{/if}}
@ -473,7 +472,9 @@
{{#if deleteExplanation}} {{#if deleteExplanation}}
<div class="clearfix"></div> <div class="clearfix"></div>
<br/> <br/>
<div class="pull-right"><i class="fa fa-exclamation-triangle"></i> {{deleteExplanation}}</div> <div class="pull-right">
{{fa-icon "exclamation-triangle"}} {{deleteExplanation}}
</div>
{{/if}} {{/if}}
</section> </section>
<div class="clearfix"></div> <div class="clearfix"></div>

View File

@ -1683,6 +1683,10 @@ en:
automatic: "Automatic" automatic: "Automatic"
automatic_membership_email_domains: "Users who register with an email domain that exactly matches one in this list will be automatically added to this group:" automatic_membership_email_domains: "Users who register with an email domain that exactly matches one in this list will be automatically added to this group:"
automatic_membership_retroactive: "Apply the same email domain rule to add existing registered users" automatic_membership_retroactive: "Apply the same email domain rule to add existing registered users"
custom:
title: "Custom Groups"
automatic:
title: "Automatic Groups"
api: api:
generate_master: "Generate Master API Key" generate_master: "Generate Master API Key"