mirror of
https://github.com/codeninjasllc/discourse.git
synced 2024-11-27 17:46:05 -05:00
FEATURE: First class messages to groups, you can select a group as a target of a message
This commit is contained in:
parent
fbffe28772
commit
9899e8d4a5
11 changed files with 93 additions and 15 deletions
|
@ -6,7 +6,9 @@ export default TextField.extend({
|
||||||
_initializeAutocomplete: function() {
|
_initializeAutocomplete: function() {
|
||||||
var self = this,
|
var self = this,
|
||||||
selected = [],
|
selected = [],
|
||||||
|
groups = [],
|
||||||
currentUser = this.currentUser,
|
currentUser = this.currentUser,
|
||||||
|
includeMentionableGroups = this.get('includeMentionableGroups') === 'true',
|
||||||
includeGroups = this.get('includeGroups') === 'true',
|
includeGroups = this.get('includeGroups') === 'true',
|
||||||
allowedUsers = this.get('allowedUsers') === 'true';
|
allowedUsers = this.get('allowedUsers') === 'true';
|
||||||
|
|
||||||
|
@ -24,18 +26,22 @@ export default TextField.extend({
|
||||||
allowAny: this.get('allowAny'),
|
allowAny: this.get('allowAny'),
|
||||||
|
|
||||||
dataSource: function(term) {
|
dataSource: function(term) {
|
||||||
return userSearch({
|
var results = userSearch({
|
||||||
term: term.replace(/[^a-zA-Z0-9_\-\.]/, ''),
|
term: term.replace(/[^a-zA-Z0-9_\-\.]/, ''),
|
||||||
topicId: self.get('topicId'),
|
topicId: self.get('topicId'),
|
||||||
exclude: excludedUsernames(),
|
exclude: excludedUsernames(),
|
||||||
includeGroups,
|
includeGroups,
|
||||||
allowedUsers
|
allowedUsers,
|
||||||
|
includeMentionableGroups
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return results;
|
||||||
},
|
},
|
||||||
|
|
||||||
transformComplete: function(v) {
|
transformComplete: function(v) {
|
||||||
if (v.username) {
|
if (v.username || v.name) {
|
||||||
return v.username;
|
if (!v.username) { groups.push(v.name); }
|
||||||
|
return v.username || v.name;
|
||||||
} else {
|
} else {
|
||||||
var excludes = excludedUsernames();
|
var excludes = excludedUsernames();
|
||||||
return v.usernames.filter(function(item){
|
return v.usernames.filter(function(item){
|
||||||
|
@ -45,10 +51,14 @@ export default TextField.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
onChangeItems: function(items) {
|
onChangeItems: function(items) {
|
||||||
|
var hasGroups = false;
|
||||||
items = items.map(function(i) {
|
items = items.map(function(i) {
|
||||||
|
if (groups.indexOf(i) > -1) { hasGroups = true; }
|
||||||
return i.username ? i.username : i;
|
return i.username ? i.username : i;
|
||||||
});
|
});
|
||||||
self.set('usernames', items.join(","));
|
self.set('usernames', items.join(","));
|
||||||
|
self.set('hasGroups', hasGroups);
|
||||||
|
|
||||||
selected = items;
|
selected = items;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -75,9 +75,10 @@ export default Ember.Controller.extend({
|
||||||
if (!Discourse.User.currentProp('staff')) { return false; }
|
if (!Discourse.User.currentProp('staff')) { return false; }
|
||||||
|
|
||||||
var usernames = this.get('model.targetUsernames');
|
var usernames = this.get('model.targetUsernames');
|
||||||
|
var hasTargetGroups = this.get('model.hasTargetGroups');
|
||||||
|
|
||||||
// We need exactly one user to issue a warning
|
// We need exactly one user to issue a warning
|
||||||
if (Ember.isEmpty(usernames) || usernames.split(',').length !== 1) {
|
if (Ember.isEmpty(usernames) || usernames.split(',').length !== 1 || hasTargetGroups) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return this.get('model.creatingPrivateMessage');
|
return this.get('model.creatingPrivateMessage');
|
||||||
|
|
|
@ -6,7 +6,7 @@ var cache = {},
|
||||||
currentTerm,
|
currentTerm,
|
||||||
oldSearch;
|
oldSearch;
|
||||||
|
|
||||||
function performSearch(term, topicId, includeGroups, allowedUsers, resultsFn) {
|
function performSearch(term, topicId, includeGroups, includeMentionableGroups, allowedUsers, resultsFn) {
|
||||||
var cached = cache[term];
|
var cached = cache[term];
|
||||||
if (cached) {
|
if (cached) {
|
||||||
resultsFn(cached);
|
resultsFn(cached);
|
||||||
|
@ -18,6 +18,7 @@ function performSearch(term, topicId, includeGroups, allowedUsers, resultsFn) {
|
||||||
data: { term: term,
|
data: { term: term,
|
||||||
topic_id: topicId,
|
topic_id: topicId,
|
||||||
include_groups: includeGroups,
|
include_groups: includeGroups,
|
||||||
|
include_mentionable_groups: includeMentionableGroups,
|
||||||
topic_allowed_users: allowedUsers }
|
topic_allowed_users: allowedUsers }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -76,6 +77,7 @@ function organizeResults(r, options) {
|
||||||
export default function userSearch(options) {
|
export default function userSearch(options) {
|
||||||
var term = options.term || "",
|
var term = options.term || "",
|
||||||
includeGroups = options.includeGroups,
|
includeGroups = options.includeGroups,
|
||||||
|
includeMentionableGroups = options.includeMentionableGroups,
|
||||||
allowedUsers = options.allowedUsers,
|
allowedUsers = options.allowedUsers,
|
||||||
topicId = options.topicId;
|
topicId = options.topicId;
|
||||||
|
|
||||||
|
@ -103,7 +105,7 @@ export default function userSearch(options) {
|
||||||
resolve(CANCELLED_STATUS);
|
resolve(CANCELLED_STATUS);
|
||||||
}, 5000);
|
}, 5000);
|
||||||
|
|
||||||
debouncedSearch(term, topicId, includeGroups, allowedUsers, function(r) {
|
debouncedSearch(term, topicId, includeGroups, includeMentionableGroups, allowedUsers, function(r) {
|
||||||
clearTimeout(clearPromise);
|
clearTimeout(clearPromise);
|
||||||
resolve(organizeResults(r, options));
|
resolve(organizeResults(r, options));
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<h3>{{fa-icon "envelope"}} {{i18n 'private_message_info.title'}}</h3>
|
<h3>{{fa-icon 'envelope'}} {{i18n 'private_message_info.title'}}</h3>
|
||||||
<div class='participants clearfix'>
|
<div class='participants clearfix'>
|
||||||
{{#each details.allowed_groups as |ag|}}
|
{{#each details.allowed_groups as |ag|}}
|
||||||
<div class='user group'>
|
<div class='user group'>
|
||||||
#{{unbound ag.name}}
|
{{fa-icon 'users'}} {{#link-to "group.index" ag.name}}{{unbound ag.name}}{{/link-to}}
|
||||||
</div>
|
</div>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
{{#each details.allowed_users as |au|}}
|
{{#each details.allowed_users as |au|}}
|
||||||
|
|
|
@ -41,11 +41,13 @@
|
||||||
{{user-selector topicId=controller.controllers.topic.model.id
|
{{user-selector topicId=controller.controllers.topic.model.id
|
||||||
excludeCurrentUser="true"
|
excludeCurrentUser="true"
|
||||||
id="private-message-users"
|
id="private-message-users"
|
||||||
includeGroups="true"
|
includeMentionableGroups="true"
|
||||||
class="span8"
|
class="span8"
|
||||||
placeholderKey="composer.users_placeholder"
|
placeholderKey="composer.users_placeholder"
|
||||||
tabindex="1"
|
tabindex="1"
|
||||||
usernames=model.targetUsernames}}
|
usernames=model.targetUsernames
|
||||||
|
hasGroups=model.hasTargetGroups
|
||||||
|
}}
|
||||||
{{#if showWarning}}
|
{{#if showWarning}}
|
||||||
<div class='add-warning'>
|
<div class='add-warning'>
|
||||||
<label>
|
<label>
|
||||||
|
|
|
@ -507,6 +507,14 @@ class PostsController < ApplicationController
|
||||||
result[:user_agent] = request.user_agent
|
result[:user_agent] = request.user_agent
|
||||||
result[:referrer] = request.env["HTTP_REFERER"]
|
result[:referrer] = request.env["HTTP_REFERER"]
|
||||||
|
|
||||||
|
if usernames = result[:target_usernames]
|
||||||
|
usernames = usernames.split(",")
|
||||||
|
groups = Group.mentionable(current_user).where('name in (?)', usernames).pluck('name')
|
||||||
|
usernames -= groups
|
||||||
|
result[:target_usernames] = usernames.join(",")
|
||||||
|
result[:target_group_names] = groups.join(",")
|
||||||
|
end
|
||||||
|
|
||||||
result
|
result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -548,6 +548,14 @@ class UsersController < ApplicationController
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if params[:include_mentionable_groups] == "true" && current_user
|
||||||
|
to_render[:groups] = Group.mentionable(current_user)
|
||||||
|
.where("name ILIKE :term_like", term_like: "#{term}%")
|
||||||
|
.map do |m|
|
||||||
|
{name: m.name, usernames: []}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
render json: to_render
|
render json: to_render
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -201,7 +201,7 @@ class Group < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.search_group(name)
|
def self.search_group(name)
|
||||||
Group.where(visible: true).where("name ILIKE :term_like", term_like: "#{name.downcase}%")
|
Group.where(visible: true).where("name ILIKE :term_like", term_like: "#{name}%")
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.lookup_group(name)
|
def self.lookup_group(name)
|
||||||
|
|
|
@ -150,19 +150,32 @@ class TopicCreator
|
||||||
|
|
||||||
def add_users(topic, usernames)
|
def add_users(topic, usernames)
|
||||||
return unless usernames
|
return unless usernames
|
||||||
User.where(username: usernames.split(',').flatten).each do |user|
|
|
||||||
|
names = usernames.split(',').flatten
|
||||||
|
len = 0
|
||||||
|
|
||||||
|
User.where(username: names).each do |user|
|
||||||
check_can_send_permission!(topic, user)
|
check_can_send_permission!(topic, user)
|
||||||
@added_users << user
|
@added_users << user
|
||||||
topic.topic_allowed_users.build(user_id: user.id)
|
topic.topic_allowed_users.build(user_id: user.id)
|
||||||
|
len += 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
rollback_with!(topic, :target_user_not_found) unless len == names.length
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_groups(topic, groups)
|
def add_groups(topic, groups)
|
||||||
return unless groups
|
return unless groups
|
||||||
Group.where(name: groups.split(',')).each do |group|
|
names = groups.split(',')
|
||||||
|
len = 0
|
||||||
|
|
||||||
|
Group.where(name: names).each do |group|
|
||||||
check_can_send_permission!(topic, group)
|
check_can_send_permission!(topic, group)
|
||||||
topic.topic_allowed_groups.build(group_id: group.id)
|
topic.topic_allowed_groups.build(group_id: group.id)
|
||||||
|
len += 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
rollback_with!(topic, :target_group_not_found) unless len == names.length
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_can_send_permission!(topic, obj)
|
def check_can_send_permission!(topic, obj)
|
||||||
|
|
|
@ -544,7 +544,7 @@ describe PostCreator do
|
||||||
target_group_names: group.name)
|
target_group_names: group.name)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'acts correctly' do
|
it 'can post to a group correctly' do
|
||||||
expect(post.topic.archetype).to eq(Archetype.private_message)
|
expect(post.topic.archetype).to eq(Archetype.private_message)
|
||||||
expect(post.topic.topic_allowed_users.count).to eq(1)
|
expect(post.topic.topic_allowed_users.count).to eq(1)
|
||||||
expect(post.topic.topic_allowed_groups.count).to eq(1)
|
expect(post.topic.topic_allowed_groups.count).to eq(1)
|
||||||
|
|
|
@ -583,6 +583,40 @@ describe PostsController do
|
||||||
expect(parsed['cooked']).to be_present
|
expect(parsed['cooked']).to be_present
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "can send a message to a group" do
|
||||||
|
|
||||||
|
group = Group.create(name: 'test_group', alias_level: Group::ALIAS_LEVELS[:nobody])
|
||||||
|
user1 = Fabricate(:user)
|
||||||
|
group.add(user1)
|
||||||
|
|
||||||
|
xhr :post, :create, {
|
||||||
|
raw: 'I can haz a test',
|
||||||
|
title: 'I loves my test',
|
||||||
|
target_usernames: group.name,
|
||||||
|
archetype: Archetype.private_message
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(response).not_to be_success
|
||||||
|
|
||||||
|
# allow pm to this group
|
||||||
|
group.update_columns(alias_level: Group::ALIAS_LEVELS[:everyone])
|
||||||
|
|
||||||
|
xhr :post, :create, {
|
||||||
|
raw: 'I can haz a test',
|
||||||
|
title: 'I loves my test',
|
||||||
|
target_usernames: group.name,
|
||||||
|
archetype: Archetype.private_message
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(response).to be_success
|
||||||
|
|
||||||
|
parsed = ::JSON.parse(response.body)
|
||||||
|
post = Post.find(parsed['id'])
|
||||||
|
|
||||||
|
expect(post.topic.topic_allowed_users.length).to eq(1)
|
||||||
|
expect(post.topic.topic_allowed_groups.length).to eq(1)
|
||||||
|
end
|
||||||
|
|
||||||
it "returns the nested post with a param" do
|
it "returns the nested post with a param" do
|
||||||
xhr :post, :create, {raw: 'this is the test content',
|
xhr :post, :create, {raw: 'this is the test content',
|
||||||
title: 'this is the test title for the topic',
|
title: 'this is the test title for the topic',
|
||||||
|
|
Loading…
Reference in a new issue