From 209daf780107ecf2d3cd643f61b5b9e883e30227 Mon Sep 17 00:00:00 2001 From: David Taylor Date: Tue, 1 Aug 2017 15:20:00 +0100 Subject: [PATCH] =?UTF-8?q?Add=20group=20messages=20support=20to=20admin?= =?UTF-8?q?=20UI,=20and=20add=20a=20=E2=80=98type=E2=80=99=20field=20for?= =?UTF-8?q?=20further=20improvements?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/controllers/chat_controller.rb | 4 +- app/models/channel.rb | 2 +- app/models/rule.rb | 43 +++++++++----- app/serializers/rule_serializer.rb | 2 +- app/services/manager.rb | 2 +- .../admin-plugins-chat-provider.js.es6 | 4 +- .../admin-plugins-chat-edit-rule.js.es6 | 8 ++- assets/javascripts/admin/models/rule.js.es6 | 21 ++++++- .../routes/admin-plugins-chat-provider.js.es6 | 6 +- .../modal/admin-plugins-chat-edit-rule.hbs | 58 ++++++++++++++----- config/locales/client.en.yml | 7 +++ spec/controllers/chat_controller_spec.rb | 2 +- spec/models/rule_spec.rb | 6 +- spec/services/manager_spec.rb | 6 +- 14 files changed, 124 insertions(+), 47 deletions(-) diff --git a/app/controllers/chat_controller.rb b/app/controllers/chat_controller.rb index 82126ea..257cafb 100644 --- a/app/controllers/chat_controller.rb +++ b/app/controllers/chat_controller.rb @@ -118,7 +118,7 @@ class DiscourseChat::ChatController < ApplicationController def create_rule begin - hash = params.require(:rule).permit(:channel_id, :filter, :group_id, :category_id, tags:[]) + hash = params.require(:rule).permit(:channel_id, :type, :filter, :group_id, :category_id, tags:[]) rule = DiscourseChat::Rule.new(hash) @@ -135,7 +135,7 @@ class DiscourseChat::ChatController < ApplicationController def update_rule begin rule = DiscourseChat::Rule.find(params[:id].to_i) - hash = params.require(:rule).permit(:filter, :group_id, :category_id, tags:[]) + hash = params.require(:rule).permit(:type, :filter, :group_id, :category_id, tags:[]) if not rule.update(hash) raise Discourse::InvalidParameters, 'Rule is not valid' diff --git a/app/models/channel.rb b/app/models/channel.rb index c2730fb..1fe280d 100644 --- a/app/models/channel.rb +++ b/app/models/channel.rb @@ -59,7 +59,7 @@ class DiscourseChat::Channel < DiscourseChat::PluginModel end def rules - DiscourseChat::Rule.with_channel_id(id) + DiscourseChat::Rule.with_channel_id(id).order_by_precedence end scope :with_provider, ->(provider) { where("value::json->>'provider'=?", provider)} diff --git a/app/models/rule.rb b/app/models/rule.rb index 44c0125..8668888 100644 --- a/app/models/rule.rb +++ b/app/models/rule.rb @@ -2,18 +2,22 @@ class DiscourseChat::Rule < DiscourseChat::PluginModel KEY_PREFIX = 'rule:' # Setup ActiveRecord::Store to use the JSON field to read/write these values - store :value, accessors: [ :channel_id, :group_id, :category_id, :tags, :filter ], coder: JSON + store :value, accessors: [ :channel_id, :type, :group_id, :category_id, :tags, :filter ], coder: JSON after_initialize :init_filter def init_filter self.filter ||= 'watch' + self.type ||= 'normal' end validates :filter, :inclusion => { :in => %w(watch follow mute), :message => "%{value} is not a valid filter" } - validate :channel_valid?, :category_and_group_valid?, :tags_valid? + validates :type, :inclusion => { :in => %w(normal group_message), + :message => "%{value} is not a valid filter" } + + validate :channel_valid?, :category_valid?, :group_valid?, :tags_valid? def channel_valid? # Validate channel @@ -22,19 +26,12 @@ class DiscourseChat::Rule < DiscourseChat::PluginModel end end - def category_and_group_valid? - if category_id and group_id - errors.add(:category_id, "cannot be specified in addition to group_id") - return + def category_valid? + if type != 'normal' && !category_id.nil? + errors.add(:category_id, "cannot be specified for that type of rule") end - if group_id - # Validate group - if not Group.where(id: group_id).exists? - errors.add(:group_id, "#{group_id} is not a valid group id") - end - return - end + return unless type == 'normal' # Validate category if not (category_id.nil? or Category.where(id: category_id).exists?) @@ -42,6 +39,19 @@ class DiscourseChat::Rule < DiscourseChat::PluginModel end end + def group_valid? + if type == 'normal' && !group_id.nil? + errors.add(:group_id, "cannot be specified for that type of rule") + end + + return if type == 'normal' + + # Validate group + if not Group.where(id: group_id).exists? + errors.add(:group_id, "#{group_id} is not a valid group id") + end + end + def tags_valid? # Validate tags return if tags.nil? @@ -81,6 +91,8 @@ class DiscourseChat::Rule < DiscourseChat::PluginModel self.channel_id = val.id end + scope :with_type, ->(type) { where("value::json->>'type'=?", type.to_s)} + scope :with_channel, ->(channel) { with_channel_id(channel.id) } scope :with_channel_id, ->(channel_id) { where("value::json->>'channel_id'=?", channel_id.to_s)} @@ -88,6 +100,11 @@ class DiscourseChat::Rule < DiscourseChat::PluginModel scope :with_group_ids, ->(group_id) { where("value::json->>'group_id' IN (?)", group_id.map(&:to_s))} scope :order_by_precedence, ->{ order("CASE + WHEN value::json->>'type' = 'group_mention' THEN 1 + WHEN value::json->>'type' = 'group_message' THEN 2 + ELSE 3 + END", + "CASE WHEN value::json->>'filter' = 'mute' THEN 1 WHEN value::json->>'filter' = 'watch' THEN 2 WHEN value::json->>'filter' = 'follow' THEN 3 diff --git a/app/serializers/rule_serializer.rb b/app/serializers/rule_serializer.rb index e3b9cb6..efaf02b 100644 --- a/app/serializers/rule_serializer.rb +++ b/app/serializers/rule_serializer.rb @@ -1,5 +1,5 @@ class DiscourseChat::RuleSerializer < ApplicationSerializer - attributes :id, :channel_id, :group_id, :group_name, :category_id, :tags, :filter + attributes :id, :channel_id, :type, :group_id, :group_name, :category_id, :tags, :filter def group_name if object.group_id diff --git a/app/services/manager.rb b/app/services/manager.rb index d4fd9b9..11bd537 100644 --- a/app/services/manager.rb +++ b/app/services/manager.rb @@ -25,7 +25,7 @@ module DiscourseChat if topic.archetype == Archetype.private_message group_ids_with_access = topic.topic_allowed_groups.pluck(:group_id) return if group_ids_with_access.empty? - matching_rules = DiscourseChat::Rule.with_group_ids(group_ids_with_access) + matching_rules = DiscourseChat::Rule.with_type('group_message').with_group_ids(group_ids_with_access) else matching_rules = DiscourseChat::Rule.with_category_id(topic.category_id) if topic.category # Also load the rules for the wildcard category diff --git a/assets/javascripts/admin/controllers/admin-plugins-chat-provider.js.es6 b/assets/javascripts/admin/controllers/admin-plugins-chat-provider.js.es6 index c3f90e6..1d15db3 100644 --- a/assets/javascripts/admin/controllers/admin-plugins-chat-provider.js.es6 +++ b/assets/javascripts/admin/controllers/admin-plugins-chat-provider.js.es6 @@ -37,12 +37,12 @@ export default Ember.Controller.extend({ createRule(channel){ this.set('modalShowing', true); - var model = {rule: this.store.createRecord('rule',{channel_id: channel.id}), channel:channel, provider:this.get('model.provider')}; + var model = {rule: this.store.createRecord('rule',{channel_id: channel.id}), channel:channel, provider:this.get('model.provider'), groups:this.get('model.groups')}; showModal('admin-plugins-chat-edit-rule', { model: model, admin: true }); }, editRule(rule, channel){ this.set('modalShowing', true); - var model = {rule: rule, channel:channel, provider:this.get('model.provider')}; + var model = {rule: rule, channel:channel, provider:this.get('model.provider'), groups:this.get('model.groups')}; showModal('admin-plugins-chat-edit-rule', { model: model, admin: true }); }, diff --git a/assets/javascripts/admin/controllers/modals/admin-plugins-chat-edit-rule.js.es6 b/assets/javascripts/admin/controllers/modals/admin-plugins-chat-edit-rule.js.es6 index 220d525..aa32187 100644 --- a/assets/javascripts/admin/controllers/modals/admin-plugins-chat-edit-rule.js.es6 +++ b/assets/javascripts/admin/controllers/modals/admin-plugins-chat-edit-rule.js.es6 @@ -3,9 +3,9 @@ import ModalFunctionality from 'discourse/mixins/modal-functionality'; import { ajax } from 'discourse/lib/ajax'; import { extractError } from 'discourse/lib/ajax-error'; import InputValidation from 'discourse/models/input-validation'; +import computed from "ember-addons/ember-computed-decorators"; export default Ember.Controller.extend(ModalFunctionality, { - setupKeydown: function() { Ember.run.schedule('afterRender', () => { $('#chat_integration_edit_channel_modal').keydown(e => { @@ -16,11 +16,15 @@ export default Ember.Controller.extend(ModalFunctionality, { }); }.on('init'), - saveDisabled: function(){ return false; }.property(), + @computed('model.rule.type') + showCategory: function(type){ + return (type == "normal") + }, + actions: { cancel: function(){ this.send('closeModal'); diff --git a/assets/javascripts/admin/models/rule.js.es6 b/assets/javascripts/admin/models/rule.js.es6 index f225d45..70fab17 100644 --- a/assets/javascripts/admin/models/rule.js.es6 +++ b/assets/javascripts/admin/models/rule.js.es6 @@ -9,12 +9,29 @@ export default RestModel.extend({ { id: 'mute', name: I18n.t('chat_integration.filter.mute'), icon: 'times-circle' } ], + available_types: [ + { id: 'normal', name: I18n.t('chat_integration.type.normal')}, + { id: 'group_message', name: I18n.t('chat_integration.type.group_message')}, + { id: 'group_mention', name: I18n.t('chat_integration.type.group_mention')} + ], + category_id: null, tags: null, channel_id: null, filter: 'watch', + type: 'normal', error_key: null, + + removeUnneededInfo: function(){ + const type=this.get('type'); + if(type=='normal'){ + this.set('group_id', null); + }else{ + this.set('category_id', null); + } + }.observes('type'), + @computed('category_id') category(categoryId) { if (categoryId){ @@ -30,12 +47,12 @@ export default RestModel.extend({ }, updateProperties() { - var prop_names = ['category_id','group_id','tags','filter']; + var prop_names = ['type','category_id','group_id','tags','filter']; return this.getProperties(prop_names); }, createProperties() { - var prop_names = ['channel_id', 'category_id','group_id','tags','filter']; + var prop_names = ['type','channel_id', 'category_id','group_id','tags','filter']; return this.getProperties(prop_names); } diff --git a/assets/javascripts/admin/routes/admin-plugins-chat-provider.js.es6 b/assets/javascripts/admin/routes/admin-plugins-chat-provider.js.es6 index 7889816..216f407 100644 --- a/assets/javascripts/admin/routes/admin-plugins-chat-provider.js.es6 +++ b/assets/javascripts/admin/routes/admin-plugins-chat-provider.js.es6 @@ -1,11 +1,15 @@ import { ajax } from 'discourse/lib/ajax'; +import Group from 'discourse/models/group'; export default Discourse.Route.extend({ model(params, transition) { return Ember.RSVP.hash({ channels: this.store.findAll('channel', {provider: params.provider}), - provider: this.modelFor("admin-plugins-chat").findBy('id',params.provider) + provider: this.modelFor("admin-plugins-chat").findBy('id',params.provider), + groups: Group.findAll().then(groups => { + return groups.filter(g => !g.get('automatic')); + }) }).then(value => { value.channels.forEach(channel => { channel.set('rules', channel.rules.map(rule => { diff --git a/assets/javascripts/admin/templates/modal/admin-plugins-chat-edit-rule.hbs b/assets/javascripts/admin/templates/modal/admin-plugins-chat-edit-rule.hbs index d7e4c68..4b2e83c 100644 --- a/assets/javascripts/admin/templates/modal/admin-plugins-chat-edit-rule.hbs +++ b/assets/javascripts/admin/templates/modal/admin-plugins-chat-edit-rule.hbs @@ -25,6 +25,17 @@ + + + + {{combo-box name="type" content=model.rule.available_types value=model.rule.type}} + + + + + + + @@ -36,22 +47,37 @@ - - - - {{category-chooser - name="category" - value=model.rule.category_id - rootNoneLabel="chat_integration.all_categories" - rootNone=true - overrideWidths=false - }} - - - - - - + {{#if showCategory}} + + + + {{category-chooser + name="category" + value=model.rule.category_id + rootNoneLabel="chat_integration.all_categories" + rootNone=true + overrideWidths=false + }} + + + + + + + {{else}} + + + + {{combo-box content=model.groups valueAttribute="id" value=model.rule.group_id none="chat_integration.choose_group"}} + + + + + + + {{/if}} + + {{#if siteSettings.tagging_enabled}} diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index fdda3a9..43d469f 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -22,6 +22,10 @@ en: close: "Close" error: "An unknown error occured while sending the message. Check the site logs for more information." success: "Message sent successfully" + type: + normal: Normal + group_message: Group Message + group_mention: Group Mention filter: mute: 'Mute' follow: 'First post only' @@ -45,13 +49,16 @@ en: save: Save Rule cancel: Cancel provider: Provider + type: Type channel: Channel filter: Filter category: Category + group: Group tags: Tags instructions: filter: "Notification level. Mute overrides other matching rules." category: "This rule will only apply to topics in the specified category." + group: "This rule will apply to posts referencing this group" tags: "If specified, this rule will only apply to topics which have at least one of these tags." provider: diff --git a/spec/controllers/chat_controller_spec.rb b/spec/controllers/chat_controller_spec.rb index dc76526..dc1eabc 100644 --- a/spec/controllers/chat_controller_spec.rb +++ b/spec/controllers/chat_controller_spec.rb @@ -105,7 +105,7 @@ describe 'Chat Controller', type: :request do "provider" => 'dummy', "data" => {}, "error_key" => nil, - "rules" => [{"id" => rule.id, "filter" => "follow", "channel_id" => channel.id, "category_id" => category.id, "tags" => [tag.name]}] + "rules" => [{"id" => rule.id, "type" => 'normal', "group_name" => nil, "group_id" => nil, "filter" => "follow", "channel_id" => channel.id, "category_id" => category.id, "tags" => [tag.name]}] ) end diff --git a/spec/models/rule_spec.rb b/spec/models/rule_spec.rb index bace0c6..b698383 100644 --- a/spec/models/rule_spec.rb +++ b/spec/models/rule_spec.rb @@ -116,8 +116,8 @@ RSpec.describe DiscourseChat::Rule do it 'can be filtered by group' do group1 = Fabricate(:group) group2 = Fabricate(:group) - rule2 = DiscourseChat::Rule.create(channel:channel, group_id: group1.id) - rule3 = DiscourseChat::Rule.create(channel:channel, group_id: group2.id) + rule2 = DiscourseChat::Rule.create!(channel:channel, type:'group_message', group_id: group1.id) + rule3 = DiscourseChat::Rule.create!(channel:channel, type:'group_message', group_id: group2.id) expect(DiscourseChat::Rule.all.length).to eq(3) @@ -161,12 +161,14 @@ RSpec.describe DiscourseChat::Rule do rule.group_id = group.id expect(rule.valid?).to eq(false) rule.category_id = nil + rule.type = "group_message" expect(rule.valid?).to eq(true) end it 'validates group correctly' do rule.category_id = nil rule.group_id = group.id + rule.type = "group_message" expect(rule.valid?).to eq(true) rule.group_id = -99 expect(rule.valid?).to eq(false) diff --git a/spec/services/manager_spec.rb b/spec/services/manager_spec.rb index e35cb09..c06ea46 100644 --- a/spec/services/manager_spec.rb +++ b/spec/services/manager_spec.rb @@ -110,7 +110,7 @@ RSpec.describe DiscourseChat::Manager do it "should work for group pms" do DiscourseChat::Rule.create!(channel: chan1, filter: 'watch' ) # Wildcard watch - DiscourseChat::Rule.create!(channel: chan2, filter: 'watch', group_id: group.id ) # Group watch + DiscourseChat::Rule.create!(channel: chan2, type: 'group_message', filter: 'watch', group_id: group.id ) # Group watch private_post = Fabricate(:private_message_post) private_post.topic.invite_group(Fabricate(:user), group) @@ -122,8 +122,8 @@ RSpec.describe DiscourseChat::Manager do it "should work for pms with multiple groups" do group2 = Fabricate(:group) - DiscourseChat::Rule.create!(channel: chan1, filter: 'watch', group_id: group.id ) - DiscourseChat::Rule.create!(channel: chan2, filter: 'watch', group_id: group2.id ) + DiscourseChat::Rule.create!(channel: chan1, type: 'group_message', filter: 'watch', group_id: group.id ) + DiscourseChat::Rule.create!(channel: chan2, type: 'group_message', filter: 'watch', group_id: group2.id ) private_post = Fabricate(:private_message_post) private_post.topic.invite_group(Fabricate(:user), group)