diff --git a/app/assets/javascripts/admin/controllers/admin_groups_controller.js b/app/assets/javascripts/admin/controllers/admin_groups_controller.js
index 1d8f755c1..5ed0fbb31 100644
--- a/app/assets/javascripts/admin/controllers/admin_groups_controller.js
+++ b/app/assets/javascripts/admin/controllers/admin_groups_controller.js
@@ -1,9 +1,9 @@
-Discourse.AdminGroupsController = Ember.ArrayController.extend({
+Discourse.AdminGroupsController = Ember.Controller.extend({
itemController: 'adminGroup',
edit: function(group){
this.get('model').select(group);
- group.loadUsers();
+ group.load();
},
refreshAutoGroups: function(){
@@ -14,9 +14,31 @@ Discourse.AdminGroupsController = Ember.ArrayController.extend({
controller.set('model', Discourse.Group.findAll());
controller.set('refreshingAutoGroups',false);
});
+ },
+
+ newGroup: function(){
+ var group = Discourse.Group.create();
+ group.set("loaded", true);
+ var model = this.get("model");
+ model.addObject(group);
+ model.select(group);
+ },
+
+ save: function(group){
+ if(!group.get("id")){
+ group.create();
+ } else {
+ group.save();
+ }
+ },
+
+ destroy: function(group){
+ var list = this.get("model");
+ if(group.get("id")){
+ group.destroy().then(function(){
+ list.removeObject(group);
+ });
+ }
}
});
-Discourse.AdminGroupController = Ember.Controller.extend({
-
-});
diff --git a/app/assets/javascripts/admin/models/flagged_post.js b/app/assets/javascripts/admin/models/flagged_post.js
index 829a443b0..4b793e9cd 100644
--- a/app/assets/javascripts/admin/models/flagged_post.js
+++ b/app/assets/javascripts/admin/models/flagged_post.js
@@ -26,7 +26,8 @@ Discourse.FlaggedPost = Discourse.Post.extend({
if (a.message) {
return r.push({
user: _this.userLookup[a.user_id],
- message: a.message
+ message: a.message,
+ permalink: a.permalink
});
}
});
diff --git a/app/assets/javascripts/admin/models/group.js b/app/assets/javascripts/admin/models/group.js
index e56015359..39710ab24 100644
--- a/app/assets/javascripts/admin/models/group.js
+++ b/app/assets/javascripts/admin/models/group.js
@@ -1,4 +1,6 @@
Discourse.Group = Discourse.Model.extend({
+ loaded: false,
+
userCountDisplay: function(){
var c = this.get('user_count');
// don't display zero its ugly
@@ -7,16 +9,19 @@ Discourse.Group = Discourse.Model.extend({
}
}.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));
+ load: function() {
+ var id = this.get('id');
+ if(id && !this.get('loaded')) {
+ 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)
+ group.set('loaded', true)
});
- group.set('users', users)
- });
+ }
},
usernames: function() {
@@ -28,7 +33,32 @@ Discourse.Group = Discourse.Model.extend({
}).join(',')
}
return usernames;
- }.property('users')
+ }.property('users'),
+
+ destroy: function(){
+ var group = this;
+ group.set('disableSave', true);
+
+ return Discourse.ajax("/admin/groups/" + this.get("id"), {type: "DELETE"})
+ .then(function(){
+ group.set('disableSave', false);
+ });
+ },
+
+ create: function(){
+ var group = this;
+ group.set('disableSave', true);
+
+ return Discourse.ajax("/admin/groups", {type: "POST", data: {
+ group: {
+ name: this.get('name'),
+ usernames: this.get('usernames')
+ }
+ }}).then(function(r){
+ group.set('disableSave', false);
+ group.set('id', r.id);
+ });
+ }
});
diff --git a/app/assets/javascripts/admin/templates/flags.js.handlebars b/app/assets/javascripts/admin/templates/flags.js.handlebars
index 8efe148fd..92d75a863 100644
--- a/app/assets/javascripts/admin/templates/flags.js.handlebars
+++ b/app/assets/javascripts/admin/templates/flags.js.handlebars
@@ -38,7 +38,7 @@
|
- {{#linkTo 'adminUser' user}}{{avatar user imageSize="small"}}{{/linkTo}} {{message}}
+
|
|
|
diff --git a/app/assets/javascripts/admin/templates/groups.js.handlebars b/app/assets/javascripts/admin/templates/groups.js.handlebars
index 1832c1feb..89038c43e 100644
--- a/app/assets/javascripts/admin/templates/groups.js.handlebars
+++ b/app/assets/javascripts/admin/templates/groups.js.handlebars
@@ -1,5 +1,5 @@
-
+
{{i18n admin.groups.edit}}
@@ -9,19 +9,35 @@
{{/each}}
-
-
+
+
+
{{#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"}}
-
+ {{#if model.active.loaded}}
+ {{#with model.active}}
+ {{#if automatic}}
+
{{name}}
+ {{else}}
+ {{view Discourse.TextField valueBinding="name" placeholderKey="admin.groups.name_placeholder"}}
+ {{/if}}
- {{/with}}
+ {{view Discourse.UserSelector id="group-users" placeholderKey="admin.groups.selector_placeholder" tabindex="1" usernamesBinding="usernames"}}
+
+ {{/with}}
+ {{else}}
+
{{i18n loading}}
+ {{/if}}
{{else}}
nothing here yet
{{/if}}
diff --git a/app/assets/javascripts/discourse/models/selectable_array.js b/app/assets/javascripts/discourse/models/selectable_array.js
index eaa4c6bd4..a57d77b82 100644
--- a/app/assets/javascripts/discourse/models/selectable_array.js
+++ b/app/assets/javascripts/discourse/models/selectable_array.js
@@ -18,5 +18,13 @@ Discourse.SelectableArray = Em.ArrayProxy.extend({
}
});
this.set("active", selected);
+ },
+ removeObject: function(object) {
+ if(object === this.get("active")){
+ this.set("active", null);
+ Em.set(object, "active", false);
+ }
+
+ this._super(object);
}
});
diff --git a/app/assets/javascripts/discourse/templates/list/list.js.handlebars b/app/assets/javascripts/discourse/templates/list/list.js.handlebars
index da76a4f67..c0fd456d9 100644
--- a/app/assets/javascripts/discourse/templates/list/list.js.handlebars
+++ b/app/assets/javascripts/discourse/templates/list/list.js.handlebars
@@ -23,11 +23,11 @@
-
+
{{#if controller.loading}}
-
+
diff --git a/app/assets/javascripts/discourse/templates/topic_summary/private_message.js.handlebars b/app/assets/javascripts/discourse/templates/topic_summary/private_message.js.handlebars
index 473ece0ec..e5a316a16 100644
--- a/app/assets/javascripts/discourse/templates/topic_summary/private_message.js.handlebars
+++ b/app/assets/javascripts/discourse/templates/topic_summary/private_message.js.handlebars
@@ -1,5 +1,10 @@
{{i18n private_message_info.title}}
+ {{#each content.allowed_groups}}
+
+ #{{unbound name}}
+
+ {{/each}}
{{#each content.allowed_users}}
diff --git a/app/assets/javascripts/discourse/views/user_selector_view.js b/app/assets/javascripts/discourse/views/user_selector_view.js
index 9d484061f..ba344b537 100644
--- a/app/assets/javascripts/discourse/views/user_selector_view.js
+++ b/app/assets/javascripts/discourse/views/user_selector_view.js
@@ -1,5 +1,5 @@
Discourse.UserSelector = Discourse.TextField.extend({
-
+
didInsertElement: function(){
var _this = this;
var selected = [];
@@ -40,7 +40,7 @@ Discourse.UserSelector = Discourse.TextField.extend({
}
});
-
+
}
});
@@ -48,7 +48,7 @@ Discourse.UserSelector = Discourse.TextField.extend({
Discourse.UserSelector.reopenClass({
// I really want to move this into a template file, but I need a handlebars template here, not an ember one
- templateFunction: function(){
+ templateFunction: function(){
this.compiled = this.compiled || Handlebars.compile("" +
" " +
"{{#each options}}" +
diff --git a/app/assets/stylesheets/admin/admin_base.scss b/app/assets/stylesheets/admin/admin_base.scss
index c0b0a76c9..b99fc1043 100644
--- a/app/assets/stylesheets/admin/admin_base.scss
+++ b/app/assets/stylesheets/admin/admin_base.scss
@@ -654,3 +654,12 @@ table {
}
}
+
+.row.groups {
+ input[type='text'] {
+ width: 500px;
+ }
+ input#group-users {
+ width: 600px;
+ }
+}
diff --git a/app/assets/stylesheets/application/topic.css.scss b/app/assets/stylesheets/application/topic.css.scss
index c5f3c15c6..02cc7485e 100644
--- a/app/assets/stylesheets/application/topic.css.scss
+++ b/app/assets/stylesheets/application/topic.css.scss
@@ -451,7 +451,12 @@ kbd {
.private_message .participants .user {
float: left;
display: block;
+ line-height: 35px;
margin-right: 15px;
+ margin-top: 8px;
+ &.group {
+ color: black;
+ }
}
.posts-wrapper .spinner {
diff --git a/app/controllers/admin/flags_controller.rb b/app/controllers/admin/flags_controller.rb
index 2dad84a3d..5a9b00de0 100644
--- a/app/controllers/admin/flags_controller.rb
+++ b/app/controllers/admin/flags_controller.rb
@@ -56,20 +56,23 @@ limit 100
map[p["id"]] = p
}
- sql = SqlBuilder.new "select a.id, a.user_id, post_action_type_id, a.created_at, post_id, a.message
+ sql = SqlBuilder.new "select a.id, a.user_id, post_action_type_id, a.created_at, post_id, a.message, p.topic_id, t.slug
from post_actions a
+left join posts p on p.id = related_post_id
+left join topics t on t.id = p.topic_id
/*where*/
"
sql.where("post_action_type_id in (:flag_types)", flag_types: PostActionType.notify_flag_types.values)
sql.where("post_id in (:posts)", posts: posts.map{|p| p["id"].to_i})
if params[:filter] == 'old'
- sql.where('deleted_at is not null')
+ sql.where('a.deleted_at is not null')
else
- sql.where('deleted_at is null')
+ sql.where('a.deleted_at is null')
end
sql.exec.each do |action|
+ action["permalink"] = Topic.url(action["topic_id"],action["slug"]) if action["slug"].present?
p = map[action["post_id"]]
p[:post_actions] ||= []
p[:post_actions] << action
diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb
index 24db86baa..b4989d410 100644
--- a/app/controllers/admin/groups_controller.rb
+++ b/app/controllers/admin/groups_controller.rb
@@ -1,7 +1,7 @@
class Admin::GroupsController < Admin::AdminController
def index
groups = Group.order(:name).all
- render_serialized(groups, AdminGroupSerializer)
+ render_serialized(groups, BasicGroupSerializer)
end
def refresh_automatic_groups
@@ -11,7 +11,7 @@ class Admin::GroupsController < Admin::AdminController
def users
group = Group.find(params[:group_id].to_i)
- render_serialized(group.users, BasicUserSerializer)
+ render_serialized(group.users.limit(100).to_a, BasicUserSerializer)
end
def update
@@ -28,7 +28,7 @@ class Admin::GroupsController < Admin::AdminController
group.name = params[:group][:name]
group.usernames = params[:group][:usernames] if params[:group][:usernames]
group.save!
- render_serialized(group, AdminGroupSerializer)
+ render_serialized(group, BasicGroupSerializer)
end
def destroy
diff --git a/app/models/group.rb b/app/models/group.rb
index a568e95cd..c96248ebd 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -7,6 +7,8 @@ class Group < ActiveRecord::Base
after_save :destroy_deletions
+ validate :name_format_validator
+
AUTO_GROUPS = {
:admins => 1,
:moderators => 2,
@@ -26,8 +28,8 @@ class Group < ActiveRecord::Base
id = AUTO_GROUPS[name]
- unless group = self[name]
- group = Group.new(name: "", automatic: true)
+ unless group = self.lookup_group(name)
+ group = Group.new(name: name.to_s, automatic: true)
group.id = id
group.save!
end
@@ -61,6 +63,8 @@ class Group < ActiveRecord::Base
# we want to ensure consistency
Group.reset_counters(group.id, :group_users)
+
+ group
end
def self.refresh_automatic_groups!(*args)
@@ -73,9 +77,15 @@ class Group < ActiveRecord::Base
end
def self.[](name)
- raise ArgumentError, "unknown group" unless id = AUTO_GROUPS[name]
+ unless g = lookup_group(name)
+ g = refresh_automatic_group!(name)
+ end
+ g
+ end
- Group.where(id: id).first
+ def self.lookup_group(name)
+ raise ArgumentError, "unknown group" unless id = AUTO_GROUPS[name]
+ g = Group.where(id: id).first
end
@@ -84,7 +94,7 @@ class Group < ActiveRecord::Base
GroupUser.where(group_id: trust_group_ids, user_id: user_id).delete_all
- if group = Group[name]
+ if group = lookup_group(name)
group.group_users.build(user_id: user_id)
group.save!
else
@@ -92,6 +102,7 @@ class Group < ActiveRecord::Base
end
end
+
def self.builtin
Enum.new(:moderators, :admins, :trust_level_1, :trust_level_2)
end
@@ -131,9 +142,15 @@ class Group < ActiveRecord::Base
def add(user)
self.users.push(user)
end
-
protected
+ def name_format_validator
+ validator = UsernameValidator.new(name)
+ unless validator.valid_format?
+ validator.errors.each { |e| errors.add(:name, e) }
+ end
+ end
+
# hack around AR
def destroy_deletions
if @deletions
diff --git a/app/models/post_action.rb b/app/models/post_action.rb
index 59b3cd12b..766d072ab 100644
--- a/app/models/post_action.rb
+++ b/app/models/post_action.rb
@@ -74,12 +74,16 @@ class PostAction < ActiveRecord::Base
def self.act(user, post, post_action_type_id, message = nil)
begin
- title, target_usernames, subtype, body = nil
+ title, target_usernames, target_group_names, subtype, body = nil
if message
[:notify_moderators, :notify_user].each do |k|
if post_action_type_id == PostActionType.types[k]
- target_usernames = k == :notify_moderators ? target_moderators(user) : post.user.username
+ if k == :notify_moderators
+ target_group_names = target_moderators
+ else
+ target_usernames = post.user.username
+ end
title = I18n.t("post_action_types.#{k}.email_title",
title: post.topic.title)
body = I18n.t("post_action_types.#{k}.email_body",
@@ -91,9 +95,10 @@ class PostAction < ActiveRecord::Base
end
related_post_id = nil
- if target_usernames.present?
+ if target_usernames.present? || target_group_names.present?
related_post_id = PostCreator.new(user,
target_usernames: target_usernames,
+ target_group_names: target_group_names,
archetype: Archetype.private_message,
subtype: subtype,
title: title,
@@ -209,13 +214,8 @@ class PostAction < ActiveRecord::Base
protected
- def self.target_moderators(me)
- User
- .where("moderator = 't' or admin = 't'")
- .where('id <> ?', [me.id])
- .select('username')
- .map{|u| u.username}
- .join(',')
+ def self.target_moderators
+ Group[:moderators].name
end
end
diff --git a/app/models/topic.rb b/app/models/topic.rb
index eef3bb19a..436352ef3 100644
--- a/app/models/topic.rb
+++ b/app/models/topic.rb
@@ -641,9 +641,16 @@ class Topic < ActiveRecord::Base
"/t/#{slug}/#{id}/#{posts_count}"
end
+
+ def self.url(id, slug, post_number=nil)
+ url = "#{Discourse.base_url}/t/#{slug}/#{id}"
+ url << "/#{post_number}" if post_number.to_i > 1
+ url
+ end
+
def relative_url(post_number=nil)
url = "/t/#{slug}/#{id}"
- url << "/#{post_number}" if post_number.present? && post_number.to_i > 1
+ url << "/#{post_number}" if post_number.to_i > 1
url
end
diff --git a/app/serializers/admin_group_serializer.rb b/app/serializers/basic_group_serializer.rb
similarity index 50%
rename from app/serializers/admin_group_serializer.rb
rename to app/serializers/basic_group_serializer.rb
index bb62c38b1..81216da68 100644
--- a/app/serializers/admin_group_serializer.rb
+++ b/app/serializers/basic_group_serializer.rb
@@ -1,3 +1,3 @@
-class AdminGroupSerializer < ApplicationSerializer
+class BasicGroupSerializer < ApplicationSerializer
attributes :id, :automatic, :name, :user_count
end
diff --git a/app/serializers/topic_view_serializer.rb b/app/serializers/topic_view_serializer.rb
index c1e9084a9..ef3753a66 100644
--- a/app/serializers/topic_view_serializer.rb
+++ b/app/serializers/topic_view_serializer.rb
@@ -50,6 +50,7 @@ class TopicViewSerializer < ApplicationSerializer
has_one :created_by, serializer: BasicUserSerializer, embed: :objects
has_one :last_poster, serializer: BasicUserSerializer, embed: :objects
has_many :allowed_users, serializer: BasicUserSerializer, embed: :objects
+ has_many :allowed_groups, serializer: BasicGroupSerializer, embed: :objects
has_many :links, serializer: TopicLinkSerializer, embed: :objects
has_many :participants, serializer: TopicPostCountSerializer, embed: :objects
@@ -172,6 +173,10 @@ class TopicViewSerializer < ApplicationSerializer
object.topic.allowed_users
end
+ def allowed_groups
+ object.topic.allowed_groups
+ end
+
def include_links?
object.links.present?
end
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index 2e07d9168..31fbcb2c0 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -895,11 +895,13 @@ en:
delete_title: "delete post (if its the first post delete topic)"
flagged_by: "Flagged by"
error: "Something went wrong"
+ view_message: "view message"
groups:
title: "Groups"
edit: "Edit Groups"
selector_placeholder: "add users"
+ name_placeholder: "Group name, no spaces, same as username rule"
api:
title: "API"
diff --git a/config/locales/server.es.yml b/config/locales/server.es.yml
old mode 100755
new mode 100644
diff --git a/db/migrate/20130509040248_update_sequence_for_groups.rb b/db/migrate/20130509040248_update_sequence_for_groups.rb
new file mode 100644
index 000000000..0c0c70b68
--- /dev/null
+++ b/db/migrate/20130509040248_update_sequence_for_groups.rb
@@ -0,0 +1,11 @@
+class UpdateSequenceForGroups < ActiveRecord::Migration
+ def up
+ # even if you alter a sequence you still need to set the seq
+ execute < |