From 6b536dcde533a0b2692fe8e763d2a956854a028b Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 8 May 2013 15:20:38 +1000 Subject: [PATCH] work in progress ... groups --- .../controllers/admin_groups_controller.js | 18 ++++++-- app/assets/javascripts/admin/models/group.js | 41 +++++++++++++++++-- .../admin/routes/admin_groups_routes.js | 7 ++-- .../admin/templates/groups.js.handlebars | 15 ++++--- .../discourse/models/selectable_array.js | 5 ++- app/assets/stylesheets/admin/admin_base.scss | 11 +++++ app/controllers/admin/groups_controller.rb | 15 +++++++ app/models/group.rb | 9 +++- app/models/group_user.rb | 2 +- app/serializers/admin_group_serializer.rb | 3 ++ config/locales/server.en.yml | 11 +++++ config/routes.rb | 8 +++- ...20130508040235_add_user_count_to_groups.rb | 5 +++ lib/jobs/ensure_db_consistency.rb | 3 +- .../admin/groups_controller_spec.rb | 31 ++++++++++++++ spec/models/group_spec.rb | 26 ++++++++++++ 16 files changed, 189 insertions(+), 21 deletions(-) create mode 100644 app/serializers/admin_group_serializer.rb create mode 100644 db/migrate/20130508040235_add_user_count_to_groups.rb create mode 100644 spec/controllers/admin/groups_controller_spec.rb diff --git a/app/assets/javascripts/admin/controllers/admin_groups_controller.js b/app/assets/javascripts/admin/controllers/admin_groups_controller.js index e0b75168d2a..1d8f755c1f0 100644 --- a/app/assets/javascripts/admin/controllers/admin_groups_controller.js +++ b/app/assets/javascripts/admin/controllers/admin_groups_controller.js @@ -1,10 +1,22 @@ Discourse.AdminGroupsController = Ember.ArrayController.extend({ itemController: 'adminGroup', - edit: function(action){ - this.get('content').select(action); + + edit: function(group){ + this.get('model').select(group); + group.loadUsers(); + }, + + refreshAutoGroups: function(){ + var controller = this; + + this.set('refreshingAutoGroups', true); + Discourse.ajax('/admin/groups/refresh_automatic_groups', {type: 'POST'}).then(function(){ + controller.set('model', Discourse.Group.findAll()); + controller.set('refreshingAutoGroups',false); + }); } }); -Discourse.AdminGroupController = Ember.ObjectController.extend({ +Discourse.AdminGroupController = Ember.Controller.extend({ }); diff --git a/app/assets/javascripts/admin/models/group.js b/app/assets/javascripts/admin/models/group.js index 70e13815b2a..e5601535935 100644 --- a/app/assets/javascripts/admin/models/group.js +++ b/app/assets/javascripts/admin/models/group.js @@ -1,4 +1,34 @@ Discourse.Group = Discourse.Model.extend({ + userCountDisplay: function(){ + var c = this.get('user_count'); + // don't display zero its ugly + if(c > 0) { + return c; + } + }.property('user_count'), + + loadUsers: function() { + var group = this; + + Discourse.ajax('/admin/groups/' + this.get('id') + '/users').then(function(payload){ + var users = Em.A() + payload.each(function(user){ + users.addObject(Discourse.User.create(user)); + }); + group.set('users', users) + }); + }, + + usernames: function() { + var users = this.get('users'); + var usernames = ""; + if(users) { + usernames = $.map(users, function(user){ + return user.get('username'); + }).join(',') + } + return usernames; + }.property('users') }); @@ -6,19 +36,22 @@ Discourse.Group.reopenClass({ findAll: function(){ var list = Discourse.SelectableArray.create(); - list.addObject(Discourse.Group.create({id: 1, name: "all mods", members: ["A","b","c"]})); - list.addObject(Discourse.Group.create({id: 2, name: "other mods", members: ["A","b","c"]})); + Discourse.ajax("/admin/groups").then(function(groups){ + groups.each(function(group){ + list.addObject(Discourse.Group.create(group)); + }); + }); return list; }, find: function(id) { var promise = new Em.Deferred(); - + setTimeout(function(){ promise.resolve(Discourse.Group.create({id: 1, name: "all mods", members: ["A","b","c"]})); }, 1000); - + return promise; } }); diff --git a/app/assets/javascripts/admin/routes/admin_groups_routes.js b/app/assets/javascripts/admin/routes/admin_groups_routes.js index a0f7022d8ea..535c280e6bd 100644 --- a/app/assets/javascripts/admin/routes/admin_groups_routes.js +++ b/app/assets/javascripts/admin/routes/admin_groups_routes.js @@ -1,9 +1,10 @@ Discourse.AdminGroupsRoute = Discourse.Route.extend({ - model: function() { - return Discourse.Group.findAll(); - }, renderTemplate: function() { this.render('admin/templates/groups',{into: 'admin/templates/admin'}); + }, + + setupController: function(controller, model) { + controller.set('model', Discourse.Group.findAll()); } }); diff --git a/app/assets/javascripts/admin/templates/groups.js.handlebars b/app/assets/javascripts/admin/templates/groups.js.handlebars index f5a53ea524f..1832c1feb74 100644 --- a/app/assets/javascripts/admin/templates/groups.js.handlebars +++ b/app/assets/javascripts/admin/templates/groups.js.handlebars @@ -3,20 +3,23 @@

{{i18n admin.groups.edit}}

+
+ +
- {{#if content.active}} - {{#with content.active}} -

{{name}}

+ {{#if model.active}} + {{#with model.active}} +

{{name}}

{{view Discourse.UserSelector id="private-message-users" class="span8" placeholderKey="admin.groups.selector_placeholder" tabindex="1" usernamesBinding="usernames"}} - + {{/with}} {{else}} diff --git a/app/assets/javascripts/discourse/models/selectable_array.js b/app/assets/javascripts/discourse/models/selectable_array.js index 733ea685630..eaa4c6bd404 100644 --- a/app/assets/javascripts/discourse/models/selectable_array.js +++ b/app/assets/javascripts/discourse/models/selectable_array.js @@ -1,6 +1,9 @@ // this allows you to track the selected item in an array, ghetto for now Discourse.SelectableArray = Em.ArrayProxy.extend({ - content: [], + init: function() { + this.content = []; + this._super(); + }, selectIndex: function(index){ this.select(this[index]); }, diff --git a/app/assets/stylesheets/admin/admin_base.scss b/app/assets/stylesheets/admin/admin_base.scss index cf7edbfdbbb..c0b0a76c941 100644 --- a/app/assets/stylesheets/admin/admin_base.scss +++ b/app/assets/stylesheets/admin/admin_base.scss @@ -3,6 +3,17 @@ @import "foundation/mixins"; @import "foundation/helpers"; + +.content-list li a span.count { + font-size: 12px; + float: right; + margin-right: 10px; + background-color: #eee; + padding: 2px 5px; + border-radius: 5px; + color: #555; +} + .admin-content { margin-bottom: 50px; .admin-contents { diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb index 12702c4de59..08d8cfd2000 100644 --- a/app/controllers/admin/groups_controller.rb +++ b/app/controllers/admin/groups_controller.rb @@ -1,4 +1,19 @@ class Admin::GroupsController < Admin::AdminController + def index + groups = Group.order(:name).all + render_serialized(groups, AdminGroupSerializer) + end + + def refresh_automatic_groups + Group.refresh_automatic_groups! + render json: "ok" + end + def show end + + def users + group = Group.find(params[:group_id].to_i) + render_serialized(group.users, BasicUserSerializer) + end end diff --git a/app/models/group.rb b/app/models/group.rb index 970c6e5312c..22830f2d3d9 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -25,11 +25,12 @@ class Group < ActiveRecord::Base id = AUTO_GROUPS[name] unless group = self[name] - group = Group.new(name: name.to_s, automatic: true) + group = Group.new(name: "", automatic: true) group.id = id group.save! end + group.name = I18n.t("groups.default_names.#{name}") real_ids = case name when :admins @@ -55,9 +56,15 @@ class Group < ActiveRecord::Base end group.save! + + # we want to ensure consistency + Group.reset_counters(group.id, :group_users) end def self.refresh_automatic_groups!(*args) + if args.length == 0 + args = AUTO_GROUPS.map{|k,v| k} + end args.each do |group| refresh_automatic_group!(group) end diff --git a/app/models/group_user.rb b/app/models/group_user.rb index 0c93905c8aa..4b87ec1f21c 100644 --- a/app/models/group_user.rb +++ b/app/models/group_user.rb @@ -1,4 +1,4 @@ class GroupUser < ActiveRecord::Base - belongs_to :group + belongs_to :group, counter_cache: "user_count" belongs_to :user end diff --git a/app/serializers/admin_group_serializer.rb b/app/serializers/admin_group_serializer.rb new file mode 100644 index 00000000000..bb62c38b1d6 --- /dev/null +++ b/app/serializers/admin_group_serializer.rb @@ -0,0 +1,3 @@ +class AdminGroupSerializer < ApplicationSerializer + attributes :id, :automatic, :name, :user_count +end diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 8a1e4c7f0d6..07aea4be8a5 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -44,6 +44,17 @@ en: rss_topics_in_category: "RSS feed of topics in the '%{category}' category" author_wrote: "%{author} wrote:" + groups: + default_names: + admins: "admins" + moderators: "moderators" + staff: "staff" + trust_level_1: "trust_level_1" + trust_level_2: "trust_level_2" + trust_level_3: "trust_level_3" + trust_level_4: "trust_level_4" + trust_level_5: "trust_level_5" + education: until_posts: one: "post" diff --git a/config/routes.rb b/config/routes.rb index 611fca97d56..ac02614b0bd 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -29,7 +29,13 @@ Discourse::Application.routes.draw do get 'reports/:type' => 'reports#show' - resources :groups, constraints: AdminConstraint.new + resources :groups, constraints: AdminConstraint.new do + collection do + post 'refresh_automatic_groups' => 'groups#refresh_automatic_groups' + end + get 'users' + end + resources :users, id: USERNAME_ROUTE_FORMAT do collection do get 'list/:query' => 'users#index' diff --git a/db/migrate/20130508040235_add_user_count_to_groups.rb b/db/migrate/20130508040235_add_user_count_to_groups.rb new file mode 100644 index 00000000000..cae58bc21e3 --- /dev/null +++ b/db/migrate/20130508040235_add_user_count_to_groups.rb @@ -0,0 +1,5 @@ +class AddUserCountToGroups < ActiveRecord::Migration + def change + add_column :groups, :user_count, :integer, null: false, default: 0 + end +end diff --git a/lib/jobs/ensure_db_consistency.rb b/lib/jobs/ensure_db_consistency.rb index df414823bf3..154fdf62298 100644 --- a/lib/jobs/ensure_db_consistency.rb +++ b/lib/jobs/ensure_db_consistency.rb @@ -1,9 +1,10 @@ module Jobs - # checks to see if any users need to be promoted + # various consistency checks class EnsureDbConsistency < Jobs::Base def execute(args) TopicUser.ensure_consistency! UserVisit.ensure_consistency! + Group.refresh_automatic_groups! end end end diff --git a/spec/controllers/admin/groups_controller_spec.rb b/spec/controllers/admin/groups_controller_spec.rb new file mode 100644 index 00000000000..b9a85b6389d --- /dev/null +++ b/spec/controllers/admin/groups_controller_spec.rb @@ -0,0 +1,31 @@ +require 'spec_helper' + +describe Admin::GroupsController do + it "is a subclass of AdminController" do + (Admin::GroupsController < Admin::AdminController).should be_true + end + + it "produces valid json for groups" do + admin = log_in(:admin) + group = Fabricate.build(:group, name: "test") + group.add(admin) + group.save + + xhr :get, :index + response.status.should == 200 + ::JSON.parse(response.body).should == [{ + "id"=>group.id, + "name"=>group.name, + "user_count"=>1, + "automatic"=>false + }] + end + + it "is able to refresh automatic groups" do + admin = log_in(:admin) + Group.expects(:refresh_automatic_groups!).returns(true) + + xhr :post, :refresh_automatic_groups + response.status.should == 200 + end +end diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 1236fb4404d..9760927fc17 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -54,4 +54,30 @@ describe Group do Group[:trust_level_2].user_ids.sort.should == [user.id, user2.id].sort end + it "Correctly updates all automatic groups upon request" do + admin = Fabricate(:admin) + user = Fabricate(:user) + user.change_trust_level!(:regular) + + Group.exec_sql("update groups set user_count=0 where id = #{Group::AUTO_GROUPS[:trust_level_2]}") + + Group.refresh_automatic_groups! + + groups = Group.includes(:users).to_a + groups.count.should == Group::AUTO_GROUPS.count + + g = groups.find{|g| g.id == Group::AUTO_GROUPS[:admins]} + g.users.count.should == 1 + g.user_count.should == 1 + + g = groups.find{|g| g.id == Group::AUTO_GROUPS[:staff]} + g.users.count.should == 1 + g.user_count.should == 1 + + g = groups.find{|g| g.id == Group::AUTO_GROUPS[:trust_level_2]} + g.users.count.should == 1 + g.user_count.should == 1 + + end + end