New admin interface for improved channel definitions

This commit is contained in:
David Taylor 2017-07-18 16:17:03 +01:00
parent 1ef9073027
commit bdb81191d7
24 changed files with 514 additions and 308 deletions

View File

@ -15,23 +15,19 @@ class DiscourseChat::ChatController < ApplicationController
render json:providers, root: 'providers'
end
def test_provider
def test
begin
requested_provider = params[:provider]
channel = params[:channel]
topic_id = params[:topic_id]
channel_id = params[:channel_id].to_i
topic_id = params[:topic_id].to_i
provider = ::DiscourseChat::Provider.get_by_name(requested_provider)
channel = DiscourseChat::Channel.find(channel_id)
if provider.nil? or not ::DiscourseChat::Provider.is_enabled(provider)
provider = ::DiscourseChat::Provider.get_by_name(channel.provider)
if not ::DiscourseChat::Provider.is_enabled(provider)
raise Discourse::NotFound
end
if defined? provider::PROVIDER_CHANNEL_REGEX
channel_regex = Regexp.new provider::PROVIDER_CHANNEL_REGEX
raise Discourse::InvalidParameters, 'Channel is not valid' if not channel_regex.match?(channel)
end
post = Topic.find(topic_id.to_i).posts.first
provider.trigger_notification(post, channel)
@ -48,23 +44,91 @@ class DiscourseChat::ChatController < ApplicationController
end
end
def list_channels
providers = ::DiscourseChat::Provider.enabled_providers.map {|x| x::PROVIDER_NAME}
requested_provider = params[:provider]
if not providers.include? requested_provider
raise Discourse::NotFound
end
channels = DiscourseChat::Channel.with_provider(requested_provider)
render_serialized channels, DiscourseChat::ChannelSerializer, root: 'channels'
end
def create_channel
begin
providers = ::DiscourseChat::Provider.enabled_providers.map {|x| x::PROVIDER_NAME}
requested_provider = params[:channel][:provider]
if not providers.include? requested_provider
raise Discourse::InvalidParameters, 'Provider is not valid'
end
allowed_keys = DiscourseChat::Provider.get_by_name(requested_provider)::CHANNEL_PARAMETERS.map{|p| p[:key].to_sym}
hash = params.require(:channel).permit(:provider, data:allowed_keys)
channel = DiscourseChat::Channel.new(hash)
if not channel.save(hash)
raise Discourse::InvalidParameters, 'Channel is not valid'
end
render_serialized channel, DiscourseChat::ChannelSerializer, root: 'channel'
rescue Discourse::InvalidParameters => e
render json: {errors: [e.message]}, status: 422
end
end
def update_channel
begin
channel = DiscourseChat::Channel.find(params[:id].to_i)
# rule.error_key = nil # Reset any error on the rule
allowed_keys = DiscourseChat::Provider.get_by_name(channel.provider)::CHANNEL_PARAMETERS.map{|p| p[:key].to_sym}
hash = params.require(:channel).permit(data:allowed_keys)
if not channel.update(hash)
raise Discourse::InvalidParameters, 'Channel is not valid'
end
render_serialized channel, DiscourseChat::ChannelSerializer, root: 'channel'
rescue Discourse::InvalidParameters => e
render json: {errors: [e.message]}, status: 422
end
end
def destroy_channel
rule = DiscourseChat::Channel.find(params[:id].to_i)
rule.destroy
render json: success_json
end
def list_rules
providers = ::DiscourseChat::Provider.enabled_providers.map {|x| x::PROVIDER_NAME}
requested_provider = params[:provider]
if providers.include? requested_provider
rules = DiscourseChat::Rule.with_provider(requested_provider)
else
if not providers.include? requested_provider
raise Discourse::NotFound
end
rules = DiscourseChat::Rule.with_provider(requested_provider)
render_serialized rules, DiscourseChat::RuleSerializer, root: 'rules'
end
def create_rule
begin
hash = params.require(:rule).permit(:provider, :channel, :filter, :category_id, tags:[])
hash = params.require(:rule).permit(:channel_id, :filter, :category_id, tags:[])
rule = DiscourseChat::Rule.new(hash)
@ -82,7 +146,7 @@ class DiscourseChat::ChatController < ApplicationController
begin
rule = DiscourseChat::Rule.find(params[:id].to_i)
rule.error_key = nil # Reset any error on the rule
hash = params.require(:rule).permit(:provider, :channel, :filter, :category_id, tags:[])
hash = params.require(:rule).permit(:filter, :category_id, tags:[])
if not rule.update(hash)
raise Discourse::InvalidParameters, 'Rule is not valid'

View File

@ -17,7 +17,7 @@ class DiscourseChat::Rule < DiscourseChat::PluginModel
def channel_valid?
# Validate category
if not (channel_id.nil? or DiscourseChat::Channel.where(id: channel_id).exists?)
if not (DiscourseChat::Channel.where(id: channel_id).exists?)
errors.add(:channel_id, "#{channel_id} is not a valid channel id")
end
end

View File

@ -4,10 +4,15 @@ module DiscourseChat
AdminEngine.routes.draw do
get "" => "chat#respond"
get '/providers' => "chat#list_providers"
post '/test' => "chat#test_provider"
post '/test' => "chat#test"
get '/channels' => "chat#list_channels"
post '/channels' => "chat#create_channel"
put '/channels/:id' => "chat#update_channel"
delete '/channels/:id' => "chat#destroy_channel"
get '/rules' => "chat#list_rules"
put '/rules' => "chat#create_rule"
post '/rules' => "chat#create_rule"
put '/rules/:id' => "chat#update_rule"
delete '/rules/:id' => "chat#destroy_rule"

View File

@ -0,0 +1,11 @@
require_relative './rule_serializer'
class DiscourseChat::ChannelSerializer < ApplicationSerializer
attributes :id, :provider, :data, :rules
def rules
object.rules.map do |rule|
DiscourseChat::RuleSerializer.new(rule, root:false)
end
end
end

View File

@ -1,3 +1,3 @@
class DiscourseChat::RuleSerializer < ActiveModel::Serializer
attributes :id, :provider, :channel, :category_id, :tags, :filter, :error_key
class DiscourseChat::RuleSerializer < ApplicationSerializer
attributes :id, :channel_id, :category_id, :tags, :filter, :error_key
end

View File

@ -0,0 +1,7 @@
import buildPluginAdapter from 'admin/adapters/build-plugin';
import Rule from 'discourse/plugins/discourse-chat-integration/admin/models/rule'
export default buildPluginAdapter('chat').extend({
});

View File

@ -0,0 +1,36 @@
import { popupAjaxError } from 'discourse/lib/ajax-error';
export default Ember.Component.extend({
classNames: ['channel-details'],
actions: {
refresh: function(){
this.sendAction('refresh');
},
delete(channel){
bootbox.confirm(I18n.t("chat_integration.channel_delete_confirm"), I18n.t("no_value"), I18n.t("yes_value"), result => {
if (result) {
channel.destroyRecord().then(() => {
this.send('refresh');
}).catch(popupAjaxError)
}
});
},
edit(channel){
this.sendAction('edit', channel)
},
test(channel){
this.sendAction('test', channel)
},
createRule(channel){
var newRule = this.get('store').createRecord('rule',{channel_id: channel.id});
channel.rules.pushObject(newRule)
}
}
});

View File

@ -0,0 +1,38 @@
import { popupAjaxError } from 'discourse/lib/ajax-error';
export default Ember.Component.extend({
tagName: 'tr',
editing: false,
autoEdit: function(){
if(!this.get('rule').id){
this.set('editing', true);
}
}.on('init'),
actions: {
edit: function(){
this.set('editing', true);
},
cancel: function(){
this.send('refresh');
},
save: function(){
this.get('rule').save().then(result => {
this.send('refresh');
}).catch(popupAjaxError);
},
delete(rule){
rule.destroyRecord().then(() => {
this.send('refresh');
}).catch(popupAjaxError)
},
refresh: function(){
this.sendAction('refresh');
}
}
});

View File

@ -7,42 +7,38 @@ import { popupAjaxError } from 'discourse/lib/ajax-error';
export default Ember.Controller.extend({
modalShowing: false,
anyErrors: function(){
var anyErrors = false;
this.get('model.rules').forEach(function(rule){
if(rule.error_key){
this.get('model.channels').forEach(function(channel){
if(channel.error_key){
anyErrors = true;
}
});
return anyErrors;
}.property('model.rules'),
}.property('model.channels'),
actions:{
create(){
createChannel(){
this.set('modalShowing', true);
var model = {rule: this.store.createRecord('rule',{provider: this.get('model.provider').id}), provider:this.get('model.provider')};
showModal('admin-plugins-chat-edit-rule', { model: model, admin: true });
var 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 });
},
edit(rule){
editChannel(channel){
this.set('modalShowing', true);
var model = {rule: rule, provider:this.get('model.provider')};
showModal('admin-plugins-chat-edit-rule', { model: model, admin: true });
},
delete(rule){
const self = this;
rule.destroyRecord().then(function() {
self.send('refresh');
}).catch(popupAjaxError)
var model = {channel: channel, provider: this.get('model.provider')};
showModal('admin-plugins-chat-edit-channel', { model: model, admin: true });
},
testChannel(channel){
this.set('modalShowing', true);
var model = {channel:channel}
showModal('admin-plugins-chat-test', { model: model, admin: true });
},
showError(error_key){
bootbox.alert(I18n.t(error_key));
},
test(){
this.set('modalShowing', true);
var model = {provider:this.get('model.provider'), channel:''}
showModal('admin-plugins-chat-test', { model: model, admin: true });
}
}

View File

@ -0,0 +1,94 @@
import Rule from 'discourse/plugins/discourse-chat-integration/admin/models/rule'
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';
export default Ember.Controller.extend(ModalFunctionality, {
initThing: function(){
console.log("Initialising controller");
console.log(this.get('model.data'));
}.on('init'),
// The validation property must be defined at runtime since the possible parameters vary by provider
setupValidations: function(){
if(this.get('model.provider')){
var theKeys = this.get('model.provider.channel_parameters').map( ( param ) => param['key'] );
Ember.defineProperty(this,'paramValidation',Ember.computed('model.channel.data.{' + theKeys.join(',') + '}',this._paramValidation));
}
}.observes('model'),
validate(parameter){
var regString = parameter.regex;
var regex = new RegExp(regString);
var val = this.get('model.channel.data.'+parameter.key);
if(val == ""){ // Fail silently if field blank
return InputValidation.create({
failed: true,
});
}else if(!regString){ // Pass silently if no regex available for provider
return InputValidation.create({
ok: true,
});
}else if(regex.test(val)){ // Test against regex
return InputValidation.create({
ok: true,
reason: I18n.t('chat_integration.edit_channel_modal.channel_validation.ok')
});
}else{ // Failed regex
return InputValidation.create({
failed: true,
reason: I18n.t('chat_integration.edit_channel_modal.channel_validation.fail')
});
}
},
_paramValidation: function(){
var response = {}
var parameters = this.get('model.provider.channel_parameters');
parameters.forEach(parameter => {
response[parameter.key] = this.validate(parameter);
});
return response;
},
saveDisabled: function(){
var validations = this.get('paramValidation');
if(!validations){ return true }
var invalid = false;
Object.keys(validations).forEach(key =>{
if(!validations[key]){
invalid = true;
}
if(!validations[key]['ok']){
invalid = true;
}
});
return invalid;
}.property('paramValidation'),
actions: {
cancel: function(){
this.send('closeModal');
},
save: function(){
const self = this;
this.get('model.channel').save().then(function(result) {
self.send('closeModal');
}).catch(function(error) {
self.flash(extractError(error), 'error');
});
}
}
});

View File

@ -1,61 +0,0 @@
import Rule from 'discourse/plugins/discourse-chat-integration/admin/models/rule'
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';
export default Ember.Controller.extend(ModalFunctionality, {
model: Rule.create({}),
channelValidation: function(){
var regString = this.get('model.provider.channel_regex');
var regex = new RegExp(regString);
var val = this.get('model.rule.channel');
if(val == ""){ // Fail silently if field blank
return InputValidation.create({
failed: true,
});
}else if(!regString){ // Pass silently if no regex available for provider
return InputValidation.create({
ok: true,
});
}else if(regex.test(val)){ // Test against regex
return InputValidation.create({
ok: true,
reason: I18n.t('chat_integration.edit_rule_modal.channel_validation.ok')
});
}else{ // Failed regex
return InputValidation.create({
failed: true,
reason: I18n.t('chat_integration.edit_rule_modal.channel_validation.fail')
});
}
}.property('model.rule.channel'),
saveDisabled: function(){
if(this.get('channelValidation.failed')){ return true }
return false;
}.property('channelValidation.failed'),
actions: {
cancel: function(){
this.send('closeModal');
},
save: function(){
const self = this;
this.get('model.rule').update().then(function(result) {
self.send('closeModal');
}).catch(function(error) {
self.flash(extractError(error), 'error');
});
}
}
});

View File

@ -3,11 +3,11 @@ import { ajax } from 'discourse/lib/ajax';
export default Ember.Controller.extend(ModalFunctionality, {
sendDisabled: function(){
if(this.get('model').topic_id && this.get('model').channel){
if(this.get('model').topic_id){
return false
}
return true
}.property('model.topic_id', 'model.channel'),
}.property('model.topic_id'),
actions: {
@ -15,8 +15,7 @@ export default Ember.Controller.extend(ModalFunctionality, {
self = this;
this.set('loading', true);
ajax("/admin/plugins/chat/test", {
data: { provider: this.get('model.provider.name'),
channel: this.get('model.channel'),
data: { channel_id: this.get('model.channel.id'),
topic_id: this.get('model.topic_id')
},
type: 'POST'

View File

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

View File

@ -11,9 +11,8 @@ export default RestModel.extend({
category_id: null,
tags: null,
provider: '',
channel: '',
filter: null,
channel_id: null,
filter: 'watch',
error_key: null,
@computed('category_id')
@ -31,12 +30,13 @@ export default RestModel.extend({
},
updateProperties() {
var prop_names = ['category_id','provider','channel', 'tags','filter'];
var prop_names = ['category_id','tags','filter'];
return this.getProperties(prop_names);
},
createProperties() {
return this.updateProperties();
var prop_names = ['channel_id', 'category_id','tags','filter'];
return this.getProperties(prop_names);
}
});

View File

@ -1,12 +1,20 @@
import Rule from 'discourse/plugins/discourse-chat-integration/admin/models/rule'
import { ajax } from 'discourse/lib/ajax';
export default Discourse.Route.extend({
model(params, transition) {
return Ember.RSVP.hash({
rules: this.store.find('rule', {provider: params.provider}),
channels: this.store.findAll('channel', {provider: params.provider}),
provider: this.modelFor("admin-plugins-chat").findBy('id',params.provider)
}).then(value => {
value.channels.forEach(channel => {
channel.set('rules', channel.rules.map(rule => {
rule = this.store.createRecord('rule', rule);
rule.channel = channel;
return rule;
}));
});
return value;
});
},

View File

@ -0,0 +1,49 @@
{{#d-modal-body id="chat_integration_edit_channel_modal" title="chat_integration.edit_channel_modal.title"}}
<div>
<form>
<table>
<tr class="input">
<td class="label"><label for='provider'>{{i18n "chat_integration.edit_channel_modal.provider"}}</label></td>
<td>
{{i18n (concat 'chat_integration.provider.' model.channel.provider '.title')}}
</td>
</tr>
<tr class="instructions">
<td></td>
<td></td>
</tr>
{{# each model.provider.channel_parameters as |param|}}
<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>
{{text-field
name=(concat 'param-' param.key)
value=(mut (get model.channel.data param.key))
}}
&nbsp;
{{#if (get model.channel.data param.key)}}
{{input-tip validation=(get paramValidation param.key)}}
{{/if}}
</td>
</tr>
<tr class="instructions">
<td></td>
<td><label>{{i18n (concat 'chat_integration.provider.' model.channel.provider '.param.' param.key '.help')}}</label></td>
</tr>
{{/each}}
</table>
</form>
</div>
{{/d-modal-body}}
<div class="modal-footer">
{{d-button id="save_rule" 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" title="chat_integration.edit_channel_modal.cancel" label="chat_integration.edit_channel_modal.cancel"}}
</div>

View File

@ -1,87 +0,0 @@
{{#d-modal-body id="chat_integration_edit_rule_modal" title="chat_integration.edit_rule_modal.title"}}
<div>
<form>
<table>
<tr class="input">
<td class="label"><label for='provider'>{{i18n "chat_integration.edit_rule_modal.provider"}}</label></td>
<td>
{{i18n (concat 'chat_integration.provider.' model.rule.provider '.title')}}
</td>
</tr>
<tr class="instructions">
<td></td>
<td></td>
</tr>
<tr class="input">
<td class="label"><label for='channel'>{{i18n "chat_integration.edit_rule_modal.channel"}}</label></td>
<td>
{{text-field
name="channel"
value=model.rule.channel
autofocus="autofocus"
id="channel-field"}}
&nbsp;{{input-tip validation=channelValidation}}
</td>
</tr>
<tr class="instructions">
<td></td>
<td><label>{{i18n (concat 'chat_integration.provider.' model.rule.provider '.channel_instructions')}}</label></td>
</tr>
<tr class="input">
<td class="label"><label for='filter'>{{i18n "chat_integration.edit_rule_modal.filter"}}</label></td>
<td>
{{combo-box name="filter" content=model.rule.available_filters value=model.rule.filter}}
</td>
</tr>
<tr class="instructions">
<td></td>
<td><label>{{i18n 'chat_integration.edit_rule_modal.instructions.filter'}}</label></td>
</tr>
<tr class="input">
<td class="label"><label for='category'>{{i18n "chat_integration.edit_rule_modal.category"}}</label></td>
<td>
{{category-chooser
name="category"
value=model.rule.category_id
rootNoneLabel="chat_integration.all_categories"
rootNone=true
overrideWidths=false
}}
</td>
</tr>
<tr class="instructions">
<td></td>
<td><label>{{i18n 'chat_integration.edit_rule_modal.instructions.category'}}</label></td>
</tr>
{{#if siteSettings.tagging_enabled}}
<tr class="input">
<td class="label"><label for='tags'>{{i18n "chat_integration.edit_rule_modal.tags"}}</label></td>
<td>
{{tag-chooser placeholderKey="chat_integration.all_tags" name="tags" tags=model.rule.tags}}
</td>
</tr>
<tr class="instructions">
<td></td>
<td><label>{{i18n 'chat_integration.edit_rule_modal.instructions.tags'}}</label></td>
</tr>
{{/if}}
</table>
</form>
</div>
{{/d-modal-body}}
<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 class="btn-large" action="cancel" title="chat_integration.edit_rule_modal.cancel" label="chat_integration.edit_rule_modal.cancel"}}
</div>

View File

@ -3,34 +3,7 @@
<form>
<table>
<tr class="input">
<td class="label"><label for='provider'>{{i18n "chat_integration.test_modal.provider"}}</label></td>
<td>
{{i18n (concat 'chat_integration.provider.' model.provider.id '.title')}}
</td>
</tr>
<tr class="instructions">
<td></td>
<td></td>
</tr>
<tr class="input">
<td class="label"><label for='channel'>{{i18n "chat_integration.test_modal.channel"}}</label></td>
<td>
{{text-field
name="channel"
value=model.channel
autofocus="autofocus"
id="channel-field"}}
{{!-- &nbsp;{{input-tip validation=channelValidation}} --}}
</td>
</tr>
<tr class="instructions">
<td></td>
<td><label>{{i18n (concat 'chat_integration.provider.' model.provider.id '.channel_instructions')}}</label></td>
</tr>
<tr class="input">
<td class="label"><label for='channel'>{{i18n "chat_integration.test_modal.topic"}}</label></td>
<td>

View File

@ -6,70 +6,13 @@
</div>
{{/if}}
<table>
<tr>
<th>{{i18n "chat_integration.rule_table.channel"}}</th>
<th>{{i18n "chat_integration.rule_table.filter"}}</th>
<th>{{i18n "chat_integration.rule_table.category"}}</th>
{{#if siteSettings.tagging_enabled}}
<th>{{i18n "chat_integration.rule_table.tags"}}</th>
{{/if}}
<th></th>
</tr>
{{#each model.rules as |rule|}}
<tr>
<td>
{{#if rule.error_key}}
{{d-button action="showError" actionParam=rule.error_key class="delete btn-danger" icon="exclamation-triangle"}}
{{/if}}
{{rule.channel}}
</td>
<td>{{rule.filterName}}</td>
<td>
{{#if rule.category}}
{{category-link rule.category allowUncategorized="true" link="false"}}
{{else}}
{{i18n "chat_integration.all_categories"}}
{{/if}}
</td>
{{#if siteSettings.tagging_enabled}}
<td>
{{#if rule.tags}}
{{rule.tags}}
{{else}}
{{i18n "chat_integration.all_tags"}}
{{/if}}
</td>
{{/if}}
<td>
{{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"}}
</td>
</tr>
{{/each}}
</table>
{{# each model.channels as |channel|}}
{{channel-details channel=channel provider=provider store=store refresh='refresh' edit='editChannel' test='testChannel'}}
{{/each}}
<div class="table-footer">
{{d-button id="test_provider" action="test" actionParam=model.provider icon="rocket" title="chat_integration.test_provider" label="chat_integration.test_provider"}}
<div class="pull-right">
{{d-button id="create_rule" action="create" actionParam=model.provider icon="plus" title="chat_integration.create_rule" label="chat_integration.create_rule"}}
{{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

@ -0,0 +1,40 @@
<div class='channel-header'>
<span class='channel-title'>{{channel.data.identifier}}</span>
<div class='pull-right'>
{{d-button action="edit" actionParam=channel icon="pencil" title="chat_integration.edit_channel" label="chat_integration.edit_channel"}}
{{d-button action="test" actionParam=channel icon="rocket" title="chat_integration.test_channel" label="chat_integration.test_channel"}}
{{d-button class='cancel' action="delete" actionParam=channel icon="trash" title="chat_integration.delete_channel" label="chat_integration.delete_channel"}}
</div>
</div>
<div class='channel-body'>
<table>
<tr>
{{!-- <th></th> --}}
<th>{{i18n "chat_integration.rule_table.filter"}}</th>
<th>{{i18n "chat_integration.rule_table.category"}}</th>
{{#if siteSettings.tagging_enabled}}
<th>{{i18n "chat_integration.rule_table.tags"}}</th>
{{/if}}
<th></th>
</tr>
{{#each channel.rules as |rule|}}
{{rule-row rule=rule refresh=refresh}}
{{/each}}
</table>
</div>
<div class='channel-footer'>
<div class='pull-right'>
{{d-button action="createRule" actionParam=channel icon="plus" title="chat_integration.create_rule" label="chat_integration.create_rule"}}
</div>
</div>

View File

@ -0,0 +1,53 @@
<td>
{{#if editing}}
{{combo-box name="filter" content=rule.available_filters value=rule.filter}}
{{else}}
{{rule.filterName}}
{{/if}}
</td>
<td>
{{#if editing}}
{{category-chooser
name="category"
value=rule.category_id
rootNoneLabel="chat_integration.all_categories"
rootNone=true
overrideWidths=false
}}
{{else}}
{{#if rule.category}}
{{category-link rule.category allowUncategorized="true" link="false"}}
{{else}}
{{i18n "chat_integration.all_categories"}}
{{/if}}
{{/if}}
</td>
{{#if siteSettings.tagging_enabled}}
<td>
{{#if editing}}
{{tag-chooser placeholderKey="chat_integration.all_tags" name="tags" tags=rule.tags}}
{{else}}
{{#if rule.tags}}
{{rule.tags}}
{{else}}
{{i18n "chat_integration.all_tags"}}
{{/if}}
{{/if}}
</td>
{{/if}}
<td>
{{#if editing}}
{{d-button action="save" actionParam=rule icon="check" class="ok" title="chat_integration.rule_table.save_rule"}}
{{d-button action="cancel" actionParam=rule icon="times" class="cancel" title="chat_integration.rule_table.cancel_edit"}}
{{else}}
{{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"}}
{{/if}}
</td>

View File

@ -1,6 +1,7 @@
#admin-plugin-chat{
table{
margin-top:0;
td:last-child{
white-space:nowrap;
}
@ -25,10 +26,33 @@
}
div.channel-details{
margin: 20px 10px;
border: 1px solid dark-light-diff($primary, $secondary, 90%, -75%);
div.channel-header{
background: dark-light-diff($primary, $secondary, 90%, -75%);
padding: 10px;
overflow:auto;
.channel-title{
font-weight: bold;
font-size: 1.3em;
}
}
div.channel-footer{
overflow:auto;
}
}
}
#chat_integration_edit_rule_modal, #chat_integration_test_modal{
#chat_integration_edit_channel_modal, #chat_integration_test_modal{
table{
width:100%;
@ -66,4 +90,4 @@
}
}
}

View File

@ -9,7 +9,11 @@ en:
all_categories: "(all categories)"
all_tags: "(all tags)"
create_rule: "Create Rule"
test_provider: "Test Provider"
create_channel: "Create Channel"
delete_channel: "Delete"
test_channel: "Test"
edit_channel: "Edit"
channel_delete_confirm: "Are you sure you want to delete this channel? All associated rules will be deleted."
test_modal:
title: "Send a test message"
provider: "Provider"
@ -30,23 +34,15 @@ en:
tags: "Tags"
edit_rule: "Edit"
delete_rule: "Delete"
edit_rule_modal:
title: "Edit Rule"
save: "Save Rule"
edit_channel_modal:
title: "Edit Channel"
save: "Save Channel"
cancel: "Cancel"
provider: "Provider"
category: "Category"
tags: "Tags"
channel: "Channel"
filter: "Filter"
channel_validation:
ok: "Valid"
fail: "Invalid channel format"
instructions:
filter: "Notification level. Mute overrides other matching rules."
category: "This rule will only apply to topics in the specified category."
tags: "If specified, this rule will only apply to topics which have at least one of these tags."
fail: "Invalid format"
provider:
#######################################
@ -54,7 +50,10 @@ en:
#######################################
slack:
title: "Slack"
channel_instructions: "e.g. #channel, @username."
param:
identifier:
title: Channel
help: "e.g. #channel, @username."
errors:
action_prohibited: "The bot does not have permission to post to that channel"
channel_not_found: "The specified channel does not exist on slack"

View File

@ -19,6 +19,7 @@ after_initialize do
require_relative "app/models/rule"
require_relative "app/models/channel"
require_relative "app/serializers/channel_serializer"
require_relative "app/serializers/rule_serializer"
require_relative "app/controllers/chat_controller"