DEV: Ember upgrade (#155)

This commit is contained in:
Keegan George 2023-01-23 10:30:48 -08:00 committed by GitHub
parent 91d2cfb952
commit bdc2f27a2a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 772 additions and 757 deletions

View File

@ -0,0 +1,19 @@
{{#each @provider.channel_parameters as |param|}}
{{#unless param.hidden}}
<div class="channel-info">
<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>
<br />
</div>
{{/unless}}
{{/each}}

View File

@ -1,5 +0,0 @@
import Component from "@ember/component";
export default Component.extend({
classNames: ["channel-info"],
});

View File

@ -0,0 +1,78 @@
<div class="channel-details">
<div class="channel-header">
<div class="pull-right">
<DButton
@icon="pencil-alt"
@title="chat_integration.edit_channel"
@label="chat_integration.edit_channel"
@action={{action @editChannel @channel}}
/>
<DButton
@icon="rocket"
@title="chat_integration.test_channel"
@label="chat_integration.test_channel"
@action={{action @test @channel}}
@class="btn-chat-test"
/>
<DButton
@icon="trash-alt"
@title="chat_integration.delete_channel"
@label="chat_integration.delete_channel"
@action={{action this.deleteChannel @channel}}
@class="cancel delete-channel"
/>
</div>
<span class="channel-title">
{{#if @channel.error_key}}
<DButton
@class="delete btn-danger"
@icon="exclamation-triangle"
@action={{action @showError @channel}}
/>
{{/if}}
<ChannelData @provider={{@provider}} @channel={{@channel}} />
</span>
</div>
<div class="channel-body">
<table>
<thead>
<tr>
<th>{{i18n "chat_integration.rule_table.filter"}}</th>
<th>{{i18n "chat_integration.rule_table.category"}}</th>
{{#if this.siteSettings.tagging_enabled}}
<th>{{i18n "chat_integration.rule_table.tags"}}</th>
{{/if}}
<th></th>
</tr>
</thead>
<tbody>
{{#each @channel.rules as |rule|}}
<RuleRow
@rule={{rule}}
@edit={{action @editRuleWithChannel rule @channel}}
@refresh={{@refresh}}
/>
{{/each}}
</tbody>
</table>
</div>
<div class="channel-footer">
<div class="pull-right">
<DButton
@class=""
@icon="plus"
@title="chat_integration.create_rule"
@label="chat_integration.create_rule"
@action={{action @createRule @channel}}
/>
</div>
</div>
</div>

View File

@ -1,27 +1,23 @@
import Component from "@ember/component"; import Component from "@glimmer/component";
import { popupAjaxError } from "discourse/lib/ajax-error"; import { popupAjaxError } from "discourse/lib/ajax-error";
import I18n from "I18n"; import I18n from "I18n";
import { inject as service } from "@ember/service"; import { inject as service } from "@ember/service";
import { action } from "@ember/object";
export default Component.extend({ export default class ChannelDetails extends Component {
dialog: service(), @service dialog;
classNames: ["channel-details"], @service siteSettings;
actions: { @action
deleteChannel(channel) { deleteChannel(channel) {
this.dialog.deleteConfirm({ this.dialog.deleteConfirm({
message: I18n.t("chat_integration.channel_delete_confirm"), message: I18n.t("chat_integration.channel_delete_confirm"),
didConfirm: () => { didConfirm: () => {
return channel return channel
.destroyRecord() .destroyRecord()
.then(() => this.refresh()) .then(() => this.args.refresh())
.catch(popupAjaxError); .catch(popupAjaxError);
}, },
}); });
}, }
}
editRule(rule) {
this.editRuleWithChannel(rule, this.get("channel"));
},
},
});

View File

@ -0,0 +1,42 @@
<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>
<Input
name={{concat "param-" @param.key}}
@value={{this.inputValue}}
{{on "change" this.updateValue}}
/>
<InputTip @validation={{this.validate}} />
</td>
</tr>
<tr class="chat-instructions">
<td></td>
<td>
<label>
{{i18n
(concat
"chat_integration.provider."
@model.channel.provider
".param."
@param.key
".help"
)
}}
</label>
</td>
</tr>

View File

@ -0,0 +1,51 @@
import Component from "@glimmer/component";
import EmberObject, { action } from "@ember/object";
import { tracked } from "@glimmer/tracking";
import I18n from "I18n";
export default class ChannelParamRow extends Component {
@tracked inputValue = this.args.model.channel.data[this.args.param.key] || "";
get validate() {
const parameter = this.args.param;
const regString = parameter.regex;
const regex = new RegExp(regString);
if (this.inputValue === "") {
// Fail silently if field blank
this.args.isValidParams(false);
return EmberObject.create({
failed: true,
});
} else if (!regString) {
// Pass silently if no regex available for provider
this.args.isValidParams(true);
return EmberObject.create({
ok: true,
});
} else if (regex.test(this.inputValue)) {
// Test against regex
this.args.isValidParams(true);
return EmberObject.create({
ok: true,
reason: I18n.t(
"chat_integration.edit_channel_modal.channel_validation.ok"
),
});
} else {
// Failed regex
this.args.isValidParams(false);
return EmberObject.create({
failed: true,
reason: I18n.t(
"chat_integration.edit_channel_modal.channel_validation.fail"
),
});
}
}
@action
updateValue(event) {
this.args.model.channel.data[this.args.param.key] = event.target.value;
}
}

View File

@ -0,0 +1,47 @@
<tr>
<td>
{{@rule.filterName}}
</td>
<td>
{{#if this.isCategory}}
{{#if @rule.category}}
{{category-link @rule.category allowUncategorized="true" link="false"}}
{{else}}
{{i18n "chat_integration.all_categories"}}
{{/if}}
{{else if this.isMention}}
{{i18n "chat_integration.group_mention_template" name=@rule.group_name}}
{{else if this.isMessage}}
{{i18n "chat_integration.group_message_template" name=@rule.group_name}}
{{/if}}
</td>
<td>
{{#if this.siteSettings.tagging_enabled}}
{{#if @rule.tags}}
{{@rule.tags}}
{{else}}
{{i18n "chat_integration.all_tags"}}
{{/if}}
{{/if}}
</td>
<td>
<DButton
@class="edit"
@icon="pencil-alt"
@title="chat_integration.rule_table.edit_rule"
@action={{@edit}}
@actionParam={{@rule}}
/>
<DButton
@class="delete"
@icon="far-trash-alt"
@title="chat_integration.rule_table.delete_rule"
@action={{action this.delete}}
@actionParam={{@rule}}
/>
</td>
</tr>

View File

@ -1,31 +1,27 @@
import Component from "@ember/component"; import { action } from "@ember/object";
import Component from "@glimmer/component";
import { popupAjaxError } from "discourse/lib/ajax-error"; import { popupAjaxError } from "discourse/lib/ajax-error";
import computed from "discourse-common/utils/decorators"; import { inject as service } from "@ember/service";
export default class RuleRow extends Component {
@service siteSettings;
export default Component.extend({ get isCategory() {
tagName: "tr", return this.args.rule.type === "normal";
}
@computed("rule.type") get isMessage() {
isCategory(type) { return this.args.rule.type === "group_message";
return type === "normal"; }
},
@computed("rule.type") get isMention() {
isMessage(type) { return this.args.rule.type === "group_mention";
return type === "group_message"; }
},
@computed("rule.type") @action
isMention(type) {
return type === "group_mention";
},
actions: {
delete(rule) { delete(rule) {
rule rule
.destroyRecord() .destroyRecord()
.then(() => this.refresh()) .then(() => this.args.refresh())
.catch(popupAjaxError); .catch(popupAjaxError);
}, }
}, }
});

View File

@ -1,12 +1,20 @@
import Controller from "@ember/controller"; import Controller from "@ember/controller";
import showModal from "discourse/lib/show-modal"; import showModal from "discourse/lib/show-modal";
import computed from "discourse-common/utils/decorators"; import { tracked } from "@glimmer/tracking";
import { action } from "@ember/object";
export default Controller.extend({ const MODALS = {
modalShowing: false, editChannel: "admin-plugins-chat-integration-edit-channel",
testChannel: "admin-plugins-chat-integration-test",
editRule: "admin-plugins-chat-integration-edit-rule",
channelError: "admin-plugins-chat-integration-channel-error",
};
@computed("model.channels") export default class AdminPluginsChatIntegrationEditRule extends Controller {
anyErrors(channels) { @tracked modalShowing = false;
get anyErrors() {
const channels = this.model.channels;
let anyErrors = false; let anyErrors = false;
channels.forEach((channel) => { channels.forEach((channel) => {
@ -16,90 +24,83 @@ export default Controller.extend({
}); });
return anyErrors; return anyErrors;
}, }
actions: { triggerModal(model, modal) {
this.modalShowing = true;
showModal(modal, {
model,
admin: true,
});
}
@action
createChannel() { createChannel() {
this.set("modalShowing", true); return this.triggerModal(
{
const model = {
channel: this.store.createRecord("channel", { channel: this.store.createRecord("channel", {
provider: this.get("model.provider.id"), provider: this.model.provider.id,
data: {}, data: {},
}), }),
provider: this.get("model.provider"), provider: this.model.provider,
};
showModal("admin-plugins-chat-integration-edit-channel", {
model,
admin: true,
});
}, },
MODALS.editChannel
);
}
@action
editChannel(channel) { editChannel(channel) {
this.set("modalShowing", true); return this.triggerModal(
{
const model = {
channel, channel,
provider: this.get("model.provider"), provider: this.model.provider,
};
showModal("admin-plugins-chat-integration-edit-channel", {
model,
admin: true,
});
}, },
MODALS.editChannel
);
}
@action
testChannel(channel) { testChannel(channel) {
this.set("modalShowing", true); return this.triggerModal({ channel }, MODALS.testChannel);
showModal("admin-plugins-chat-integration-test", { }
model: { channel },
admin: true,
});
},
@action
createRule(channel) { createRule(channel) {
this.set("modalShowing", true); return this.triggerModal(
{
const model = {
rule: this.store.createRecord("rule", { rule: this.store.createRecord("rule", {
channel_id: channel.id, channel_id: channel.id,
channel, channel,
}), }),
channel, channel,
provider: this.get("model.provider"), provider: this.model.provider,
groups: this.get("model.groups"), groups: this.model.groups,
};
showModal("admin-plugins-chat-integration-edit-rule", {
model,
admin: true,
});
}, },
MODALS.editRule
);
}
@action
editRuleWithChannel(rule, channel) { editRuleWithChannel(rule, channel) {
this.set("modalShowing", true); return this.triggerModal(
{
const model = {
rule, rule,
channel, channel,
provider: this.get("model.provider"), provider: this.model.provider,
groups: this.get("model.groups"), groups: this.model.groups,
};
showModal("admin-plugins-chat-integration-edit-rule", {
model,
admin: true,
});
}, },
MODALS.editRule
);
}
@action
showError(channel) { showError(channel) {
this.set("modalShowing", true); return this.triggerModal({ channel }, MODALS.channelError);
}
showModal("admin-plugins-chat-integration-channel-error", { @action
model: channel, refresh() {
admin: true, this.send("refreshProvider");
}); }
}, }
},
});

View File

@ -1,3 +1,3 @@
import Controller from "@ember/controller"; import Controller from "@ember/controller";
export default Controller.extend({}); export default class AdminPluginsChatIntegration extends Controller {}

View File

@ -1,131 +1,42 @@
import Controller from "@ember/controller"; import Controller from "@ember/controller";
import I18n from "I18n";
import ModalFunctionality from "discourse/mixins/modal-functionality"; import ModalFunctionality from "discourse/mixins/modal-functionality";
import { popupAjaxError } from "discourse/lib/ajax-error"; import { popupAjaxError } from "discourse/lib/ajax-error";
import EmberObject, { import { action } from "@ember/object";
defineProperty, import { tracked } from "@glimmer/tracking";
computed as emberComputed,
} from "@ember/object";
import computed, { observes, on } from "discourse-common/utils/decorators";
import { schedule } from "@ember/runloop";
export default Controller.extend(ModalFunctionality, { export default class AdminPluginsChatIntegrationEditChannel extends Controller.extend(
@on("init") ModalFunctionality
setupKeydown() { ) {
schedule("afterRender", () => { @tracked validParams = false;
$("#chat-integration-edit-channel-modal").keydown((e) => {
if (e.keyCode === 13) {
this.send("save");
}
});
});
},
// The validation property must be defined at runtime since the possible parameters vary by provider @action
@observes("model") isValidParams(validity) {
setupValidations() { return (this.validParams = validity);
if (this.get("model.provider")) {
const theKeys = this.get("model.provider.channel_parameters").map(
(param) => param["key"]
);
defineProperty(
this,
"paramValidation",
emberComputed(
`model.channel.data.{${theKeys.join(",")}}`,
this._paramValidation
)
);
this.notifyPropertyChange("paramValidation");
}
},
validate(parameter) {
const regString = parameter.regex;
const regex = new RegExp(regString);
let val = this.get(`model.channel.data.${parameter.key}`);
if (val === undefined) {
val = "";
} }
if (val === "") { @action
// Fail silently if field blank handleKeyUp(e) {
return EmberObject.create({ if (e.code === "Enter" && this.validParams) {
failed: true, this.save();
});
} else if (!regString) {
// Pass silently if no regex available for provider
return EmberObject.create({
ok: true,
});
} else if (regex.test(val)) {
// Test against regex
return EmberObject.create({
ok: true,
reason: I18n.t(
"chat_integration.edit_channel_modal.channel_validation.ok"
),
});
} else {
// Failed regex
return EmberObject.create({
failed: true,
reason: I18n.t(
"chat_integration.edit_channel_modal.channel_validation.fail"
),
});
} }
},
_paramValidation() {
const response = {};
const parameters = this.get("model.provider.channel_parameters");
parameters.forEach((parameter) => {
response[parameter.key] = this.validate(parameter);
});
return response;
},
@computed("paramValidation")
saveDisabled(paramValidation) {
if (!paramValidation) {
return true;
} }
let invalid = false; @action
Object.keys(paramValidation).forEach((key) => {
if (!paramValidation[key]) {
invalid = true;
}
if (!paramValidation[key]["ok"]) {
invalid = true;
}
});
return invalid;
},
actions: {
cancel() { cancel() {
this.send("closeModal"); this.send("closeModal");
}, }
@action
save() { save() {
if (this.get("saveDisabled")) { if (!this.validParams) {
return; return;
} }
this.get("model.channel") this.model.channel
.save() .save()
.then(() => { .then(() => {
this.send("closeModal"); this.send("closeModal");
}) })
.catch(popupAjaxError); .catch(popupAjaxError);
}, }
}, }
});

View File

@ -1,31 +1,27 @@
import Controller from "@ember/controller"; import Controller from "@ember/controller";
import ModalFunctionality from "discourse/mixins/modal-functionality"; import ModalFunctionality from "discourse/mixins/modal-functionality";
import { popupAjaxError } from "discourse/lib/ajax-error"; import { popupAjaxError } from "discourse/lib/ajax-error";
import computed, { on } from "discourse-common/utils/decorators"; import { tracked } from "@glimmer/tracking";
import { schedule } from "@ember/runloop"; import { action } from "@ember/object";
import { inject as service } from "@ember/service";
export default Controller.extend(ModalFunctionality, { export default class AdminPluginsChatIntegrationEditRule extends Controller.extend(
saveDisabled: false, ModalFunctionality
) {
@service siteSettings;
@tracked saveDisabled = false;
@on("init") get showCategory() {
setupKeydown() { return this.model.rule.type === "normal";
schedule("afterRender", () => {
$("#chat-integration-edit-channel-modal").keydown((e) => {
if (e.keyCode === 13) {
this.send("save");
} }
});
});
},
@computed("model.rule.type") get currentRuleType() {
showCategory(type) { return this.model.rule.type;
return type === "normal"; }
},
actions: { @action
save(rule) { save(rule) {
if (this.get("saveDisabled")) { if (this.saveDisabled) {
return; return;
} }
@ -33,6 +29,23 @@ export default Controller.extend(ModalFunctionality, {
.save() .save()
.then(() => this.send("closeModal")) .then(() => this.send("closeModal"))
.catch(popupAjaxError); .catch(popupAjaxError);
}, }
},
}); @action
handleKeyUp(e) {
if (e.code === "Enter") {
this.save();
}
}
@action
onChangeRuleType(type) {
this.model.rule.type = type;
this.currentRuleType = type;
if (type !== "normal") {
this.showCategory = false;
} else {
this.showCategory = true;
}
}
}

View File

@ -1,47 +1,43 @@
import Controller from "@ember/controller"; import Controller from "@ember/controller";
import { not } from "@ember/object/computed";
import I18n from "I18n"; import I18n from "I18n";
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 { popupAjaxError } from "discourse/lib/ajax-error"; import { popupAjaxError } from "discourse/lib/ajax-error";
import computed, { on } from "discourse-common/utils/decorators"; import { action } from "@ember/object";
import { schedule } from "@ember/runloop"; import { tracked } from "@glimmer/tracking";
export default Controller.extend(ModalFunctionality, { export default class AdminPluginsChatIntegrationTest extends Controller.extend(
@on("init") ModalFunctionality
setupKeydown() { ) {
schedule("afterRender", () => { @tracked loading = false;
$("#chat_integration_test_modal").keydown((e) => { @not("model.topic_id") sendDisabled;
if (e.keyCode === 13) {
this.send("send"); @action
handleKeyUp(e) {
if (e.code === "Enter" && !this.sendDisabled) {
this.send();
}
} }
});
});
},
@computed("model.topic_id") @action
sendDisabled(topicId) {
return !topicId;
},
actions: {
send() { send() {
if (this.get("sendDisabled")) { if (this.sendDisabled) {
return; return;
} }
this.set("loading", true); this.loading = true;
ajax("/admin/plugins/chat-integration/test", { ajax("/admin/plugins/chat-integration/test", {
data: { data: {
channel_id: this.get("model.channel.id"), channel_id: this.model.channel.id,
topic_id: this.get("model.topic_id"), topic_id: this.model.topic_id,
}, },
type: "POST", type: "POST",
}) })
.then(() => { .then(() => {
this.set("loading", false); this.loading = false;
this.flash(I18n.t("chat_integration.test_modal.success"), "success"); this.flash(I18n.t("chat_integration.test_modal.success"), "success");
}) })
.catch(popupAjaxError); .catch(popupAjaxError);
}, }
}, }
});

View File

@ -1,11 +1,11 @@
import RestModel from "discourse/models/rest"; import RestModel from "discourse/models/rest";
export default RestModel.extend({ export default class Channel extends RestModel {
updateProperties() { updateProperties() {
return this.getProperties(["data"]); return this.getProperties(["data"]);
}, }
createProperties() { createProperties() {
return this.getProperties(["provider", "data"]); return this.getProperties(["provider", "data"]);
}, }
}); }

View File

@ -1,3 +1,3 @@
import RestModel from "discourse/models/rest"; import RestModel from "discourse/models/rest";
export default RestModel.extend({}); export default class Provider extends RestModel {}

View File

@ -1,12 +1,31 @@
import I18n from "I18n"; import I18n from "I18n";
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, { observes } from "discourse-common/utils/decorators"; import { tracked } from "@glimmer/tracking";
export default RestModel.extend({ export default class Rule extends RestModel {
@computed("channel.provider") @tracked type = "normal";
available_filters(provider) { @tracked category_id = null;
@tracked tags = null;
@tracked channel_id = null;
@tracked filter = "watch";
@tracked error_key = null;
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"),
},
];
get available_filters() {
const available = []; const available = [];
const provider = this.channel.provider;
if (provider === "slack") { if (provider === "slack") {
available.push({ available.push({
@ -35,51 +54,21 @@ export default RestModel.extend({
); );
return available; return available;
},
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,
@observes("type")
removeUnneededInfo() {
const type = this.get("type");
if (type === "normal") {
this.set("group_id", null);
} else {
this.set("category_id", null);
} }
},
@computed("category_id") get category() {
category(categoryId) { const categoryId = this.category_id;
if (categoryId) { if (categoryId) {
return Category.findById(categoryId); return Category.findById(categoryId);
} else { } else {
return false; return false;
} }
}, }
@computed("filter") get filterName() {
filterName(filter) { return I18n.t(`chat_integration.filter.${this.filter}`);
return I18n.t(`chat_integration.filter.${filter}`); }
},
updateProperties() { updateProperties() {
return this.getProperties([ return this.getProperties([
@ -89,7 +78,7 @@ export default RestModel.extend({
"tags", "tags",
"filter", "filter",
]); ]);
}, }
createProperties() { createProperties() {
return this.getProperties([ return this.getProperties([
@ -100,5 +89,5 @@ export default RestModel.extend({
"tags", "tags",
"filter", "filter",
]); ]);
}, }
}); }

View File

@ -1,6 +1,6 @@
import DiscourseRoute from "discourse/routes/discourse"; import DiscourseRoute from "discourse/routes/discourse";
export default DiscourseRoute.extend({ export default class AdminPluginsChatIntegrationIndex extends DiscourseRoute {
afterModel(model) { afterModel(model) {
if (model.totalRows > 0) { if (model.totalRows > 0) {
this.transitionTo( this.transitionTo(
@ -8,5 +8,5 @@ export default DiscourseRoute.extend({
model.get("firstObject").name model.get("firstObject").name
); );
} }
}, }
}); }

View File

@ -3,7 +3,7 @@ import Group from "discourse/models/group";
import { action } from "@ember/object"; import { action } from "@ember/object";
import RSVP from "rsvp"; import RSVP from "rsvp";
export default DiscourseRoute.extend({ export default class AdminPluginsChatIntegrationProvider extends DiscourseRoute {
model(params) { model(params) {
return RSVP.hash({ return RSVP.hash({
channels: this.store.findAll("channel", { provider: params.provider }), channels: this.store.findAll("channel", { provider: params.provider }),
@ -26,24 +26,24 @@ export default DiscourseRoute.extend({
return value; return value;
}); });
}, }
serialize(model) { serialize(model) {
return { provider: model["provider"].get("id") }; return { provider: model["provider"].get("id") };
}, }
@action @action
closeModal() { closeModal() {
if (this.get("controller.modalShowing")) { if (this.controller.modalShowing) {
this.refresh(); this.refresh();
this.set("controller.modalShowing", false); this.controller.modalShowing = false;
} }
return true; // Continue bubbling up, so the modal actually closes return true; // Continue bubbling up, so the modal actually closes
}, }
@action @action
refreshProvider() { refreshProvider() {
this.refresh(); this.refresh();
}, }
}); }

View File

@ -1,15 +1,15 @@
import DiscourseRoute from "discourse/routes/discourse"; import DiscourseRoute from "discourse/routes/discourse";
import { action } from "@ember/object"; import { action } from "@ember/object";
export default DiscourseRoute.extend({ export default class AdminPluginsChatIntegration extends DiscourseRoute {
model() { model() {
return this.store.findAll("provider"); return this.store.findAll("provider");
}, }
@action @action
showSettings() { showSettings() {
this.transitionTo("adminSiteSettingsCategory", "plugins", { this.transitionTo("adminSiteSettingsCategory", "plugins", {
queryParams: { filter: "chat_integration" }, queryParams: { filter: "chat_integration" },
}); });
}, }
}); }

View File

@ -1,4 +1,4 @@
{{#d-modal-body id="chat_integration_error_modal"}} <DModalBody @id="chat_integration_error_modal">
<h4>{{i18n model.error_key}}</h4> <h4>{{i18n model.error_key}}</h4>
<pre>{{model.error_info}}</pre> <pre>{{model.error_info}}</pre>
{{/d-modal-body}} </DModalBody>

View File

@ -1,15 +1,18 @@
{{#d-modal-body <DModalBody
id="chat-integration-edit-channel-modal" @title="chat_integration.edit_channel_modal.title"
title="chat_integration.edit_channel_modal.title" @id="chat-integration-edit-channel-modal"
}} {{on "keyup" this.handleKeyUp}}
>
<div> <div>
<form {{action "save" on="submit"}}> <form {{action "save" on="submit"}}>
<table> <table>
<tbody> <tbody>
<tr class="input"> <tr class="input">
<td class="label"><label for="provider">{{i18n <td class="label">
"chat_integration.edit_channel_modal.provider" <label for="provider">
}}</label></td> {{i18n "chat_integration.edit_channel_modal.provider"}}
</label>
</td>
<td> <td>
{{i18n {{i18n
(concat (concat
@ -24,63 +27,34 @@
<td></td> <td></td>
</tr> </tr>
{{#each model.provider.channel_parameters as |param|}} {{#each this.model.provider.channel_parameters as |param|}}
<tr class="input"> <ChannelParamRow
<td class="label"><label for="param-{{param.key}}">{{i18n @param={{param}}
(concat @model={{this.model}}
"chat_integration.provider." @validParams={{this.validParams}}
model.channel.provider @isValidParams={{this.isValidParams}}
".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="chat-instructions">
<td></td>
<td><label>{{i18n
(concat
"chat_integration.provider."
model.channel.provider
".param."
param.key
".help"
)
}}</label></td>
</tr>
{{/each}} {{/each}}
</tbody> </tbody>
</table> </table>
</form> </form>
</div> </div>
{{/d-modal-body}} </DModalBody>
<div class="modal-footer"> <div class="modal-footer">
{{d-button <DButton
id="save-channel" @class="btn-primary btn-large"
class="btn-primary btn-large" @id="save-channel"
action=(action "save") @title="chat_integration.edit_channel_modal.save"
title="chat_integration.edit_channel_modal.save" @label="chat_integration.edit_channel_modal.save"
label="chat_integration.edit_channel_modal.save" @action={{action "save"}}
disabled=saveDisabled @disabled={{(not this.validParams)}}
}} />
{{d-button <DButton
class="btn-large" @class="btn-large"
action=(action "cancel") @title="chat_integration.edit_channel_modal.cancel"
title="chat_integration.edit_channel_modal.cancel" @label="chat_integration.edit_channel_modal.cancel"
label="chat_integration.edit_channel_modal.cancel" @action={{action "cancel"}}
}} />
</div> </div>

View File

@ -1,19 +1,24 @@
{{#d-modal-body <DModalBody
id="chat-integration-edit-rule_modal" @title="chat_integration.edit_rule_modal.title"
title="chat_integration.edit_rule_modal.title" @id="chat-integration-edit-rule_modal"
}} {{on "keyup" this.handleKeyUp}}
>
<div> <div>
<form {{action "save" on="submit"}}> <form {{action "save" on="submit"}}>
<table> <table>
<tbody> <tbody>
<tr class="input"> <tr class="input">
<td class="label"><label for="provider">{{i18n <td class="label">
"chat_integration.edit_rule_modal.provider" <label for="provider">
}}</label></td> {{i18n "chat_integration.edit_rule_modal.provider"}}
</label>
</td>
<td> <td>
{{i18n {{i18n
(concat (concat
"chat_integration.provider." model.channel.provider ".title" "chat_integration.provider."
this.model.channel.provider
".title"
) )
}} }}
</td> </td>
@ -25,11 +30,16 @@
</tr> </tr>
<tr class="input"> <tr class="input">
<td class="label"><label for="channel">{{i18n <td class="label">
"chat_integration.edit_rule_modal.channel" <label for="channel">
}}</label></td> {{i18n "chat_integration.edit_rule_modal.channel"}}
</label>
</td>
<td> <td>
{{channel-data provider=model.provider channel=model.channel}} <ChannelData
@provider={{this.model.provider}}
@channel={{this.model.channel}}
/>
</td> </td>
</tr> </tr>
@ -39,131 +49,158 @@
</tr> </tr>
<tr class="input"> <tr class="input">
<td class="label"><label for="filter">{{i18n <td class="label">
"chat_integration.edit_rule_modal.type" <label for="filter">
}}</label></td> {{i18n "chat_integration.edit_rule_modal.type"}}
</label>
</td>
<td> <td>
{{combo-box <ComboBox
name="type" @name="type"
content=model.rule.available_types @content={{this.model.rule.available_types}}
value=model.rule.type @value={{this.model.rule.type}}
}} @onChange={{action (mut this.model.rule.type)}}
/>
</td> </td>
</tr> </tr>
<tr class="chat-instructions"> <tr class="chat-instructions">
<td></td> <td></td>
<td><label>{{i18n <td>
"chat_integration.edit_rule_modal.instructions.type" <label>
}}</label></td> {{i18n "chat_integration.edit_rule_modal.instructions.type"}}
</label>
</td>
</tr> </tr>
<tr class="input"> <tr class="input">
<td class="label"><label for="filter">{{i18n <td class="label">
"chat_integration.edit_rule_modal.filter" <label for="filter">
}}</label></td> {{i18n "chat_integration.edit_rule_modal.filter"}}
</label>
</td>
<td> <td>
{{combo-box <ComboBox
name="filter" @name="filter"
content=model.rule.available_filters @content={{this.model.rule.available_filters}}
value=model.rule.filter @value={{this.model.rule.filter}}
}} @onChange={{action (mut this.model.rule.filter)}}
/>
</td> </td>
</tr> </tr>
<tr class="chat-instructions"> <tr class="chat-instructions">
<td></td> <td></td>
<td><label>{{i18n <td>
"chat_integration.edit_rule_modal.instructions.filter" <label>
}}</label></td> {{i18n "chat_integration.edit_rule_modal.instructions.filter"}}
</label>
</td>
</tr> </tr>
{{#if showCategory}} {{#if (eq this.model.rule.type "normal")}}
<tr class="input"> <tr class="input">
<td class="label"><label for="category">{{i18n <td class="label">
"chat_integration.edit_rule_modal.category" <label for="category">
}}</label></td> {{i18n "chat_integration.edit_rule_modal.category"}}
</label>
</td>
<td> <td>
{{category-chooser <CategoryChooser
name="category" @name="category"
none="chat_integration.all_categories" @options={{hash none="chat_integration.all_categories"}}
value=model.rule.category_id @value={{this.model.rule.category_id}}
}} @onChange={{action (mut this.model.rule.category_id)}}
/>
</td> </td>
</tr> </tr>
<tr class="chat-instructions"> <tr class="chat-instructions">
<td></td> <td></td>
<td><label>{{i18n <td>
<label>
{{i18n
"chat_integration.edit_rule_modal.instructions.category" "chat_integration.edit_rule_modal.instructions.category"
}}</label></td> }}
</label>
</td>
</tr> </tr>
{{else}} {{else}}
<tr class="input"> <tr class="input">
<td class="label"><label for="group">{{i18n <td class="label">
"chat_integration.edit_rule_modal.group" <label for="group">
}}</label></td> {{i18n "chat_integration.edit_rule_modal.group"}}
</label>
</td>
<td> <td>
{{combo-box <ComboBox
content=model.groups @content={{this.model.groups}}
valueAttribute="id" @valueProperty="id"
value=model.rule.group_id @value={{this.model.rule.group_id}}
none="chat_integration.choose_group" @onChange={{action (mut this.model.rule.group_id)}}
}} @options={{hash none="chat_integration.choose_group"}}
/>
</td> </td>
</tr> </tr>
<tr class="chat-instructions"> <tr class="chat-instructions">
<td></td> <td></td>
<td><label>{{i18n <td>
"chat_integration.edit_rule_modal.instructions.group" <label>
}}</label></td> {{i18n "chat_integration.edit_rule_modal.instructions.group"}}
</label>
</td>
</tr> </tr>
{{/if}} {{/if}}
{{#if siteSettings.tagging_enabled}} {{#if this.siteSettings.tagging_enabled}}
<tr class="input"> <tr class="input">
<td class="label"><label for="tags">{{i18n <td class="label">
"chat_integration.edit_rule_modal.tags" <label for="tags">
}}</label></td> {{i18n "chat_integration.edit_rule_modal.tags"}}
</label>
</td>
<td> <td>
{{tag-chooser <TagChooser
placeholderKey="chat_integration.all_tags" @placeholderKey="chat_integration.all_tags"
name="tags" @name="tags"
tags=model.rule.tags @tags={{this.model.rule.tags}}
everyTag=true @everyTag="true"
}} @onChange={{action (mut this.model.rule.tags)}}
/>
</td> </td>
</tr> </tr>
<tr class="chat-instructions"> <tr class="chat-instructions">
<td></td> <td></td>
<td><label>{{i18n <td>
"chat_integration.edit_rule_modal.instructions.tags" <label>
}}</label></td> {{i18n "chat_integration.edit_rule_modal.instructions.tags"}}
</label>
</td>
</tr> </tr>
{{/if}} {{/if}}
</tbody> </tbody>
</table> </table>
</form> </form>
</div> </div>
{{/d-modal-body}} </DModalBody>
<div class="modal-footer"> <div class="modal-footer">
{{d-button <DButton
id="save-rule" @id="save-rule"
class="btn-primary btn-large" @class="btn-primary btn-large"
action=(action "save") @title="chat_integration.edit_rule_modal.save"
actionParam=model.rule @label="chat_integration.edit_rule_modal.save"
title="chat_integration.edit_rule_modal.save" @action={{action "save"}}
label="chat_integration.edit_rule_modal.save" @actionParam={{this.model.rule}}
disabled=saveDisabled @disabled={{this.saveDisabled}}
}} />
{{d-button <DButton
class="btn-large" @class="btn-large"
action=(route-action "closeModal") @title="chat_integration.edit_rule_modal.cancel"
title="chat_integration.edit_rule_modal.cancel" @label="chat_integration.edit_rule_modal.cancel"
label="chat_integration.edit_rule_modal.cancel" @action={{route-action "closeModal"}}
}} />
</div> </div>

View File

@ -1,41 +1,43 @@
{{#d-modal-body <DModalBody
id="chat_integration_test_modal" @title="chat_integration.test_modal.title"
title="chat_integration.test_modal.title" @id="chat_integration_test_modal"
}} {{on "keyup" this.handleKeyUp}}
>
<div> <div>
<form {{action "send" on="submit"}}> <form {{action "send" on="submit"}}>
<table> <table>
<tbody> <tbody>
<tr class="input"> <tr class="input">
<td class="label"><label for="channel">{{i18n <td class="label">
"chat_integration.test_modal.topic" <label for="channel">
}}</label></td> {{i18n "chat_integration.test_modal.topic"}}
</label>
</td>
<td> <td>
{{choose-topic selectedTopicId=model.topic_id}} <ChooseTopic @selectedTopicId={{this.model.topic_id}} />
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</form> </form>
</div> </div>
{{/d-modal-body}} </DModalBody>
<div class="modal-footer"> <div class="modal-footer">
{{#conditional-loading-spinner condition=loading}} <ConditionalLoadingSpinner @condition={{this.loading}}>
{{d-button <DButton
id="send-test" @class="btn-primary btn-large"
class="btn-primary btn-large" @id="send-test"
action=(action "send") @title="chat_integration.test_modal.send"
title="chat_integration.test_modal.send" @action={{action "send"}}
label="chat_integration.test_modal.send" @label="chat_integration.test_modal.send"
disabled=sendDisabled @disabled={{this.sendDisabled}}
}} />
<DButton
{{d-button @class="btn-large"
class="btn-large" @title="chat_integration.test_modal.close"
action=(route-action "closeModal") @action={{route-action "closeModal"}}
title="chat_integration.test_modal.close" @label="chat_integration.test_modal.close"
label="chat_integration.test_modal.close" />
}} </ConditionalLoadingSpinner>
{{/conditional-loading-spinner}}
</div> </div>

View File

@ -0,0 +1,33 @@
{{#if this.anyErrors}}
<div class="error">
{{d-icon "exclamation-triangle"}}
<span class="error-message">
{{i18n "chat_integration.channels_with_errors"}}
</span>
</div>
{{/if}}
{{#each this.model.channels.content as |channel|}}
<ChannelDetails
@channel={{channel}}
@provider={{this.model.provider}}
@refresh={{action "refresh"}}
@editChannel={{action "editChannel"}}
@test={{action "testChannel"}}
@createRule={{action "createRule"}}
@editRuleWithChannel={{action "editRuleWithChannel"}}
@showError={{action "showError"}}
/>
{{/each}}
<div class="table-footer">
<div class="pull-right">
<DButton
@id="create-channel"
@icon="plus"
@title="chat_integration.create_channel"
@label="chat_integration.create_channel"
@action={{action "createChannel" this.model.provider}}
/>
</div>
</div>

View File

@ -0,0 +1,30 @@
<div id="admin-plugin-chat">
<div class="admin-controls">
<div class="admin-controls-chat-providers">
<ul class="nav nav-pills">
{{#each this.model as |provider|}}
<NavItem
@route="adminPlugins.chat-integration.provider"
@routeParam={{provider.name}}
@label={{(concat
"chat_integration.provider." provider.name ".title"
)}}
/>
{{/each}}
</ul>
</div>
<DButton
@icon="cog"
@title="chat_integration.settings"
@label="chat_integration.settings"
@action={{route-action "showSettings"}}
/>
</div>
{{#unless this.model.totalRows}}
{{i18n "chat_integration.no_providers"}}
{{/unless}}
{{outlet}}
</div>

View File

@ -3,7 +3,7 @@ import { popupAjaxError } from "discourse/lib/ajax-error";
import DiscourseRoute from "discourse/routes/discourse"; import DiscourseRoute from "discourse/routes/discourse";
import { next } from "@ember/runloop"; import { next } from "@ember/runloop";
export default DiscourseRoute.extend({ export default class Trascript extends DiscourseRoute {
model(params) { model(params) {
if (this.currentUser) { if (this.currentUser) {
const secret = params.secret; const secret = params.secret;
@ -28,5 +28,5 @@ export default DiscourseRoute.extend({
this.session.set("shouldRedirectToUrl", window.location.href); this.session.set("shouldRedirectToUrl", window.location.href);
this.replaceWith("login"); this.replaceWith("login");
} }
}, }
}); }

View File

@ -1,34 +0,0 @@
{{#if anyErrors}}
<div class="error">
{{d-icon "exclamation-triangle"}}
<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
refresh=(route-action "refreshProvider")
editChannel=(action "editChannel")
test=(action "testChannel")
createRule=(action "createRule")
editRuleWithChannel=(action "editRuleWithChannel")
showError=(action "showError")
}}
{{/each}}
<div class="table-footer">
<div class="pull-right">
{{d-button
id="create-channel"
action=(action "createChannel")
actionParam=model.provider
icon="plus"
title="chat_integration.create_channel"
label="chat_integration.create_channel"
}}
</div>
</div>

View File

@ -1,28 +0,0 @@
<div id="admin-plugin-chat">
<div class="admin-controls">
<div class="admin-controls-chat-providers">
<ul class="nav nav-pills">
{{#each model as |provider|}}
{{nav-item
route="adminPlugins.chat-integration.provider"
routeParam=provider.name
label=(concat "chat_integration.provider." provider.name ".title")
}}
{{/each}}
</ul>
</div>
{{d-button
action=(route-action "showSettings")
icon="cog"
title="chat_integration.settings"
label="chat_integration.settings"
}}
</div>
{{#unless model.totalRows}}
{{i18n "chat_integration.no_providers"}}
{{/unless}}
{{outlet}}
</div>

View File

@ -1,17 +0,0 @@
{{#each provider.channel_parameters as |param|}}
{{#unless param.hidden}}
<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>
<br />
{{/unless}}
{{/each}}

View File

@ -1,77 +0,0 @@
<div class="channel-header">
<div class="pull-right">
{{d-button
action=editChannel
actionParam=channel
icon="pencil-alt"
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"
class="btn-chat-test"
}}
{{d-button
class="cancel delete-channel"
action=(action "deleteChannel")
actionParam=channel
icon="trash-alt"
title="chat_integration.delete_channel"
label="chat_integration.delete_channel"
}}
</div>
<span class="channel-title">
{{#if channel.error_key}}
{{d-button
action=showError
actionParam=channel
class="delete btn-danger"
icon="exclamation-triangle"
}}
{{/if}}
{{channel-data provider=provider channel=channel}}
</span>
</div>
<div class="channel-body">
<table>
<thead>
<tr>
<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>
</thead>
<tbody>
{{#each channel.rules as |rule|}}
{{rule-row rule=rule edit=(action "editRule") refresh=refresh}}
{{/each}}
</tbody>
</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

@ -1,45 +0,0 @@
<td>
{{rule.filterName}}
</td>
<td>
{{#if isCategory}}
{{#if rule.category}}
{{category-link rule.category allowUncategorized="true" link="false"}}
{{else}}
{{i18n "chat_integration.all_categories"}}
{{/if}}
{{else if isMention}}
{{i18n "chat_integration.group_mention_template" name=rule.group_name}}
{{else if isMessage}}
{{i18n "chat_integration.group_message_template" name=rule.group_name}}
{{/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-alt"
class="edit"
title="chat_integration.rule_table.edit_rule"
}}
{{d-button
action=(action "delete")
actionParam=rule
icon="far-trash-alt"
class="delete"
title="chat_integration.rule_table.delete_rule"
}}
</td>

View File

@ -66,6 +66,10 @@
table { table {
width: 100%; width: 100%;
tbody {
border-top: none;
}
tr { tr {
border: none; border: none;
} }
@ -85,6 +89,8 @@
tr.chat-instructions label { tr.chat-instructions label {
color: var(--primary-medium); color: var(--primary-medium);
font-size: var(--font-down-1);
margin-top: 0.5rem;
} }
} }

View File

@ -143,9 +143,9 @@ acceptance("Chat Integration", function (needs) {
// Press enter // Press enter
await triggerKeyEvent( await triggerKeyEvent(
"#chat-integration-edit-channel-modal input", "#chat-integration-edit-channel-modal",
"keydown", "keydown",
13 "Enter"
); );
assert.notOk( assert.notOk(