Use far fewer admin user list routes, instead pass the query as a

parameter.
This commit is contained in:
Robin Ward 2014-11-26 13:05:49 -05:00
parent 41deea6892
commit a3e53e0d28
13 changed files with 196 additions and 452 deletions

View File

@ -0,0 +1,74 @@
export default Ember.ArrayController.extend({
query: null,
showEmails: false,
refreshing: false,
listFilter: null,
selectAll: false,
queryNew: Em.computed.equal('query', 'new'),
queryPending: Em.computed.equal('query', 'pending'),
queryHasApproval: Em.computed.or('queryNew', 'queryPending'),
showApproval: Em.computed.and('siteSettings.must_approve_users', 'queryHasApproval'),
searchHint: Discourse.computed.i18n('search_hint'),
hasSelection: Em.computed.gt('selectedCount', 0),
selectedCount: function() {
var model = this.get('model');
if (!model || !model.length) return 0;
return model.filterProperty('selected').length;
}.property('model.@each.selected'),
selectAllChanged: function() {
var val = this.get('selectAll');
this.get('model').forEach(function(user) {
if (user.get('can_approve')) {
user.set('selected', val);
}
});
}.observes('selectAll'),
title: function() {
return I18n.t('admin.users.titles.' + this.get('query'));
}.property('query'),
_filterUsers: Discourse.debounce(function() {
this._refreshUsers();
}, 250).observes('listFilter'),
_refreshUsers: function() {
var self = this;
this.set('refreshing', true);
Discourse.AdminUser.findAll(this.get('query'), { filter: this.get('listFilter'), show_emails: this.get('showEmails') }).then(function (result) {
self.set('model', result);
}).finally(function() {
self.set('refreshing', false);
});
},
actions: {
approveUsers: function() {
Discourse.AdminUser.bulkApprove(this.get('model').filterProperty('selected'));
this._refreshUsers();
},
rejectUsers: function() {
var controller = this;
Discourse.AdminUser.bulkReject(this.get('model').filterProperty('selected')).then(function(result){
var message = I18n.t("admin.users.reject_successful", {count: result.success});
if (result.failed > 0) {
message += ' ' + I18n.t("admin.users.reject_failures", {count: result.failed});
message += ' ' + I18n.t("admin.user.delete_forbidden", {count: Discourse.SiteSettings.delete_user_max_post_age});
}
bootbox.alert(message);
controller._refreshUsers();
});
},
showEmails: function() {
this.set('showEmails', true);
this._refreshUsers(true);
}
}
});

View File

@ -1,140 +0,0 @@
/**
This controller supports the interface for listing users in the admin section.
@class AdminUsersListController
@extends Ember.ArrayController
@namespace Discourse
@module Discourse
**/
export default Ember.ArrayController.extend(Discourse.Presence, {
username: null,
query: null,
selectAll: false,
loading: false,
mustApproveUsers: Discourse.computed.setting('must_approve_users'),
queryNew: Em.computed.equal('query', 'new'),
queryPending: Em.computed.equal('query', 'pending'),
queryHasApproval: Em.computed.or('queryNew', 'queryPending'),
searchHint: function() { return I18n.t("search_hint"); }.property(),
/**
Triggered when the selectAll property is changed
@event selectAll
**/
selectAllChanged: function() {
var _this = this;
_.each(this.get('model'),function(user) {
user.set('selected', _this.get('selectAll'));
});
}.observes('selectAll'),
/**
Triggered when the username filter is changed
@event filterUsers
**/
filterUsers: Discourse.debounce(function() {
this.refreshUsers();
}, 250).observes('username'),
/**
Triggered when the order of the users list is changed
@event orderChanged
**/
orderChanged: function() {
this.refreshUsers();
}.observes('query'),
/**
The title of the user list, based on which query was performed.
@property title
**/
title: function() {
return I18n.t('admin.users.titles.' + this.get('query'));
}.property('query'),
/**
Do we want to show the approval controls?
@property showApproval
**/
showApproval: function() {
return Discourse.SiteSettings.must_approve_users && this.get('queryHasApproval');
}.property('queryPending'),
/**
How many users are currently selected
@property selectedCount
**/
selectedCount: function() {
if (this.blank('model')) return 0;
return this.get('model').filterProperty('selected').length;
}.property('model.@each.selected'),
/**
Do we have any selected users?
@property hasSelection
**/
hasSelection: Em.computed.gt('selectedCount', 0),
/**
Refresh the current list of users.
@method refreshUsers
**/
refreshUsers: function(showEmails) {
var adminUsersListController = this;
adminUsersListController.set('loading', true);
Discourse.AdminUser.findAll(this.get('query'), { filter: this.get('username'), show_emails: showEmails }).then(function (result) {
adminUsersListController.set('model', result);
adminUsersListController.set('loading', false);
});
},
/**
Show the list of users.
@method show
**/
show: function(term) {
if (this.get('query') === term) {
this.refreshUsers();
return;
}
this.set('query', term);
},
actions: {
approveUsers: function() {
Discourse.AdminUser.bulkApprove(this.get('model').filterProperty('selected'));
this.refreshUsers();
},
rejectUsers: function() {
var controller = this;
Discourse.AdminUser.bulkReject(this.get('model').filterProperty('selected')).then(function(result){
var message = I18n.t("admin.users.reject_successful", {count: result.success});
if (result.failed > 0) {
message += ' ' + I18n.t("admin.users.reject_failures", {count: result.failed});
message += ' ' + I18n.t("admin.user.delete_forbidden", {count: Discourse.SiteSettings.delete_user_max_post_age});
}
bootbox.alert(message);
controller.refreshUsers();
});
},
showEmails: function() {
this.refreshUsers(true);
}
}
});

View File

@ -0,0 +1,15 @@
export default Discourse.Route.extend({
model: function(params) {
this.userFilter = params.filter;
return Discourse.AdminUser.findAll(params.filter);
},
setupController: function(controller, model) {
controller.setProperties({
model: model,
query: this.userFilter,
showEmails: false,
refreshing: false,
});
}
});

View File

@ -49,11 +49,9 @@ Discourse.Route.buildRoutes(function() {
this.route('badges');
this.route('tl3Requirements', { path: '/tl3_requirements' });
});
this.resource('adminUsersList', { path: '/list' }, function() {
_.each(['active', 'new', 'pending', 'admins', 'moderators', 'blocked', 'suspended',
'newuser', 'basicuser', 'regular', 'leaders', 'elders'], function(x) {
this.route(x, { path: '/' + x });
}, this);
this.route('show', {path: '/:filter'});
});
});

View File

@ -1,137 +0,0 @@
/**
Handles the route that deals with listing users
@class AdminUsersListRoute
@extends Discourse.Route
@namespace Discourse
@module Discourse
**/
Discourse.AdminUsersListRoute = Discourse.Route.extend({
renderTemplate: function() {
this.render('admin/templates/users_list', {into: 'admin/templates/admin'});
},
actions: {
exportUsers: function() {
Discourse.ExportCsv.exportUserList().then(function(result) {
if (result.success) {
bootbox.alert(I18n.t("admin.export_csv.success"));
} else {
bootbox.alert(I18n.t("admin.export_csv.failed"));
}
});
}
}
});
/**
Index should just redirect to active
@class AdminUsersIndexRoute
@extends Discourse.Route
@namespace Discourse
@module Discourse
**/
Discourse.AdminUsersListIndexRoute = Discourse.Route.extend({
redirect: function() {
this.transitionTo('adminUsersList.active');
}
});
/**
Handles the route that lists active users.
@class AdminUsersListActiveRoute
@extends Discourse.Route
@namespace Discourse
@module Discourse
**/
Discourse.AdminUsersListActiveRoute = Discourse.Route.extend({
setupController: function() {
return this.controllerFor('adminUsersList').show('active');
}
});
/**
Handles the route that lists new users.
@class AdminUsersListNewRoute
@extends Discourse.Route
@namespace Discourse
@module Discourse
**/
Discourse.AdminUsersListNewRoute = Discourse.Route.extend({
setupController: function() {
return this.controllerFor('adminUsersList').show('new');
}
});
/**
Handles the route that lists pending users.
@class AdminUsersListPendingRoute
@extends Discourse.Route
@namespace Discourse
@module Discourse
**/
Discourse.AdminUsersListPendingRoute = Discourse.Route.extend({
setupController: function() {
return this.controllerFor('adminUsersList').show('pending');
}
});
/**
Handles the route that lists admin users.
@class AdminUsersListAdminsRoute
@extends Discourse.Route
@namespace Discourse
@module Discourse
**/
Discourse.AdminUsersListAdminsRoute = Discourse.Route.extend({
setupController: function() {
return this.controllerFor('adminUsersList').show('admins');
}
});
/**
Handles the route that lists moderators.
@class AdminUsersListModeratorsRoute
@extends Discourse.Route
@namespace Discourse
@module Discourse
**/
Discourse.AdminUsersListModeratorsRoute = Discourse.Route.extend({
setupController: function() {
return this.controllerFor('adminUsersList').show('moderators');
}
});
/**
Handles the route that lists blocked users.
@class AdminUsersListBlockedRoute
@extends Discourse.Route
@namespace Discourse
@module Discourse
**/
Discourse.AdminUsersListBlockedRoute = Discourse.Route.extend({
setupController: function() {
return this.controllerFor('adminUsersList').show('blocked');
}
});
/**
Handles the route that lists suspended users.
@class AdminUsersListSuspendedRoute
@extends Discourse.Route
@namespace Discourse
@module Discourse
**/
Discourse.AdminUsersListSuspendedRoute = Discourse.Route.extend({
setupController: function() {
return this.controllerFor('adminUsersList').show('suspended');
}
});

View File

@ -1,69 +0,0 @@
/**
Handles the route that lists users at trust level 0.
@class AdminUsersListNewuserRoute
@extends Discourse.Route
@namespace Discourse
@module Discourse
**/
Discourse.AdminUsersListNewuserRoute = Discourse.Route.extend({
setupController: function() {
return this.controllerFor('adminUsersList').show('newuser');
}
});
/**
Handles the route that lists users at trust level 1.
@class AdminUsersListBasicuserRoute
@extends Discourse.Route
@namespace Discourse
@module Discourse
**/
Discourse.AdminUsersListBasicuserRoute = Discourse.Route.extend({
setupController: function() {
return this.controllerFor('adminUsersList').show('basic');
}
});
/**
Handles the route that lists users at trust level 2.
@class AdminUsersListRegularRoute
@extends Discourse.Route
@namespace Discourse
@module Discourse
**/
Discourse.AdminUsersListRegularRoute = Discourse.Route.extend({
setupController: function() {
return this.controllerFor('adminUsersList').show('regular');
}
});
/**
Handles the route that lists users at trust level 3.
@class AdminUsersListLeadersRoute
@extends Discourse.Route
@namespace Discourse
@module Discourse
**/
Discourse.AdminUsersListLeadersRoute = Discourse.Route.extend({
setupController: function() {
return this.controllerFor('adminUsersList').show('leader');
}
});
/**
Handles the route that lists users at trust level 4.
@class AdminUsersListEldersRoute
@extends Discourse.Route
@namespace Discourse
@module Discourse
**/
Discourse.AdminUsersListEldersRoute = Discourse.Route.extend({
setupController: function() {
return this.controllerFor('adminUsersList').show('elder');
}
});

View File

@ -8,7 +8,7 @@
{{#if currentUser.admin}}
<li>{{#link-to 'adminSiteSettings'}}{{i18n admin.site_settings.title}}{{/link-to}}</li>
{{/if}}
<li>{{#link-to 'adminUsersList'}}{{i18n admin.users.title}}{{/link-to}}</li>
<li>{{#link-to 'adminUsersList.show' 'active'}}{{i18n admin.users.title}}{{/link-to}}</li>
{{#if showBadges}}
<li>{{#link-to 'adminBadges.index'}}{{i18n admin.badges.title}}{{/link-to}}</li>
{{/if}}

View File

@ -25,15 +25,15 @@
<table>
<tr>
<td class="title"><i class='fa fa-shield'></i> {{i18n admin.dashboard.admins}}</td>
<td class="value">{{#link-to 'adminUsersList.admins'}}{{admins}}{{/link-to}}</td>
<td class="value">{{#link-to 'adminUsersList.show' 'admins'}}{{admins}}{{/link-to}}</td>
<td class="title"><i class='fa fa-ban'></i> {{i18n admin.dashboard.suspended}}</td>
<td class="value">{{#link-to 'adminUsersList.suspended'}}{{suspended}}{{/link-to}}</td>
<td class="value">{{#link-to 'adminUsersList.show' 'suspended'}}{{suspended}}{{/link-to}}</td>
</tr>
<tr>
<td class="title"><i class='fa fa-shield'></i> {{i18n admin.dashboard.moderators}}</td>
<td class="value">{{#link-to 'adminUsersList.moderators'}}{{moderators}}{{/link-to}}</td>
<td class="value">{{#link-to 'adminUsersList.show' 'moderators'}}{{moderators}}{{/link-to}}</td>
<td class="title"><i class='fa fa-ban'></i> {{i18n admin.dashboard.blocked}}</td>
<td class="value">{{#link-to 'adminUsersList.blocked'}}{{blocked}}{{/link-to}}</td>
<td class="value">{{#link-to 'adminUsersList.show' 'blocked'}}{{blocked}}{{/link-to}}</td>
</tr>
</table>
</div>

View File

@ -1,8 +1,8 @@
<tr>
<td class="title">{{title}}</td>
<td class="value">{{#link-to 'adminUsersList.newuser'}}{{valueAtTrustLevel data 0}}{{/link-to}}</td>
<td class="value">{{#link-to 'adminUsersList.basicuser'}}{{valueAtTrustLevel data 1}}{{/link-to}}</td>
<td class="value">{{#link-to 'adminUsersList.regular'}}{{valueAtTrustLevel data 2}}{{/link-to}}</td>
<td class="value">{{#link-to 'adminUsersList.leaders'}}{{valueAtTrustLevel data 3}}{{/link-to}}</td>
<td class="value">{{#link-to 'adminUsersList.elders'}}{{valueAtTrustLevel data 4}}{{/link-to}}</td>
<td class="value">{{#link-to 'adminUsersList.show' 'newuser'}}{{valueAtTrustLevel data 0}}{{/link-to}}</td>
<td class="value">{{#link-to 'adminUsersList.show' 'basic'}}{{valueAtTrustLevel data 1}}{{/link-to}}</td>
<td class="value">{{#link-to 'adminUsersList.show' 'regular'}}{{valueAtTrustLevel data 2}}{{/link-to}}</td>
<td class="value">{{#link-to 'adminUsersList.show' 'leader'}}{{valueAtTrustLevel data 3}}{{/link-to}}</td>
<td class="value">{{#link-to 'adminUsersList.show' 'elder'}}{{valueAtTrustLevel data 4}}{{/link-to}}</td>
</tr>

View File

@ -2,7 +2,7 @@
<div class='span15'>
<ul class="nav nav-pills">
<li>{{#link-to 'adminUser' this}}<i class="fa fa-caret-left"></i> &nbsp;{{username}}{{/link-to}}</li>
<li>{{#link-to 'adminUsersList.regular'}}{{i18n admin.user.trust_level_2_users}}{{/link-to}}</li>
<li>{{#link-to 'adminUsersList.show' 'regular'}}{{i18n admin.user.trust_level_2_users}}{{/link-to}}</li>
</ul>
</div>
</div>

View File

@ -0,0 +1,84 @@
{{#if hasSelection}}
<div id='selected-controls'>
<button {{action "approveUsers"}} class='btn'>{{countI18n admin.users.approved_selected count=selectedCount}}</button>
<button {{action "rejectUsers"}} class='btn btn-danger'>{{countI18n admin.users.reject_selected count=selectedCount}}</button>
</div>
{{/if}}
<div class="admin-title">
<div class="pull-left">
<h2>{{title}}</h2>
</div>
</div>
<div class='username controls'>
{{text-field value=listFilter placeholder=searchHint}}
{{#unless showEmails}}
<div class="pull-right">
<button {{action "showEmails"}} class="btn">{{i18n admin.users.show_emails}}</button>
</div>
{{/unless}}
</div>
{{#loading-spinner condition=refreshing}}
{{#if model}}
<table class='table'>
<tr>
{{#if showApproval}}
<th>{{input type="checkbox" checked=selectAll}}</th>
{{/if}}
<th>&nbsp;</th>
<th>{{i18n username}}</th>
<th>{{i18n email}}</th>
<th>{{i18n admin.users.last_emailed}}</th>
<th>{{i18n last_seen}}</th>
<th>{{i18n admin.user.topics_entered}}</th>
<th>{{i18n admin.user.posts_read_count}}</th>
<th>{{i18n admin.user.time_read}}</th>
<th>{{i18n created}}</th>
{{#if showApproval}}
<th>{{i18n admin.users.approved}}</th>
{{/if}}
<th>&nbsp;</th>
</tr>
{{#each model}}
<tr {{bind-attr class="selected active::not-activated"}}>
{{#if controller.showApproval}}
<td>
{{#if can_approve}}
{{input type="checkbox" checked=selected}}
{{/if}}
</td>
{{/if}}
<td>{{#link-to 'adminUser' this}}{{avatar this imageSize="small"}}{{/link-to}}</td>
<td>{{#link-to 'adminUser' this}}{{unbound username}}{{/link-to}}</td>
<td>{{{unbound email}}}</td>
<td>{{{unbound last_emailed_age}}}</td>
<td>{{{unbound last_seen_age}}}</td>
<td>{{{unbound topics_entered}}}</td>
<td>{{{unbound posts_read_count}}}</td>
<td>{{{unbound time_read}}}</td>
<td>{{{unbound created_at_age}}}</td>
{{#if showApproval}}
<td>
{{#if approved}}
{{i18n yes_value}}
{{else}}
{{i18n no_value}}
{{/if}}
</td>
{{/if}}
<td>
{{#if admin}}<i class="fa fa-shield" title="{{i18n admin.title}}"></i>{{/if}}
{{#if moderator}}<i class="fa fa-shield" title="{{i18n admin.moderator}}"></i>{{/if}}
<td>
</tr>
{{/each}}
</table>
{{else}}
<p>{{i18n search.no_results}}</p>
{{/if}}
{{/loading-spinner}}

View File

@ -1,103 +1,22 @@
<div class='admin-controls'>
<div class='span15'>
<ul class="nav nav-pills">
<li>{{#link-to 'adminUsersList.active'}}{{i18n admin.users.nav.active}}{{/link-to}}</li>
<li>{{#link-to 'adminUsersList.new'}}{{i18n admin.users.nav.new}}{{/link-to}}</li>
{{#if mustApproveUsers}}
<li>{{#link-to 'adminUsersList.pending'}}{{i18n admin.users.nav.pending}}{{/link-to}}</li>
<li>{{#link-to 'adminUsersList.show' 'active'}}{{i18n admin.users.nav.active}}{{/link-to}}</li>
<li>{{#link-to 'adminUsersList.show' 'new'}}{{i18n admin.users.nav.new}}{{/link-to}}</li>
{{#if siteSettings.must_approve_users}}
<li>{{#link-to 'adminUsersList.show' 'pending'}}{{i18n admin.users.nav.pending}}{{/link-to}}</li>
{{/if}}
<li>{{#link-to 'adminUsersList.admins'}}{{i18n admin.users.nav.admins}}{{/link-to}}</li>
<li>{{#link-to 'adminUsersList.moderators'}}{{i18n admin.users.nav.moderators}}{{/link-to}}</li>
<li>{{#link-to 'adminUsersList.suspended'}}{{i18n admin.users.nav.suspended}}{{/link-to}}</li>
<li>{{#link-to 'adminUsersList.blocked'}}{{i18n admin.users.nav.blocked}}{{/link-to}}</li>
<li>{{#link-to 'adminUsersList.show' 'admins'}}{{i18n admin.users.nav.admins}}{{/link-to}}</li>
<li>{{#link-to 'adminUsersList.show' 'moderators'}}{{i18n admin.users.nav.moderators}}{{/link-to}}</li>
<li>{{#link-to 'adminUsersList.show' 'suspended'}}{{i18n admin.users.nav.suspended}}{{/link-to}}</li>
<li>{{#link-to 'adminUsersList.show' 'blocked'}}{{i18n admin.users.nav.blocked}}{{/link-to}}</li>
</ul>
</div>
<div class='username controls'>
{{text-field value=username placeholder=searchHint}}
</div>
<div class="pull-right">
<button {{action "exportUsers"}} class="btn" title="{{i18n admin.export_csv.users.title}}"><i class="fa fa-download"></i>{{i18n admin.export_csv.users.text}}</button>
</div>
</div>
<div class="admin-container">
{{#if hasSelection}}
<div id='selected-controls'>
<button {{action "approveUsers"}} class='btn'>{{countI18n admin.users.approved_selected countBinding="selectedCount"}}</button>
<button {{action "rejectUsers"}} class='btn btn-danger'>{{countI18n admin.users.reject_selected countBinding="selectedCount"}}</button>
</div>
{{/if}}
<div class="admin-title">
<div class="pull-left">
<h2>{{title}}</h2>
</div>
<div class="pull-right">
<button {{action "showEmails"}} class="btn">{{i18n admin.users.show_emails}}</button>
</div>
</div>
{{#loading-spinner condition=loading}}
{{#if model.length}}
<table class='table'>
<tr>
{{#if showApproval}}
<th>{{view Ember.Checkbox checkedBinding="selectAll"}}</th>
{{/if}}
<th>&nbsp;</th>
<th>{{i18n username}}</th>
<th>{{i18n email}}</th>
<th>{{i18n admin.users.last_emailed}}</th>
<th>{{i18n last_seen}}</th>
<th>{{i18n admin.user.topics_entered}}</th>
<th>{{i18n admin.user.posts_read_count}}</th>
<th>{{i18n admin.user.time_read}}</th>
<th>{{i18n created}}</th>
{{#if showApproval}}
<th>{{i18n admin.users.approved}}</th>
{{/if}}
<th>&nbsp;</th>
</tr>
{{#each model}}
<tr {{bind-attr class="selected active::not-activated"}}>
{{#if controller.showApproval}}
<td>
{{#if can_approve}}
{{view Ember.Checkbox checkedBinding="selected"}}
{{/if}}
</td>
{{/if}}
<td>{{#link-to 'adminUser' this}}{{avatar this imageSize="small"}}{{/link-to}}</td>
<td>{{#link-to 'adminUser' this}}{{unbound username}}{{/link-to}}</td>
<td>{{{unbound email}}}</td>
<td>{{{unbound last_emailed_age}}}</td>
<td>{{{unbound last_seen_age}}}</td>
<td>{{{unbound topics_entered}}}</td>
<td>{{{unbound posts_read_count}}}</td>
<td>{{{unbound time_read}}}</td>
<td>{{{unbound created_at_age}}}</td>
{{#if showApproval}}
<td>
{{#if approved}}
{{i18n yes_value}}
{{else}}
{{i18n no_value}}
{{/if}}
</td>
{{/if}}
<td>
{{#if admin}}<i class="fa fa-shield" title="{{i18n admin.title}}"></i>{{/if}}
{{#if moderator}}<i class="fa fa-shield" title="{{i18n admin.moderator}}"></i>{{/if}}
<td>
</tr>
{{/each}}
</table>
{{else}}
<p>{{i18n search.no_results}}</p>
{{/if}}
{{/loading-spinner}}
{{outlet}}
</div>

View File

@ -4,7 +4,7 @@ var classify = Ember.String.classify;
var get = Ember.get;
var LOADING_WHITELIST = ['badges', 'userActivity', 'userPrivateMessages', 'admin', 'adminFlags',
'user', 'preferences', 'adminEmail'],
'user', 'preferences', 'adminEmail', 'adminUsersList'],
_dummyRoute,
_loadingView;