REFACTOR: Clean up code in plugin.

This commit is contained in:
Guo Xiang Tan 2017-10-03 17:42:07 +08:00
parent aa4d169c0e
commit f836047f90
40 changed files with 457 additions and 465 deletions

View File

@ -26,7 +26,7 @@ class DiscourseChat::ChatController < ApplicationController
provider = ::DiscourseChat::Provider.get_by_name(channel.provider) provider = ::DiscourseChat::Provider.get_by_name(channel.provider)
if not ::DiscourseChat::Provider.is_enabled(provider) if !DiscourseChat::Provider.is_enabled(provider)
raise Discourse::NotFound raise Discourse::NotFound
end end
@ -48,16 +48,12 @@ class DiscourseChat::ChatController < ApplicationController
end end
def list_channels def list_channels
providers = ::DiscourseChat::Provider.enabled_providers.map { |x| x::PROVIDER_NAME } providers = ::DiscourseChat::Provider.enabled_provider_names
requested_provider = params[:provider] requested_provider = params[:provider]
if not providers.include? requested_provider raise Discourse::InvalidParameters if !providers.include?(requested_provider)
raise Discourse::NotFound
end
channels = DiscourseChat::Channel.with_provider(requested_provider) channels = DiscourseChat::Channel.with_provider(requested_provider)
render_serialized channels, DiscourseChat::ChannelSerializer, root: 'channels' render_serialized channels, DiscourseChat::ChannelSerializer, root: 'channels'
end end
@ -65,13 +61,13 @@ class DiscourseChat::ChatController < ApplicationController
begin begin
providers = ::DiscourseChat::Provider.enabled_providers.map { |x| x::PROVIDER_NAME } providers = ::DiscourseChat::Provider.enabled_providers.map { |x| x::PROVIDER_NAME }
if (not defined? params[:channel]) && defined? params[:channel][:provider] if !defined?(params[:channel]) && defined?(params[:channel][:provider])
raise Discourse::InvalidParameters, 'Provider is not valid' raise Discourse::InvalidParameters, 'Provider is not valid'
end end
requested_provider = params[:channel][:provider] requested_provider = params[:channel][:provider]
if not providers.include? requested_provider if !providers.include?(requested_provider)
raise Discourse::InvalidParameters, 'Provider is not valid' raise Discourse::InvalidParameters, 'Provider is not valid'
end end
@ -81,7 +77,7 @@ class DiscourseChat::ChatController < ApplicationController
channel = DiscourseChat::Channel.new(hash) channel = DiscourseChat::Channel.new(hash)
if not channel.save(hash) if !channel.save(hash)
raise Discourse::InvalidParameters, 'Channel is not valid' raise Discourse::InvalidParameters, 'Channel is not valid'
end end
@ -100,7 +96,7 @@ class DiscourseChat::ChatController < ApplicationController
hash = params.require(:channel).permit(data: allowed_keys) hash = params.require(:channel).permit(data: allowed_keys)
if not channel.update(hash) if !channel.update(hash)
raise Discourse::InvalidParameters, 'Channel is not valid' raise Discourse::InvalidParameters, 'Channel is not valid'
end end
@ -111,9 +107,9 @@ class DiscourseChat::ChatController < ApplicationController
end end
def destroy_channel def destroy_channel
rule = DiscourseChat::Channel.find(params[:id].to_i) rule = DiscourseChat::Channel.find_by(id: params[:id])
raise Discourse::InvalidParameters unless rule
rule.destroy rule.destroy!
render json: success_json render json: success_json
end end
@ -121,10 +117,9 @@ class DiscourseChat::ChatController < ApplicationController
def create_rule def create_rule
begin begin
hash = params.require(:rule).permit(:channel_id, :type, :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) rule = DiscourseChat::Rule.new(hash)
if not rule.save(hash) if !rule.save(hash)
raise Discourse::InvalidParameters, 'Rule is not valid' raise Discourse::InvalidParameters, 'Rule is not valid'
end end
@ -139,7 +134,7 @@ class DiscourseChat::ChatController < ApplicationController
rule = DiscourseChat::Rule.find(params[:id].to_i) rule = DiscourseChat::Rule.find(params[:id].to_i)
hash = params.require(:rule).permit(:type, :filter, :group_id, :category_id, tags: []) hash = params.require(:rule).permit(:type, :filter, :group_id, :category_id, tags: [])
if not rule.update(hash) if !rule.update(hash)
raise Discourse::InvalidParameters, 'Rule is not valid' raise Discourse::InvalidParameters, 'Rule is not valid'
end end
@ -150,9 +145,9 @@ class DiscourseChat::ChatController < ApplicationController
end end
def destroy_rule def destroy_rule
rule = DiscourseChat::Rule.find(params[:id].to_i) rule = DiscourseChat::Rule.find_by(id: params[:id])
raise Discourse::InvalidParameters.new unless rule
rule.destroy rule.destroy!
render json: success_json render json: success_json
end end

View File

@ -4,15 +4,13 @@ class DiscourseChat::PublicController < ApplicationController
def post_transcript def post_transcript
params.require(:secret) params.require(:secret)
redis_key = "chat_integration:transcript:" + params[:secret] redis_key = "chat_integration:transcript:#{params[:secret]}"
content = $redis.get(redis_key) content = $redis.get(redis_key)
if content if content
render json: { content: content } render json: { content: content }
return else
raise Discourse::NotFound
end end
raise Discourse::NotFound
end end
end end

View File

@ -33,11 +33,12 @@ module DiscourseChat
if token.start_with?('tag:') if token.start_with?('tag:')
tag_name = token.sub(/^tag:/, '') tag_name = token.sub(/^tag:/, '')
else else
return error_text # Abort and send help text return error_text
end end
tag = Tag.find_by(name: tag_name) tag = Tag.find_by(name: tag_name)
unless tag # If tag doesn't exist, abort
unless tag
return I18n.t("chat_integration.provider.#{provider}.not_found.tag", name: tag_name) return I18n.t("chat_integration.provider.#{provider}.not_found.tag", name: tag_name)
end end
tags.push(tag.name) tags.push(tag.name)
@ -46,11 +47,11 @@ module DiscourseChat
category_id = category.nil? ? nil : category.id category_id = category.nil? ? nil : category.id
case DiscourseChat::Helper.smart_create_rule(channel: channel, filter: cmd, category_id: category_id, tags: tags) case DiscourseChat::Helper.smart_create_rule(channel: channel, filter: cmd, category_id: category_id, tags: tags)
when :created when :created
return I18n.t("chat_integration.provider.#{provider}.create.created") I18n.t("chat_integration.provider.#{provider}.create.created")
when :updated when :updated
return I18n.t("chat_integration.provider.#{provider}.create.updated") I18n.t("chat_integration.provider.#{provider}.create.updated")
else else
return I18n.t("chat_integration.provider.#{provider}.create.error") I18n.t("chat_integration.provider.#{provider}.create.error")
end end
when "remove" when "remove"
return error_text unless tokens.size == 1 return error_text unless tokens.size == 1
@ -59,16 +60,16 @@ module DiscourseChat
return error_text unless rule_number.to_s == tokens[0] # Check we were given a number return error_text unless rule_number.to_s == tokens[0] # Check we were given a number
if DiscourseChat::Helper.delete_by_index(channel, rule_number) if DiscourseChat::Helper.delete_by_index(channel, rule_number)
return I18n.t("chat_integration.provider.#{provider}.delete.success") I18n.t("chat_integration.provider.#{provider}.delete.success")
else else
return I18n.t("chat_integration.provider.#{provider}.delete.error") I18n.t("chat_integration.provider.#{provider}.delete.error")
end end
when "status" when "status"
return DiscourseChat::Helper.status_for_channel(channel) return DiscourseChat::Helper.status_for_channel(channel)
when "help" when "help"
return I18n.t("chat_integration.provider.#{provider}.help") I18n.t("chat_integration.provider.#{provider}.help")
else else
return error_text error_text
end end
end end
@ -105,12 +106,12 @@ module DiscourseChat
end end
text << I18n.t("chat_integration.provider.#{provider}.status.rule_string", text << I18n.t("chat_integration.provider.#{provider}.status.rule_string",
index: i, index: i,
filter: rule.filter, filter: rule.filter,
category: category_name category: category_name
) )
if SiteSetting.tagging_enabled && (not rule.tags.nil?) if SiteSetting.tagging_enabled && (!rule.tags.nil?)
text << I18n.t("chat_integration.provider.#{provider}.status.rule_string_tags_suffix", tags: rule.tags.join(', ')) text << I18n.t("chat_integration.provider.#{provider}.status.rule_string_tags_suffix", tags: rule.tags.join(', '))
end end
@ -121,16 +122,15 @@ module DiscourseChat
if rules.size == 0 if rules.size == 0
text << I18n.t("chat_integration.provider.#{provider}.status.no_rules") text << I18n.t("chat_integration.provider.#{provider}.status.no_rules")
end end
return text
text
end end
# Delete a rule based on its (1 based) index as seen in the # Delete a rule based on its (1 based) index as seen in the
# status_for_channel function # status_for_channel function
def self.delete_by_index(channel, index) def self.delete_by_index(channel, index)
rules = channel.rules.order_by_precedence rules = channel.rules.order_by_precedence
return false if index < (1) || index > (rules.size) return false if index < (1) || index > (rules.size)
return :deleted if rules[index - 1].destroy return :deleted if rules[index - 1].destroy
end end
@ -184,17 +184,14 @@ module DiscourseChat
# This rule is unique! Create a new one: # This rule is unique! Create a new one:
return :created if Rule.new(channel: channel, filter: filter, category_id: category_id, tags: tags).save return :created if Rule.new(channel: channel, filter: filter, category_id: category_id, tags: tags).save
false
return false # Error
end end
def self.save_transcript(transcript) def self.save_transcript(transcript)
secret = SecureRandom.hex secret = SecureRandom.hex
redis_key = "chat_integration:transcript:" + secret redis_key = "chat_integration:transcript:#{secret}"
$redis.set(redis_key, transcript, ex: 3600) # Expire in 1 hour $redis.setex(redis_key, 3600, transcript)
secret
return secret
end end
end end

View File

@ -2,12 +2,12 @@ module ::DiscourseChat
PLUGIN_NAME = "discourse-chat-integration".freeze PLUGIN_NAME = "discourse-chat-integration".freeze
class AdminEngine < ::Rails::Engine class AdminEngine < ::Rails::Engine
engine_name DiscourseChat::PLUGIN_NAME + "-admin" engine_name "#{DiscourseChat::PLUGIN_NAME}-admin"
isolate_namespace DiscourseChat isolate_namespace DiscourseChat
end end
class PublicEngine < ::Rails::Engine class PublicEngine < ::Rails::Engine
engine_name DiscourseChat::PLUGIN_NAME + "-public" engine_name "#{DiscourseChat::PLUGIN_NAME}-public"
isolate_namespace DiscourseChat isolate_namespace DiscourseChat
end end

View File

@ -1,9 +1,9 @@
module Jobs module Jobs
class NotifyChats < Jobs::Base class NotifyChats < Jobs::Base
sidekiq_options retry: false # Don't retry, could result in duplicate notifications for some providers sidekiq_options retry: false
def execute(args)
return if not SiteSetting.chat_integration_enabled? # Plugin may have been disabled since job triggered
def execute(args)
return if !SiteSetting.chat_integration_enabled?
::DiscourseChat::Manager.trigger_notifications(args[:post_id]) ::DiscourseChat::Manager.trigger_notifications(args[:post_id])
end end
end end

View File

@ -29,10 +29,8 @@ class DiscourseChat::Channel < DiscourseChat::PluginModel
end end
def provider_valid? def provider_valid?
# Validate provider if !DiscourseChat::Provider.provider_names.include?(provider)
if not ::DiscourseChat::Provider.provider_names.include? provider
errors.add(:provider, "#{provider} is not a valid provider") errors.add(:provider, "#{provider} is not a valid provider")
return
end end
end end
@ -66,6 +64,5 @@ class DiscourseChat::Channel < DiscourseChat::PluginModel
if check_unique && matching_channels.exists? if check_unique && matching_channels.exists?
errors.add(:data, "matches an existing channel") errors.add(:data, "matches an existing channel")
end end
end end
end end

View File

@ -17,8 +17,6 @@ module DiscourseChat
return if post.blank? || post.post_type != Post.types[:regular] return if post.blank? || post.post_type != Post.types[:regular]
topic = post.topic topic = post.topic
# Abort if topic is blank... this should never be the case
return if topic.blank? return if topic.blank?
# If it's a private message, filter rules by groups, otherwise filter rules by category # If it's a private message, filter rules by groups, otherwise filter rules by category
@ -66,7 +64,7 @@ module DiscourseChat
matching_rules = matching_rules.select { |rule| rule.filter != "mute" } matching_rules = matching_rules.select { |rule| rule.filter != "mute" }
# If this is not the first post, discard all "follow" rules # If this is not the first post, discard all "follow" rules
if not post.is_first_post? if !post.is_first_post?
matching_rules = matching_rules.select { |rule| rule.filter != "follow" } matching_rules = matching_rules.select { |rule| rule.filter != "follow" }
end end

View File

@ -1,6 +1,3 @@
import buildPluginAdapter from 'admin/adapters/build-plugin'; import buildPluginAdapter from 'admin/adapters/build-plugin';
export default buildPluginAdapter('chat').extend({ export default buildPluginAdapter('chat');
});

View File

@ -1,5 +1,3 @@
import buildPluginAdapter from 'admin/adapters/build-plugin'; import buildPluginAdapter from 'admin/adapters/build-plugin';
export default buildPluginAdapter('chat').extend({ export default buildPluginAdapter('chat');
});

View File

@ -1,5 +1,3 @@
import buildPluginAdapter from 'admin/adapters/build-plugin'; import buildPluginAdapter from 'admin/adapters/build-plugin';
export default buildPluginAdapter('chat').extend({ export default buildPluginAdapter('chat');
});

View File

@ -33,8 +33,8 @@ export default Ember.Component.extend({
this.sendAction('editRule', rule, this.get('channel')); this.sendAction('editRule', rule, this.get('channel'));
}, },
showError(error_key){ showError(errorKey){
bootbox.alert(I18n.t(error_key)); bootbox.alert(I18n.t(errorKey));
}, },
} }

View File

@ -1,32 +1,37 @@
import { popupAjaxError } from 'discourse/lib/ajax-error'; import { popupAjaxError } from 'discourse/lib/ajax-error';
import computed from 'ember-addons/ember-computed-decorators';
export default Ember.Component.extend({ export default Ember.Component.extend({
tagName: 'tr', tagName: 'tr',
isCategory: function(){ @computed('rule.type')
return this.get('rule.type') === 'normal'; isCategory(type) {
}.property('rule.type'), return type === 'normal';
},
isMessage: function(){ @computed('rule.type')
return this.get('rule.type') === 'group_message'; isMessage(type) {
}.property('rule.type'), return type === 'group_message';
},
isMention: function(){ @computed('rule.type')
return this.get('rule.type') === 'group_mention'; isMention(type) {
}.property('rule.type'), return type === 'group_mention';
},
actions: { actions: {
edit: function(){ edit() {
this.sendAction('edit', this.get('rule')); this.sendAction('edit', this.get('rule'));
}, },
delete(rule){
delete(rule) {
rule.destroyRecord().then(() => { rule.destroyRecord().then(() => {
this.send('refresh'); this.send('refresh');
}).catch(popupAjaxError); }).catch(popupAjaxError);
}, },
refresh: function(){
refresh() {
this.sendAction('refresh'); this.sendAction('refresh');
} }
} }
}); });

View File

@ -1,46 +1,73 @@
import showModal from 'discourse/lib/show-modal'; import showModal from 'discourse/lib/show-modal';
import computed from "ember-addons/ember-computed-decorators";
export default Ember.Controller.extend({ export default Ember.Controller.extend({
modalShowing: false, modalShowing: false,
anyErrors: function(){ @computed('model.channels')
var anyErrors = false; anyErrors(channels) {
this.get('model.channels').forEach(function(channel){ let anyErrors = false;
if(channel.error_key){
channels.forEach((channel) => {
if (channel.error_key) {
anyErrors = true; anyErrors = true;
} }
}); });
return anyErrors; return anyErrors;
}.property('model.channels'), },
actions:{ actions:{
createChannel(){ createChannel() {
this.set('modalShowing', true); this.set('modalShowing', true);
var model = {channel: this.store.createRecord('channel',{provider: this.get('model.provider').id, data:{}},), provider:this.get('model.provider')};
const model = {
channel: this.store.createRecord('channel', { provider: this.get('model.provider.id'), data:{} }),
provider: this.get('model.provider')
};
showModal('admin-plugins-chat-edit-channel', { model: model, admin: true }); showModal('admin-plugins-chat-edit-channel', { model: model, admin: true });
}, },
editChannel(channel){
editChannel(channel) {
this.set('modalShowing', true); this.set('modalShowing', true);
var model = {channel: channel, provider: this.get('model.provider')};
const model = {
channel: channel,
provider: this.get('model.provider')
};
showModal('admin-plugins-chat-edit-channel', { model: model, admin: true }); showModal('admin-plugins-chat-edit-channel', { model: model, admin: true });
}, },
testChannel(channel){
testChannel(channel) {
this.set('modalShowing', true); this.set('modalShowing', true);
var model = {channel:channel}; showModal('admin-plugins-chat-test', { model: { channel: channel }, admin: true });
showModal('admin-plugins-chat-test', { model: model, admin: true });
}, },
createRule(channel){ createRule(channel){
this.set('modalShowing', true); this.set('modalShowing', true);
var model = {rule: this.store.createRecord('rule',{channel_id: channel.id}), channel:channel, provider:this.get('model.provider'), groups:this.get('model.groups')};
const 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 }); showModal('admin-plugins-chat-edit-rule', { model: model, admin: true });
}, },
editRule(rule, channel){ editRule(rule, channel){
this.set('modalShowing', true); this.set('modalShowing', true);
var model = {rule: rule, channel:channel, provider:this.get('model.provider'), groups:this.get('model.groups')};
const 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 }); showModal('admin-plugins-chat-edit-rule', { model: model, admin: true });
}, },
} }
}); });

View File

@ -1,10 +1,11 @@
import ModalFunctionality from 'discourse/mixins/modal-functionality'; import ModalFunctionality from 'discourse/mixins/modal-functionality';
import { extractError } from 'discourse/lib/ajax-error'; import { popupAjaxError } from 'discourse/lib/ajax-error';
import InputValidation from 'discourse/models/input-validation'; import InputValidation from 'discourse/models/input-validation';
import { default as computed, observes } from 'ember-addons/ember-computed-decorators';
export default Ember.Controller.extend(ModalFunctionality, { export default Ember.Controller.extend(ModalFunctionality, {
setupKeydown: function() { setupKeydown() {
Ember.run.schedule('afterRender', () => { Ember.run.schedule('afterRender', () => {
$('#chat_integration_edit_channel_modal').keydown(e => { $('#chat_integration_edit_channel_modal').keydown(e => {
if (e.keyCode === 13) { if (e.keyCode === 13) {
@ -15,36 +16,37 @@ export default Ember.Controller.extend(ModalFunctionality, {
}.on('init'), }.on('init'),
// The validation property must be defined at runtime since the possible parameters vary by provider // The validation property must be defined at runtime since the possible parameters vary by provider
setupValidations: function(){ @observes('model')
setupValidations() {
if(this.get('model.provider')){ if(this.get('model.provider')){
var theKeys = this.get('model.provider.channel_parameters').map( ( param ) => param['key'] ); const theKeys = this.get('model.provider.channel_parameters').map( ( param ) => param['key'] );
Ember.defineProperty(this,'paramValidation',Ember.computed('model.channel.data.{' + theKeys.join(',') + '}',this._paramValidation)); Ember.defineProperty(this,'paramValidation', Ember.computed(`model.channel.data.{${theKeys.join(',')}},this._paramValidation`));
} }
}.observes('model'), },
validate(parameter){ validate(parameter) {
var regString = parameter.regex; const regString = parameter.regex;
var regex = new RegExp(regString); const regex = new RegExp(regString);
var val = this.get('model.channel.data.'+parameter.key); let val = this.get(`model.channel.data.${parameter.key}`);
if(val===undefined){ if (val === undefined) {
val = ""; val = "";
} }
if(val === ""){ // Fail silently if field blank if (val === "") { // Fail silently if field blank
return InputValidation.create({ return InputValidation.create({
failed: true, failed: true,
}); });
}else if(!regString){ // Pass silently if no regex available for provider } else if (!regString) { // Pass silently if no regex available for provider
return InputValidation.create({ return InputValidation.create({
ok: true, ok: true,
}); });
}else if(regex.test(val)){ // Test against regex } else if (regex.test(val)) { // Test against regex
return InputValidation.create({ return InputValidation.create({
ok: true, ok: true,
reason: I18n.t('chat_integration.edit_channel_modal.channel_validation.ok') reason: I18n.t('chat_integration.edit_channel_modal.channel_validation.ok')
}); });
}else{ // Failed regex } else { // Failed regex
return InputValidation.create({ return InputValidation.create({
failed: true, failed: true,
reason: I18n.t('chat_integration.edit_channel_modal.channel_validation.fail') reason: I18n.t('chat_integration.edit_channel_modal.channel_validation.fail')
@ -53,50 +55,47 @@ export default Ember.Controller.extend(ModalFunctionality, {
}, },
_paramValidation: function(){ _paramValidation() {
var response = {}; const response = {};
var parameters = this.get('model.provider.channel_parameters'); const parameters = this.get('model.provider.channel_parameters');
parameters.forEach(parameter => { parameters.forEach(parameter => {
response[parameter.key] = this.validate(parameter); response[parameter.key] = this.validate(parameter);
}); });
return response; return response;
}, },
saveDisabled: function(){ @computed('paramValidation')
var validations = this.get('paramValidation'); saveDisabled(paramValidation) {
if (!paramValidation) return true;
if(!validations){ return true; } let invalid = false;
var invalid = false; Object.keys(paramValidation).forEach(key =>{
if (!validations[key]) {
Object.keys(validations).forEach(key =>{
if(!validations[key]){
invalid = true; invalid = true;
} }
if(!validations[key]['ok']){
if (!validations[key]['ok']) {
invalid = true; invalid = true;
} }
}); });
return invalid; return invalid;
}.property('paramValidation'), },
actions: { actions: {
cancel: function(){ cancel() {
this.send('closeModal'); this.send('closeModal');
}, },
save: function(){ save() {
if(this.get('saveDisabled')){return;}; if (this.get('saveDisabled')) return;
const self = this;
this.get('model.channel').save().then(function() {
self.send('closeModal');
}).catch(function(error) {
self.flash(extractError(error), 'error');
});
this.get('model.channel').save().then(() => {
this.send('closeModal');
}).catch(popupAjaxError);
} }
} }
}); });

View File

@ -1,9 +1,11 @@
import ModalFunctionality from 'discourse/mixins/modal-functionality'; import ModalFunctionality from 'discourse/mixins/modal-functionality';
import { extractError } from 'discourse/lib/ajax-error'; import { popupAjaxError } from 'discourse/lib/ajax-error';
import computed from "ember-addons/ember-computed-decorators"; import computed from "ember-addons/ember-computed-decorators";
export default Ember.Controller.extend(ModalFunctionality, { export default Ember.Controller.extend(ModalFunctionality, {
setupKeydown: function() { saveDisabled: false,
setupKeydown() {
Ember.run.schedule('afterRender', () => { Ember.run.schedule('afterRender', () => {
$('#chat_integration_edit_channel_modal').keydown(e => { $('#chat_integration_edit_channel_modal').keydown(e => {
if (e.keyCode === 13) { if (e.keyCode === 13) {
@ -13,31 +15,22 @@ export default Ember.Controller.extend(ModalFunctionality, {
}); });
}.on('init'), }.on('init'),
saveDisabled: function(){
return false;
}.property(),
@computed('model.rule.type') @computed('model.rule.type')
showCategory: function(type){ showCategory(type) {
return (type === "normal"); return type === "normal";
}, },
actions: { actions: {
cancel: function(){ cancel() {
this.send('closeModal'); this.send('closeModal');
}, },
save: function(){ save() {
if(this.get('saveDisabled')){return;}; if (this.get('saveDisabled')) return;
const self = this;
this.get('model.rule').save().then(function() {
self.send('closeModal');
}).catch(function(error) {
self.flash(extractError(error), 'error');
});
this.get('model.rule').save().then(() => {
this.send('closeModal');
}).catch(popupAjaxError);
} }
} }
}); });

View File

@ -1,8 +1,9 @@
import ModalFunctionality from 'discourse/mixins/modal-functionality'; import ModalFunctionality from 'discourse/mixins/modal-functionality';
import { ajax } from 'discourse/lib/ajax'; import { ajax } from 'discourse/lib/ajax';
import computed from "ember-addons/ember-computed-decorators";
export default Ember.Controller.extend(ModalFunctionality, { export default Ember.Controller.extend(ModalFunctionality, {
setupKeydown: function() { setupKeydown() {
Ember.run.schedule('afterRender', () => { Ember.run.schedule('afterRender', () => {
$('#chat_integration_test_modal').keydown(e => { $('#chat_integration_test_modal').keydown(e => {
if (e.keyCode === 13) { if (e.keyCode === 13) {
@ -12,42 +13,26 @@ export default Ember.Controller.extend(ModalFunctionality, {
}); });
}.on('init'), }.on('init'),
sendDisabled: function(){ @computed('model.topic_id')
if(this.get('model').topic_id){ sendDisabled(topicId) {
return false; return !topicId;
} },
return true;
}.property('model.topic_id'),
actions: { actions: {
send() {
if (this.get('sendDisabled')) return;
send: function(){
if(this.get('sendDisabled')){return;};
self = this;
this.set('loading', true); this.set('loading', true);
ajax("/admin/plugins/chat/test", { ajax("/admin/plugins/chat/test", {
data: { channel_id: this.get('model.channel.id'), data: {
topic_id: this.get('model.topic_id') channel_id: this.get('model.channel.id'),
}, topic_id: this.get('model.topic_id')
},
type: 'POST' type: 'POST'
}).then(function () { }).then(() => {
self.set('loading', false); this.set('loading', false);
self.flash(I18n.t('chat_integration.test_modal.success'), 'success'); this.flash(I18n.t('chat_integration.test_modal.success'), 'success');
}, function(e) { }.catch(popupAjaxError);
self.set('loading', false);
var response = e.jqXHR.responseJSON;
var error_key = 'chat_integration.test_modal.error';
if(response['error_key']){
error_key = response['error_key'];
}
self.flash(I18n.t(error_key), 'error');
});
} }
}
}); });

View File

@ -1,14 +1,11 @@
import RestModel from 'discourse/models/rest'; import RestModel from 'discourse/models/rest';
export default RestModel.extend({ export default RestModel.extend({
updateProperties() { updateProperties() {
var prop_names = ['data']; return this.getProperties(['data']);
return this.getProperties(prop_names);
}, },
createProperties() { createProperties() {
var prop_names = ['provider','data']; return this.getProperties(['provider','data']);
return this.getProperties(prop_names);
} }
}); });

View File

@ -1,6 +1,6 @@
import RestModel from 'discourse/models/rest'; import RestModel from 'discourse/models/rest';
import Category from 'discourse/models/category'; import Category from 'discourse/models/category';
import computed from "ember-addons/ember-computed-decorators"; import { default as computed, observes } from "ember-addons/ember-computed-decorators";
export default RestModel.extend({ export default RestModel.extend({
available_filters: [ available_filters: [
@ -22,21 +22,22 @@ export default RestModel.extend({
type: 'normal', type: 'normal',
error_key: null, error_key: null,
@observes('type')
removeUnneededInfo() {
const type = this.get('type');
removeUnneededInfo: function(){ if (type === 'normal') {
const type=this.get('type');
if(type==='normal'){
this.set('group_id', null); this.set('group_id', null);
}else{ } else {
this.set('category_id', null); this.set('category_id', null);
} }
}.observes('type'), },
@computed('category_id') @computed('category_id')
category(categoryId) { category(categoryId) {
if (categoryId){ if (categoryId){
return Category.findById(categoryId); return Category.findById(categoryId);
}else { } else {
return false; return false;
} }
}, },
@ -47,13 +48,10 @@ export default RestModel.extend({
}, },
updateProperties() { updateProperties() {
var prop_names = ['type','category_id','group_id','tags','filter']; return this.getProperties(['type','category_id','group_id','tags','filter']);
return this.getProperties(prop_names);
}, },
createProperties() { createProperties() {
var prop_names = ['type','channel_id', 'category_id','group_id','tags','filter']; return this.getProperties(['type','channel_id', 'category_id','group_id','tags','filter']);
return this.getProperties(prop_names);
} }
}); });

View File

@ -1,8 +1,7 @@
export default Discourse.Route.extend({ export default Discourse.Route.extend({
afterModel(model) { afterModel(model) {
if (model.totalRows > 0) {
if(model.totalRows > 0){ this.transitionTo('adminPlugins.chat.provider', model.get('firstObject').name);
this.transitionTo('adminPlugins.chat.provider', model.get('firstObject').name); }
}
} }
}); });

View File

@ -17,17 +17,18 @@ export default Discourse.Route.extend({
return rule; return rule;
})); }));
}); });
return value; return value;
}); });
}, },
serialize: function(model) { serialize(model) {
return { provider: model['provider'].get('id')}; return { provider: model['provider'].get('id') };
}, },
actions: { actions: {
closeModal: function(){ closeModal() {
if(this.get('controller.modalShowing')){ if (this.get('controller.modalShowing')) {
this.refresh(); this.refresh();
this.set('controller.modalShowing', false); this.set('controller.modalShowing', false);
} }
@ -35,9 +36,8 @@ export default Discourse.Route.extend({
return true; // Continue bubbling up, so the modal actually closes return true; // Continue bubbling up, so the modal actually closes
}, },
refresh: function(){ refresh() {
this.refresh(); this.refresh();
} }
} }
}); });

View File

@ -1,13 +1,13 @@
export default Discourse.Route.extend({ export default Discourse.Route.extend({
model() { model() {
return this.store.findAll('provider'); return this.store.findAll('provider');
}, },
actions: { actions: {
showSettings: function(){ showSettings() {
this.transitionTo('adminSiteSettingsCategory', 'plugins', { this.transitionTo('adminSiteSettingsCategory', 'plugins', {
queryParams: { filter: 'chat_integration'} queryParams: { filter: 'chat_integration'}
}); });
} }
} }
}); });

View File

@ -3,10 +3,10 @@
<form {{action "save" on="submit"}}> <form {{action "save" on="submit"}}>
<table> <table>
<tr class="input"> <tr class="input">
<td class="label"><label for='provider'>{{i18n "chat_integration.edit_channel_modal.provider"}}</label></td> <td class="label"><label for='provider'>{{i18n "chat_integration.edit_channel_modal.provider"}}</label></td>
<td> <td>
{{i18n (concat 'chat_integration.provider.' model.channel.provider '.title')}} {{i18n (concat 'chat_integration.provider.' model.channel.provider '.title')}}
</td> </td>
</tr> </tr>
<tr class="instructions"> <tr class="instructions">
@ -14,13 +14,13 @@
<td></td> <td></td>
</tr> </tr>
{{# each model.provider.channel_parameters as |param|}} {{#each model.provider.channel_parameters as |param|}}
<tr class="input"> <tr class="input">
<td class="label"><label for='param-{{param.key}}'>{{i18n (concat 'chat_integration.provider.' model.channel.provider '.param.' param.key '.title')}}</label></td> <td class="label"><label for='param-{{param.key}}'>{{i18n (concat 'chat_integration.provider.' model.channel.provider '.param.' param.key '.title')}}</label></td>
<td> <td>
{{text-field {{text-field
name=(concat 'param-' param.key) name=(concat 'param-' param.key)
value=(mut (get model.channel.data param.key)) value=(mut (get model.channel.data param.key))
}} }}
&nbsp; &nbsp;
@ -41,9 +41,15 @@
{{/d-modal-body}} {{/d-modal-body}}
<div class="modal-footer"> <div class="modal-footer">
{{d-button id="save-channel"
class='btn-primary btn-large'
action="save"
title="chat_integration.edit_channel_modal.save"
label="chat_integration.edit_channel_modal.save"
disabled=saveDisabled}}
{{d-button id="save_channel" class='btn-primary btn-large' action="save" title="chat_integration.edit_channel_modal.save" label="chat_integration.edit_channel_modal.save" disabled=saveDisabled}} {{d-button class="btn-large"
action="cancel"
{{d-button class="btn-large" action="cancel" title="chat_integration.edit_channel_modal.cancel" label="chat_integration.edit_channel_modal.cancel"}} title="chat_integration.edit_channel_modal.cancel"
label="chat_integration.edit_channel_modal.cancel"}}
</div> </div>

View File

@ -3,10 +3,10 @@
<form {{action "save" on="submit"}}> <form {{action "save" on="submit"}}>
<table> <table>
<tr class="input"> <tr class="input">
<td class="label"><label for='provider'>{{i18n "chat_integration.edit_rule_modal.provider"}}</label></td> <td class="label"><label for='provider'>{{i18n "chat_integration.edit_rule_modal.provider"}}</label></td>
<td> <td>
{{i18n (concat 'chat_integration.provider.' model.channel.provider '.title')}} {{i18n (concat 'chat_integration.provider.' model.channel.provider '.title')}}
</td> </td>
</tr> </tr>
<tr class="instructions"> <tr class="instructions">
@ -99,9 +99,15 @@
{{/d-modal-body}} {{/d-modal-body}}
<div class="modal-footer"> <div class="modal-footer">
{{d-button id="save-rule"
class='btn-primary btn-large'
action="save"
title="chat_integration.edit_rule_modal.save"
label="chat_integration.edit_rule_modal.save"
disabled=saveDisabled}}
{{d-button id="save_rule" class='btn-primary btn-large' action="save" title="chat_integration.edit_rule_modal.save" label="chat_integration.edit_rule_modal.save" disabled=saveDisabled}} {{d-button class="btn-large"
action="cancel"
{{d-button class="btn-large" action="cancel" title="chat_integration.edit_rule_modal.cancel" label="chat_integration.edit_rule_modal.cancel"}} title="chat_integration.edit_rule_modal.cancel"
label="chat_integration.edit_rule_modal.cancel"}}
</div> </div>

View File

@ -1,3 +1,3 @@
export default function() { export default function() {
this.route('transcript', {path: '/chat-transcript/:secret'}); this.route('transcript', { path: '/chat-transcript/:secret' });
}; };

View File

@ -2,23 +2,20 @@ import { ajax } from 'discourse/lib/ajax';
import { popupAjaxError } from 'discourse/lib/ajax-error'; import { popupAjaxError } from 'discourse/lib/ajax-error';
export default Discourse.Route.extend({ export default Discourse.Route.extend({
beforeModel: function(transition) { beforeModel(transition) {
if (this.currentUser) {
const secret = transition.params.transcript.secret;
if (Discourse.User.current()) {
var secret = transition.params.transcript.secret;
// User is logged in
this.replaceWith('discovery.latest').then(e => { this.replaceWith('discovery.latest').then(e => {
if (this.controllerFor('navigation/default').get('canCreateTopic')) { if (this.controllerFor('navigation/default').get('canCreateTopic')) {
// User can create topic
Ember.run.next(() => { Ember.run.next(() => {
ajax("/chat-transcript/"+secret).then(result => { ajax(`chat-transcript/${secret}`).then(result => {
e.send('createNewTopicViaParams', null, result['content'], null, null, null); e.send('createNewTopicViaParams', null, result['content'], null, null, null);
}, popupAjaxError); }, popupAjaxError);
}); });
} }
}); });
} else { } else {
// User is not logged in
this.session.set("shouldRedirectToUrl", window.location.href); this.session.set("shouldRedirectToUrl", window.location.href);
this.replaceWith('login'); this.replaceWith('login');
} }

View File

@ -1,27 +1,29 @@
{{#if anyErrors}}
{{#if anyErrors}} <div class="error">
<div class="error"> <i class="fa fa-exclamation-triangle"></i>
<i class="fa fa-exclamation-triangle"></i> <span class="error-message">{{i18n "chat_integration.channels_with_errors"}}</span>
<span class="error-message">{{i18n "chat_integration.channels_with_errors"}}</span>
</div>
{{/if}}
{{# each model.channels as |channel|}}
{{channel-details
channel=channel
provider=model.provider
store=store
refresh='refresh'
edit='editChannel'
test='testChannel'
createRule='createRule'
editRule='editRule'
}}
{{/each}}
<div class="table-footer">
<div class="pull-right">
{{d-button id="create_channel" action="createChannel" actionParam=model.provider icon="plus" title="chat_integration.create_channel" label="chat_integration.create_channel"}}
</div>
</div> </div>
{{/if}}
{{#each model.channels as |channel|}}
{{channel-details
channel=channel
provider=model.provider
store=store
refresh='refresh'
edit='editChannel'
test='testChannel'
createRule='createRule'
editRule='editRule'}}
{{/each}}
<div class="table-footer">
<div class="pull-right">
{{d-button id="create-channel"
action="createChannel"
actionParam=model.provider
icon="plus"
title="chat_integration.create_channel"
label="chat_integration.create_channel"}}
</div>
</div>

View File

@ -1,27 +1,25 @@
<div id="admin-plugin-chat"> <div id="admin-plugin-chat">
<div class="admin-controls">
<div class="span15">
<ul class="nav nav-pills">
{{#each model as |provider|}}
{{nav-item route='adminPlugins.chat.provider' routeParam=provider.name label=(concat 'chat_integration.provider.' provider.name '.title')}}
{{/each}}
</ul>
</div>
<div class="admin-controls"> <div class="pull-right">
<div class="span15"> {{#d-button
<ul class="nav nav-pills"> action="showSettings"
{{#each model as |provider|}} icon="gear"
{{nav-item route='adminPlugins.chat.provider' routeParam=provider.name label=(concat 'chat_integration.provider.' provider.name '.title')}} title="chat_integration.settings"
{{/each}} label="chat_integration.settings"}}
</ul> </div>
</div> </div>
<div class="pull-right">
{{#d-button
action="showSettings"
icon="gear"
title="chat_integration.settings"
label="chat_integration.settings"}}
{{/d-button}}
</div>
</div>
{{#if model.totalRows}}
{{else}}
{{i18n "chat_integration.no_providers"}}
{{/if}}
{{outlet}} {{#unless model.totalRows}}
{{i18n "chat_integration.no_providers"}}
{{/unless}}
{{outlet}}
</div> </div>

View File

@ -1,7 +1,7 @@
{{# each provider.channel_parameters as |param|}} {{#each provider.channel_parameters as |param|}}
{{#if param.hidden}}{{else}} {{#unless param.hidden}}
<span class='field-name'>{{i18n (concat 'chat_integration.provider.' channel.provider '.param.' param.key '.title')}}:</span> <span class='field-name'>{{i18n (concat 'chat_integration.provider.' channel.provider '.param.' param.key '.title')}}:</span>
<span class='field-value'>{{get channel.data param.key}}</span> <span class='field-value'>{{get channel.data param.key}}</span>
<br/> <br/>
{{/if}} {{/unless}}
{{/each}} {{/each}}

View File

@ -6,44 +6,41 @@
{{d-button class='cancel' action="delete" actionParam=channel icon="trash" title="chat_integration.delete_channel" label="chat_integration.delete_channel"}} {{d-button class='cancel' action="delete" actionParam=channel icon="trash" title="chat_integration.delete_channel" label="chat_integration.delete_channel"}}
</div> </div>
<span class='channel-title'> <span class='channel-title'>
{{#if channel.error_key}} {{#if channel.error_key}}
{{d-button action="showError" actionParam=channel.error_key class="delete btn-danger" icon="exclamation-triangle"}} {{d-button action="showError" actionParam=channel.error_key class="delete btn-danger" icon="exclamation-triangle"}}
{{/if}} {{/if}}
{{channel-data provider=provider channel=channel}} {{channel-data provider=provider channel=channel}}
</span> </span>
</div> </div>
<div class='channel-body'> <div class='channel-body'>
<table> <table>
<tr> <tr>
{{!-- <th></th> --}} <th>{{i18n "chat_integration.rule_table.filter"}}</th>
<th>{{i18n "chat_integration.rule_table.filter"}}</th> <th>{{i18n "chat_integration.rule_table.category"}}</th>
<th>{{i18n "chat_integration.rule_table.category"}}</th> {{#if siteSettings.tagging_enabled}}
<th>{{i18n "chat_integration.rule_table.tags"}}</th>
{{/if}}
{{#if siteSettings.tagging_enabled}} <th></th>
<th>{{i18n "chat_integration.rule_table.tags"}}</th> </tr>
{{/if}}
{{#each channel.rules as |rule|}}
{{rule-row rule=rule edit='editRule' refresh='refresh'}}
<th></th> {{/each}}
</tr> </table>
{{#each channel.rules as |rule|}}
{{rule-row rule=rule edit='editRule' refresh='refresh'}}
{{/each}}
</table>
</div> </div>
<div class='channel-footer'> <div class='channel-footer'>
<div class='pull-right'> <div class='pull-right'>
{{d-button action="createRule" actionParam=channel icon="plus" title="chat_integration.create_rule" label="chat_integration.create_rule"}} {{d-button action="createRule"
actionParam=channel
icon="plus"
title="chat_integration.create_rule"
label="chat_integration.create_rule"}}
</div> </div>
</div> </div>

View File

@ -1,4 +1,3 @@
<td> <td>
{{rule.filterName}} {{rule.filterName}}
</td> </td>
@ -31,5 +30,4 @@
<td> <td>
{{d-button action="edit" actionParam=rule icon="pencil" class="edit" title="chat_integration.rule_table.edit_rule"}} {{d-button action="edit" actionParam=rule icon="pencil" class="edit" title="chat_integration.rule_table.edit_rule"}}
{{d-button action="delete" actionParam=rule icon="trash-o" class="delete" title="chat_integration.rule_table.delete_rule"}} {{d-button action="delete" actionParam=rule icon="trash-o" class="delete" title="chat_integration.rule_table.delete_rule"}}
</td> </td>

View File

@ -1,97 +1,96 @@
#admin-plugin-chat{ #admin-plugin-chat {
table{ table {
margin-top:0; margin-top:0;
td:last-child{
white-space:nowrap;
}
td:not(:last-child){
width: 30%;
}
}
div.table-footer{ td:last-child {
margin: 10px; white-space:nowrap;
} }
div.error { td:not(:last-child) {
font-size: 1.1em; width: 30%;
font-weight:bold; }
max-width: 100%; }
div.table-footer {
margin: 10px;
}
div.error {
font-size: 1.1em;
font-weight:bold;
max-width: 100%;
margin-top: 10px; margin-top: 10px;
margin-bottom: 10px; margin-bottom: 10px;
background-color: $danger-low; background-color: $danger-low;
padding: 15px; padding: 15px;
} }
div.channel-details{ div.channel-details {
margin: 20px 10px; margin: 20px 10px;
border: 1px solid $primary-low;
border: 1px solid $primary-low; div.channel-header {
background: $primary-low;
padding: 10px;
overflow:auto;
div.channel-header{ .channel-title {
background: $primary-low;
padding: 10px;
overflow:auto;
.channel-title{
font-size: 1.3em; font-size: 1.3em;
.field-name{
font-weight: bold; .field-name {
font-weight: bold;
} }
}
}
}
div.channel-footer{
overflow:auto;
}
}
}
#chat_integration_edit_channel_modal, #chat_integration_test_modal, #chat_integration_edit_rule_modal{
table{
width:100%;
tr.input td{
padding-top: 10px;
&.label{
width: 100px;
label{
margin-bottom: 0px;
font-weight: bold;
}
}
}
tr.instructions label{
color: $primary-medium;
}
}
#channel-field{
width: 200px;
margin-bottom: 0px;
box-shadow: none;
}
.tag-chooser{
ul.select2-choices{
border: none;
background:none;
}
margin-bottom: 0px;
margin-top: 0px;
}
.field-name{
font-weight: bold;
} }
} div.channel-footer {
overflow:auto;
}
}
}
#chat_integration_edit_channel_modal,
#chat_integration_test_modal,
#chat_integration_edit_rule_modal {
table {
width:100%;
tr.input td {
padding-top: 10px;
&.label {
width: 100px;
label {
margin-bottom: 0px;
font-weight: bold;
}
}
}
tr.instructions label {
color: $primary-medium;
}
}
#channel-field {
width: 200px;
margin-bottom: 0px;
box-shadow: none;
}
.tag-chooser {
ul.select2-choices {
border: none;
background:none;
}
margin-bottom: 0px;
margin-top: 0px;
}
.field-name {
font-weight: bold;
}
}

View File

@ -22,11 +22,11 @@ module DiscourseChat
end end
def self.provider_names def self.provider_names
self.providers.map { |x| x::PROVIDER_NAME } self.providers.map! { |x| x::PROVIDER_NAME }
end end
def self.enabled_provider_names def self.enabled_provider_names
self.enabled_providers.map { |x| x::PROVIDER_NAME } self.enabled_providers.map! { |x| x::PROVIDER_NAME }
end end
def self.get_by_name(name) def self.get_by_name(name)
@ -71,7 +71,7 @@ module DiscourseChat
engines = [] engines = []
DiscourseChat::Provider.providers.each do |provider| DiscourseChat::Provider.providers.each do |provider|
engine = provider.constants.select do |constant| engine = provider.constants.select do |constant|
constant.to_s =~ (/Engine$/) && (not constant.to_s == "HookEngine") constant.to_s =~ (/Engine$/) && (constant.to_s != "HookEngine")
end.map(&provider.method(:const_get)).first end.map(&provider.method(:const_get)).first
if engine if engine

View File

@ -3,10 +3,11 @@ module DiscourseChat
module DiscordProvider module DiscordProvider
PROVIDER_NAME = "discord".freeze PROVIDER_NAME = "discord".freeze
PROVIDER_ENABLED_SETTING = :chat_integration_discord_enabled PROVIDER_ENABLED_SETTING = :chat_integration_discord_enabled
CHANNEL_PARAMETERS = [ CHANNEL_PARAMETERS = [
{ key: "name", regex: '^\S+' }, { key: "name", regex: '^\S+' },
{ key: "webhook_url", regex: '^https:\/\/discordapp\.com\/api\/webhooks\/', unique: true, hidden: true } { key: "webhook_url", regex: '^https:\/\/discordapp\.com\/api\/webhooks\/', unique: true, hidden: true }
] ].freeze
def self.send_message(url, message) def self.send_message(url, message)
http = Net::HTTP.new("discordapp.com", 443) http = Net::HTTP.new("discordapp.com", 443)
@ -18,12 +19,12 @@ module DiscourseChat
req.body = message.to_json req.body = message.to_json
response = http.request(req) response = http.request(req)
return response response
end end
def self.ensure_protocol(url) def self.ensure_protocol(url)
return url if not url.start_with?('//') return url if !url.start_with?('//')
return 'http:' + url "http:#{url}"
end end
def self.generate_discord_message(post) def self.generate_discord_message(post)
@ -49,22 +50,20 @@ module DiscourseChat
}] }]
} }
return message message
end end
def self.trigger_notification(post, channel) def self.trigger_notification(post, channel)
# Adding ?wait=true means that we actually get a success/failure response, rather than returning asynchronously # Adding ?wait=true means that we actually get a success/failure response, rather than returning asynchronously
webhook_url = channel.data['webhook_url'] + '?wait=true' webhook_url = "#{channel.data['webhook_url']}?wait=true"
message = generate_discord_message(post) message = generate_discord_message(post)
response = send_message(webhook_url, message) response = send_message(webhook_url, message)
if not response.kind_of? Net::HTTPSuccess if !response.kind_of?(Net::HTTPSuccess)
error_key = nil raise ::DiscourseChat::ProviderError.new(info: {
raise ::DiscourseChat::ProviderError.new info: { error_key: error_key, message: message, response_body: response.body } error_key: nil, message: message, response_body: response.body
})
end end
end end
end end

View File

@ -81,7 +81,7 @@ module DiscourseChat
response = send_message(webhook_url, message) response = send_message(webhook_url, message)
if not response.kind_of? Net::HTTPSuccess if !response.kind_of?(Net::HTTPSuccess)
error_key = nil error_key = nil
raise ::DiscourseChat::ProviderError.new info: { error_key: error_key, message: message, response_body: response.body } raise ::DiscourseChat::ProviderError.new info: { error_key: error_key, message: message, response_body: response.body }
end end

View File

@ -59,7 +59,7 @@ module DiscourseChat
response = send_message(channel.data['room_id'], message) response = send_message(channel.data['room_id'], message)
if not response.kind_of? Net::HTTPSuccess if !response.kind_of?(Net::HTTPSuccess)
error_key = nil error_key = nil
begin begin
responseData = JSON.parse(response.body) responseData = JSON.parse(response.body)

View File

@ -18,7 +18,7 @@ module DiscourseChat
response = self.do_api_request('setWebhook', message) response = self.do_api_request('setWebhook', message)
if not response['ok'] == true if response['ok'] != true
# If setting up webhook failed, disable provider # If setting up webhook failed, disable provider
SiteSetting.chat_integration_telegram_enabled = false SiteSetting.chat_integration_telegram_enabled = false
Rails.logger.error("Failed to setup telegram webhook. Message data= " + message.to_json + " response=" + response.to_json) Rails.logger.error("Failed to setup telegram webhook. Message data= " + message.to_json + " response=" + response.to_json)
@ -89,7 +89,7 @@ module DiscourseChat
response = sendMessage(message) response = sendMessage(message)
if not response['ok'] == true if response['ok'] != true
error_key = nil error_key = nil
if response['description'].include? 'chat not found' if response['description'].include? 'chat not found'
error_key = 'chat_integration.provider.telegram.errors.channel_not_found' error_key = 'chat_integration.provider.telegram.errors.channel_not_found'

View File

@ -53,7 +53,7 @@ module DiscourseChat
response = send_message(message) response = send_message(message)
if not response.kind_of? Net::HTTPSuccess if !response.kind_of?(Net::HTTPSuccess)
error_key = nil error_key = nil
error_key = 'chat_integration.provider.zulip.errors.does_not_exist' if response.body.include?('does not exist') error_key = 'chat_integration.provider.zulip.errors.does_not_exist' if response.body.include?('does not exist')
raise ::DiscourseChat::ProviderError.new info: { error_key: error_key, message: message, response_code: response.code, response_body: response.body } raise ::DiscourseChat::ProviderError.new info: { error_key: error_key, message: message, response_code: response.code, response_body: response.body }

View File

@ -114,9 +114,9 @@ describe 'Chat Controller', type: :request do
end end
it 'should fail for invalid provider' do it 'should fail for invalid provider' do
get '/admin/plugins/chat/channels.json', params: { provider: 'someprovider' } expect do
get '/admin/plugins/chat/channels.json', params: { provider: 'someprovider' }
expect(response).not_to be_success end.to raise_error(Discourse::InvalidParameters)
end end
end end

View File

@ -13,6 +13,7 @@ RSpec.shared_context "dummy provider" do
if @@raise_exception if @@raise_exception
raise @@raise_exception raise @@raise_exception
end end
@@sent_messages.push(post: post.id, channel: channel) @@sent_messages.push(post: post.id, channel: channel)
end end

View File

@ -10,41 +10,49 @@ acceptance("Chat Integration", {
object object
]; ];
}; };
server.get('/admin/plugins/chat/providers', () => { // eslint-disable-line no-undef server.get('/admin/plugins/chat/providers', () => { // eslint-disable-line no-undef
return response({ providers: [{name: 'dummy', id:'dummy',channel_parameters:[{key:'somekey', regex:"^\\S+$"}]}] }); return response({ providers: [{name: 'dummy', id:'dummy',channel_parameters:[{key:'somekey', regex:"^\\S+$"}]}] });
}); });
server.get('/admin/plugins/chat/channels', () => { // eslint-disable-line no-undef server.get('/admin/plugins/chat/channels', () => { // eslint-disable-line no-undef
return response({"channels":[{"id":97,"provider":"dummy","data":{somekey:"#general"},"rules":[ return response({"channels":[{"id":97,"provider":"dummy","data":{somekey:"#general"},"rules":[
{"id":98,"channel_id":97,"category_id":null,"team_id":null,"type":"normal","tags":[],"filter":"watch","error_key":null} {"id":98,"channel_id":97,"category_id":null,"team_id":null,"type":"normal","tags":[],"filter":"watch","error_key":null}
]}]}); ]}]});
}); });
server.post('/admin/plugins/chat/channels', () => { // eslint-disable-line no-undef server.post('/admin/plugins/chat/channels', () => { // eslint-disable-line no-undef
return response({ }); return response({ });
}); });
server.put('/admin/plugins/chat/channels/:id', () => { // eslint-disable-line no-undef server.put('/admin/plugins/chat/channels/:id', () => { // eslint-disable-line no-undef
return response({ }); return response({ });
}); });
server.delete('/admin/plugins/chat/channels/:id', () => { // eslint-disable-line no-undef server.delete('/admin/plugins/chat/channels/:id', () => { // eslint-disable-line no-undef
return response({ }); return response({ });
}); });
server.post('/admin/plugins/chat/rules', () => { // eslint-disable-line no-undef server.post('/admin/plugins/chat/rules', () => { // eslint-disable-line no-undef
return response({ }); return response({ });
}); });
server.put('/admin/plugins/chat/rules/:id', () => { // eslint-disable-line no-undef server.put('/admin/plugins/chat/rules/:id', () => { // eslint-disable-line no-undef
return response({ }); return response({ });
}); });
server.delete('/admin/plugins/chat/rules/:id', () => { // eslint-disable-line no-undef server.delete('/admin/plugins/chat/rules/:id', () => { // eslint-disable-line no-undef
return response({ }); return response({ });
}); });
server.post('/admin/plugins/chat/test', () => { // eslint-disable-line no-undef server.post('/admin/plugins/chat/test', () => { // eslint-disable-line no-undef
return response({ }); return response({ });
}); });
server.get('/groups/search.json', () => { // eslint-disable-line no-undef server.get('/groups/search.json', () => { // eslint-disable-line no-undef
return response([]); return response([]);
}); });
} }
}); });
test("Rules load successfully", assert => { test("Rules load successfully", assert => {
@ -60,21 +68,21 @@ test("Create channel works", assert => {
visit("/admin/plugins/chat"); visit("/admin/plugins/chat");
andThen(() => { andThen(() => {
click('#create_channel'); click('#create-channel');
}); });
andThen(() => { andThen(() => {
assert.ok(exists('#chat_integration_edit_channel_modal'), 'it displays the modal'); assert.ok(exists('#chat_integration_edit_channel_modal'), 'it displays the modal');
assert.ok(find('#save_channel').prop('disabled'), 'it disables the save button'); assert.ok(find('#save-channel').prop('disabled'), 'it disables the save button');
fillIn('#chat_integration_edit_channel_modal input', '#general'); fillIn('#chat_integration_edit_channel_modal input', '#general');
}); });
andThen(() => { andThen(() => {
assert.ok(find('#save_channel').prop('disabled') === false, 'it enables the save button'); assert.ok(find('#save-channel').prop('disabled') === false, 'it enables the save button');
}); });
andThen(() => { andThen(() => {
click('#save_channel'); click('#save-channel');
}); });
andThen(() => { andThen(() => {
@ -92,12 +100,12 @@ test("Edit channel works", assert => {
andThen(() => { andThen(() => {
assert.ok(exists('#chat_integration_edit_channel_modal'), 'it displays the modal'); assert.ok(exists('#chat_integration_edit_channel_modal'), 'it displays the modal');
assert.ok(!find('#save_channel').prop('disabled'), 'save is enabled'); assert.ok(!find('#save-channel').prop('disabled'), 'save is enabled');
fillIn('#chat_integration_edit_channel_modal input', ' general'); fillIn('#chat_integration_edit_channel_modal input', ' general');
}); });
andThen(() => { andThen(() => {
assert.ok(find('#save_channel').prop('disabled'), 'it disables the save button'); assert.ok(find('#save-channel').prop('disabled'), 'it disables the save button');
}); });
andThen(() => { andThen(() => {
@ -125,10 +133,10 @@ test("Create rule works", assert => {
andThen(() => { andThen(() => {
assert.ok(exists('#chat_integration_edit_rule_modal'), 'modal opens on edit'); assert.ok(exists('#chat_integration_edit_rule_modal'), 'modal opens on edit');
assert.ok(find('#save_rule').prop('disabled') === false, 'save is enabled'); assert.ok(find('#save-rule').prop('disabled') === false, 'save is enabled');
}); });
click('#save_rule'); click('#save-rule');
andThen(() => { andThen(() => {
assert.ok(!exists('#chat_integration_edit_rule_modal'), 'modal closes on save'); assert.ok(!exists('#chat_integration_edit_rule_modal'), 'modal closes on save');
@ -146,10 +154,10 @@ test("Edit rule works", assert => {
andThen(() => { andThen(() => {
assert.ok(exists('#chat_integration_edit_rule_modal'), 'modal opens on edit'); assert.ok(exists('#chat_integration_edit_rule_modal'), 'modal opens on edit');
assert.ok(find('#save_rule').prop('disabled') === false, 'it enables the save button'); assert.ok(find('#save-rule').prop('disabled') === false, 'it enables the save button');
}); });
click('#save_rule'); click('#save-rule');
andThen(() => { andThen(() => {
assert.ok(!exists('#chat_integration_edit_rule_modal'), 'modal closes on save'); assert.ok(!exists('#chat_integration_edit_rule_modal'), 'modal closes on save');