diff --git a/app/assets/javascripts/discourse/controllers/group.js.es6 b/app/assets/javascripts/discourse/controllers/group.js.es6
index dcd408673dc..fa85decd355 100644
--- a/app/assets/javascripts/discourse/controllers/group.js.es6
+++ b/app/assets/javascripts/discourse/controllers/group.js.es6
@@ -1,9 +1,49 @@
+import { default as computed, observes } from 'ember-addons/ember-computed-decorators';
+
+var Tab = Em.Object.extend({
+ @computed('name')
+ location(name) {
+ return 'group.' + name;
+ }
+});
+
+
export default Ember.Controller.extend({
counts: null,
- showing: null,
+ showing: 'posts',
- // It would be nice if bootstrap marked action lists as selected when their links
- // were 'active' not the `li` tags.
- showingIndex: Em.computed.equal('showing', 'index'),
- showingMembers: Em.computed.equal('showing', 'members')
+ @observes('counts')
+ countsChanged() {
+ const counts = this.get('counts');
+ this.get('tabs').forEach(tab => {
+ tab.set('count', counts.get(tab.get('name')));
+ });
+ },
+
+ @observes('showing')
+ showingChanged() {
+ const showing = this.get('showing');
+
+ this.get('tabs').forEach(tab => {
+ tab.set('active', showing === tab.get('name'));
+ });
+
+ },
+
+ tabs: [
+ Tab.create({
+ active: true,
+ name: 'posts',
+ 'location': 'group.index'
+ }),
+ Tab.create({
+ name: 'topics'
+ }),
+ Tab.create({
+ name: 'mentions'
+ }),
+ Tab.create({
+ name: 'members'
+ })
+ ]
});
diff --git a/app/assets/javascripts/discourse/controllers/group/index.js.es6 b/app/assets/javascripts/discourse/controllers/group/index.js.es6
index 7cac5408459..60df6a2cdf1 100644
--- a/app/assets/javascripts/discourse/controllers/group/index.js.es6
+++ b/app/assets/javascripts/discourse/controllers/group/index.js.es6
@@ -1,10 +1,5 @@
/**
Handles displaying posts within a group
-
- @class GroupIndexController
- @extends Ember.ArrayController
- @namespace Discourse
- @module Discourse
**/
export default Ember.ArrayController.extend({
needs: ['group'],
@@ -21,7 +16,8 @@ export default Ember.ArrayController.extend({
var lastPostId = posts[posts.length-1].get('id'),
group = this.get('controllers.group.model');
- group.findPosts({beforePostId: lastPostId}).then(function(newPosts) {
+ var opts = {beforePostId: lastPostId, type: this.get('type')};
+ group.findPosts(opts).then(function(newPosts) {
posts.addObjects(newPosts);
self.set('loading', false);
});
diff --git a/app/assets/javascripts/discourse/controllers/group/mentions.js.es6 b/app/assets/javascripts/discourse/controllers/group/mentions.js.es6
new file mode 100644
index 00000000000..81fa5a8ffda
--- /dev/null
+++ b/app/assets/javascripts/discourse/controllers/group/mentions.js.es6
@@ -0,0 +1,3 @@
+import IndexController from 'discourse/controllers/group/index';
+
+export default IndexController.extend({type: 'mentions'});
diff --git a/app/assets/javascripts/discourse/controllers/group/topics.js.es6 b/app/assets/javascripts/discourse/controllers/group/topics.js.es6
new file mode 100644
index 00000000000..9423350320d
--- /dev/null
+++ b/app/assets/javascripts/discourse/controllers/group/topics.js.es6
@@ -0,0 +1,3 @@
+import IndexController from 'discourse/controllers/group/index';
+
+export default IndexController.extend({type: 'topics'});
diff --git a/app/assets/javascripts/discourse/models/group.js.es6 b/app/assets/javascripts/discourse/models/group.js.es6
index 49f0e4ae0d6..b8979d2b78f 100644
--- a/app/assets/javascripts/discourse/models/group.js.es6
+++ b/app/assets/javascripts/discourse/models/group.js.es6
@@ -121,10 +121,12 @@ const Group = Discourse.Model.extend({
findPosts(opts) {
opts = opts || {};
+ const type = opts['type'] || 'posts';
+
var data = {};
if (opts.beforePostId) { data.before_post_id = opts.beforePostId; }
- return Discourse.ajax("/groups/" + this.get('name') + "/posts.json", { data: data }).then(function (posts) {
+ return Discourse.ajax(`/groups/${this.get('name')}/${type}.json`, { data: data }).then(function (posts) {
return posts.map(function (p) {
p.user = Discourse.User.create(p.user);
return Em.Object.create(p);
diff --git a/app/assets/javascripts/discourse/routes/app-route-map.js.es6 b/app/assets/javascripts/discourse/routes/app-route-map.js.es6
index 64078554fd2..e76b33296fa 100644
--- a/app/assets/javascripts/discourse/routes/app-route-map.js.es6
+++ b/app/assets/javascripts/discourse/routes/app-route-map.js.es6
@@ -48,6 +48,8 @@ export default function() {
});
this.resource('group', { path: '/groups/:name' }, function() {
+ this.route('topics');
+ this.route('mentions');
this.route('members');
});
diff --git a/app/assets/javascripts/discourse/routes/group-index.js.es6 b/app/assets/javascripts/discourse/routes/group-index.js.es6
index 89dec1edd2b..bb8ecaeb03c 100644
--- a/app/assets/javascripts/discourse/routes/group-index.js.es6
+++ b/app/assets/javascripts/discourse/routes/group-index.js.es6
@@ -9,6 +9,6 @@ export default Discourse.Route.extend({
setupController(controller, model) {
controller.set("model", model);
- this.controllerFor("group").set("showing", "index");
+ this.controllerFor("group").set("showing", "posts");
}
});
diff --git a/app/assets/javascripts/discourse/routes/group-mentions.js.es6 b/app/assets/javascripts/discourse/routes/group-mentions.js.es6
new file mode 100644
index 00000000000..1054fb18d3a
--- /dev/null
+++ b/app/assets/javascripts/discourse/routes/group-mentions.js.es6
@@ -0,0 +1,11 @@
+export default Discourse.Route.extend({
+
+ model() {
+ return this.modelFor("group").findPosts({type: 'mentions'});
+ },
+
+ setupController(controller, model) {
+ controller.set("model", model);
+ this.controllerFor("group").set("showing", "mentions");
+ }
+});
diff --git a/app/assets/javascripts/discourse/routes/group-topics.js.es6 b/app/assets/javascripts/discourse/routes/group-topics.js.es6
new file mode 100644
index 00000000000..397572e77bf
--- /dev/null
+++ b/app/assets/javascripts/discourse/routes/group-topics.js.es6
@@ -0,0 +1,11 @@
+export default Discourse.Route.extend({
+
+ model() {
+ return this.modelFor("group").findPosts({type: 'topics'});
+ },
+
+ setupController(controller, model) {
+ controller.set("model", model);
+ this.controllerFor("group").set("showing", "topics");
+ }
+});
diff --git a/app/assets/javascripts/discourse/templates/components/group-post.hbs b/app/assets/javascripts/discourse/templates/components/group-post.hbs
new file mode 100644
index 00000000000..78abb6b538e
--- /dev/null
+++ b/app/assets/javascripts/discourse/templates/components/group-post.hbs
@@ -0,0 +1,18 @@
+
+
+
+ {{{unbound post.cooked}}}
+
+
diff --git a/app/assets/javascripts/discourse/templates/group.hbs b/app/assets/javascripts/discourse/templates/group.hbs
index 67455f5f94e..22cd3d6fea4 100644
--- a/app/assets/javascripts/discourse/templates/group.hbs
+++ b/app/assets/javascripts/discourse/templates/group.hbs
@@ -1,28 +1,26 @@
-
-
- -
- {{#link-to 'group.index' model}}{{i18n 'groups.posts'}}
- ({{counts.posts}})
- {{/link-to}}
-
- -
- {{#link-to 'group.members' model}}{{i18n 'groups.members'}}
- ({{counts.members}})
+
+
+ {{#each tabs as |tab|}}
+ -
+ {{#link-to tab.location model}}{{tab.name}}
+ {{#if tab.count}}
+ ({{tab.count}})
+ {{/if}}
{{/link-to}}
+ {{/each}}
-
diff --git a/app/assets/javascripts/discourse/templates/group/index.hbs b/app/assets/javascripts/discourse/templates/group/index.hbs
index 5ed985396c7..8aab2faec65 100644
--- a/app/assets/javascripts/discourse/templates/group/index.hbs
+++ b/app/assets/javascripts/discourse/templates/group/index.hbs
@@ -1,22 +1,5 @@
- {{#each p in controller}}
-
-
-
- {{{unbound p.cooked}}}
-
-
+ {{#each controller as |post|}}
+ {{group-post post=post}}
{{/each}}
diff --git a/app/assets/javascripts/discourse/templates/group/members.hbs b/app/assets/javascripts/discourse/templates/group/members.hbs
index 405125993f3..7d91ec3e911 100644
--- a/app/assets/javascripts/discourse/templates/group/members.hbs
+++ b/app/assets/javascripts/discourse/templates/group/members.hbs
@@ -20,7 +20,7 @@
{{user-small user=m}}
{{#if m.owner}}
{{i18n "groups.owner"}}
- {{/if}}
+ {{/if}}
|
{{bound-date m.last_posted_at}}
diff --git a/app/assets/javascripts/discourse/templates/group/mentions.hbs b/app/assets/javascripts/discourse/templates/group/mentions.hbs
new file mode 100644
index 00000000000..49dabf33efc
--- /dev/null
+++ b/app/assets/javascripts/discourse/templates/group/mentions.hbs
@@ -0,0 +1,6 @@
+
+ {{#each controller as |post|}}
+ {{group-post post=post}}
+ {{/each}}
+
+
diff --git a/app/assets/javascripts/discourse/templates/group/topics.hbs b/app/assets/javascripts/discourse/templates/group/topics.hbs
new file mode 100644
index 00000000000..8aab2faec65
--- /dev/null
+++ b/app/assets/javascripts/discourse/templates/group/topics.hbs
@@ -0,0 +1,5 @@
+
+ {{#each controller as |post|}}
+ {{group-post post=post}}
+ {{/each}}
+
diff --git a/app/assets/javascripts/discourse/views/group-mentions.js.es6 b/app/assets/javascripts/discourse/views/group-mentions.js.es6
new file mode 100644
index 00000000000..4e25173395f
--- /dev/null
+++ b/app/assets/javascripts/discourse/views/group-mentions.js.es6
@@ -0,0 +1,6 @@
+import ScrollTop from 'discourse/mixins/scroll-top';
+import LoadMore from "discourse/mixins/load-more";
+
+export default Ember.View.extend(ScrollTop, LoadMore, {
+ eyelineSelector: '.user-stream .item',
+});
diff --git a/app/assets/javascripts/discourse/views/group-topics.js.es6 b/app/assets/javascripts/discourse/views/group-topics.js.es6
new file mode 100644
index 00000000000..4e25173395f
--- /dev/null
+++ b/app/assets/javascripts/discourse/views/group-topics.js.es6
@@ -0,0 +1,6 @@
+import ScrollTop from 'discourse/mixins/scroll-top';
+import LoadMore from "discourse/mixins/load-more";
+
+export default Ember.View.extend(ScrollTop, LoadMore, {
+ eyelineSelector: '.user-stream .item',
+});
diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb
index 605d04b7d25..f9a7dfcba16 100644
--- a/app/controllers/groups_controller.rb
+++ b/app/controllers/groups_controller.rb
@@ -7,6 +7,8 @@ class GroupsController < ApplicationController
def counts
group = find_group(:group_id)
render json: {counts: { posts: group.posts_for(guardian).count,
+ topics: group.posts_for(guardian).where(post_number: 1).count,
+ mentions: group.mentioned_posts_for(guardian).count,
members: group.users.count } }
end
@@ -16,6 +18,19 @@ class GroupsController < ApplicationController
render_serialized posts.to_a, GroupPostSerializer
end
+ def topics
+ group = find_group(:group_id)
+ posts = group.posts_for(guardian, params[:before_post_id]).where(post_number: 1).limit(20)
+ render_serialized posts.to_a, GroupPostSerializer
+ end
+
+ def mentions
+ group = find_group(:group_id)
+ posts = group.mentioned_posts_for(guardian, params[:before_post_id]).limit(20)
+ render_serialized posts.to_a, GroupPostSerializer
+ end
+
+
def members
group = find_group(:group_id)
diff --git a/app/models/group.rb b/app/models/group.rb
index acb7945d428..82ab38952e8 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -3,6 +3,7 @@ class Group < ActiveRecord::Base
has_many :category_groups, dependent: :destroy
has_many :group_users, dependent: :destroy
+ has_many :group_mentions, dependent: :destroy
has_many :categories, through: :category_groups
has_many :users, through: :group_users
@@ -80,6 +81,18 @@ class Group < ActiveRecord::Base
result.order('posts.created_at desc')
end
+ def mentioned_posts_for(guardian, before_post_id=nil)
+ result = Post.joins(:group_mentions)
+ .includes(:user, :topic, :topic => :category)
+ .references(:posts, :topics, :category)
+ .where('topics.archetype <> ?', Archetype.private_message)
+ .where(post_type: Post.types[:regular])
+
+ result = guardian.filter_allowed_categories(result)
+ result = result.where('posts.id < ?', before_post_id) if before_post_id
+ result.order('posts.created_at desc')
+ end
+
def self.trust_group_ids
(10..19).to_a
end
diff --git a/app/models/group_mention.rb b/app/models/group_mention.rb
new file mode 100644
index 00000000000..65faeb6f03f
--- /dev/null
+++ b/app/models/group_mention.rb
@@ -0,0 +1,4 @@
+class GroupMention < ActiveRecord::Base
+ belongs_to :post
+ belongs_to :group
+end
diff --git a/app/models/post.rb b/app/models/post.rb
index 5d121f39736..7b3a0b3c8d8 100644
--- a/app/models/post.rb
+++ b/app/models/post.rb
@@ -32,6 +32,7 @@ class Post < ActiveRecord::Base
has_many :replies, through: :post_replies
has_many :post_actions
has_many :topic_links
+ has_many :group_mentions, dependent: :destroy
has_many :post_uploads
has_many :uploads, through: :post_uploads
diff --git a/app/services/post_alerter.rb b/app/services/post_alerter.rb
index a8d7f20fdfc..4f5c0d58c9f 100644
--- a/app/services/post_alerter.rb
+++ b/app/services/post_alerter.rb
@@ -64,6 +64,16 @@ class PostAlerter
notify_post_users(post, notified)
end
+ sync_group_mentions(post, mentioned_groups)
+ end
+
+ def sync_group_mentions(post, mentioned_groups)
+ GroupMention.where(post_id: post.id).destroy_all
+ return if mentioned_groups.blank?
+
+ mentioned_groups.each do |group|
+ GroupMention.create(post_id: post.id, group_id: group.id)
+ end
end
def unread_posts(user, topic)
diff --git a/config/routes.rb b/config/routes.rb
index ad61e068234..afbfc21954a 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -342,6 +342,8 @@ Discourse::Application.routes.draw do
resources :groups do
get 'members'
get 'posts'
+ get 'topics'
+ get 'mentions'
get 'counts'
member do
diff --git a/db/migrate/20151201035631_add_group_mentions.rb b/db/migrate/20151201035631_add_group_mentions.rb
new file mode 100644
index 00000000000..4e3900387a2
--- /dev/null
+++ b/db/migrate/20151201035631_add_group_mentions.rb
@@ -0,0 +1,12 @@
+class AddGroupMentions < ActiveRecord::Migration
+ def change
+ create_table :group_mentions do |t|
+ t.integer :post_id
+ t.integer :group_id
+ t.timestamps
+ end
+
+ add_index :group_mentions, [:post_id, :group_id], unique: true
+ add_index :group_mentions, [:group_id, :post_id], unique: true
+ end
+end
diff --git a/spec/services/post_alerter_spec.rb b/spec/services/post_alerter_spec.rb
index 34c5e498582..e5d446d4b95 100644
--- a/spec/services/post_alerter_spec.rb
+++ b/spec/services/post_alerter_spec.rb
@@ -103,12 +103,15 @@ describe PostAlerter do
create_post_with_alerts(raw: "Hello @group how are you?")
}.to change(evil_trout.notifications, :count).by(1)
+ expect(GroupMention.count).to eq(1)
group.update_columns(alias_level: Group::ALIAS_LEVELS[:members_mods_and_admins])
expect {
create_post_with_alerts(raw: "Hello @group you are not mentionable")
}.to change(evil_trout.notifications, :count).by(0)
+
+ expect(GroupMention.count).to eq(2)
end
end
|