DEV: Admin webhooks interface issues (#19360)
1. The events table had broken styling, making each row overflow 2. It had confusing routes: `/:id` for "edit" and `/:id/events` for "show" (now it's `/:id/edit` and `/:id` respectively) 3. There previously was an unused backend action (`#edit`) - now it is used (and `web_hooks/:id/events` route has been removed) 4. There was outdated/misplaced/duplicated CSS 5. And more
This commit is contained in:
parent
4001e6f174
commit
f9bdda84ca
|
@ -1,16 +1,17 @@
|
|||
<div class="web-hook-direction">
|
||||
<LinkTo @route="adminWebHooks" class="btn">
|
||||
{{d-icon "list"}} {{i18n "admin.web_hooks.events.go_list"}}
|
||||
</LinkTo>
|
||||
<DButton @icon="paper-plane" @label="admin.web_hooks.events.ping" @action={{action "ping"}} @disabled={{this.pingDisabled}} />
|
||||
<LinkTo @route="adminWebHooks.show" @model={{this.model.extras.web_hook_id}} class="btn">
|
||||
{{d-icon "far-edit"}} {{i18n "admin.web_hooks.events.go_details"}}
|
||||
</LinkTo>
|
||||
</div>
|
||||
<div class="web-hook-events-listing"
|
||||
{{did-insert this.subscribe}}
|
||||
{{will-destroy this.unsubscribe}}
|
||||
>
|
||||
<DButton
|
||||
@icon="paper-plane"
|
||||
@label="admin.web_hooks.events.ping"
|
||||
@action={{this.ping}}
|
||||
@disabled={{not this.pingEnabled}}
|
||||
class="webhook-events__ping-button"
|
||||
/>
|
||||
|
||||
<div class="web-hook-events-listing">
|
||||
{{#if this.model}}
|
||||
<LoadMore @selector=".web-hook-events li" @action={{action "loadMore"}}>
|
||||
{{#if this.events}}
|
||||
<LoadMore @selector=".web-hook-events li" @action={{this.loadMore}}>
|
||||
<div class="web-hook-events content-list">
|
||||
<div class="heading-container">
|
||||
<div class="col heading first status">{{i18n "admin.web_hooks.events.status"}}</div>
|
||||
|
@ -18,20 +19,22 @@
|
|||
<div class="col heading timestamp">{{i18n "admin.web_hooks.events.timestamp"}}</div>
|
||||
<div class="col heading completion">{{i18n "admin.web_hooks.events.completion"}}</div>
|
||||
<div class="col heading actions">{{i18n "admin.web_hooks.events.actions"}}</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
|
||||
{{#if this.hasIncoming}}
|
||||
<a href tabindex="0" {{on "click" this.showInserted}} class="alert alert-info clickable">
|
||||
<CountI18n @key="admin.web_hooks.events.incoming" @count={{this.incomingCount}} />
|
||||
</a>
|
||||
{{/if}}
|
||||
|
||||
<ul>
|
||||
{{#each this.model as |webHookEvent|}}
|
||||
{{#each this.events as |webHookEvent|}}
|
||||
<AdminWebHookEvent @model={{webHookEvent}} />
|
||||
{{/each}}
|
||||
</ul>
|
||||
</div>
|
||||
<ConditionalLoadingSpinner @condition={{this.model.loadingMore}} />
|
||||
|
||||
<ConditionalLoadingSpinner @condition={{this.events.loadingMore}} />
|
||||
</LoadMore>
|
||||
{{else}}
|
||||
<p>{{i18n "admin.web_hooks.events.none"}}</p>
|
|
@ -0,0 +1,89 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { inject as service } from "@ember/service";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import { action } from "@ember/object";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import { gt, readOnly } from "@ember/object/computed";
|
||||
import { bind } from "discourse-common/utils/decorators";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
|
||||
export default class WebhookEvents extends Component {
|
||||
@service messageBus;
|
||||
@service store;
|
||||
|
||||
@tracked pingEnabled = true;
|
||||
@tracked events = [];
|
||||
@tracked incomingEventIds = [];
|
||||
|
||||
@readOnly("incomingEventIds.length") incomingCount;
|
||||
@gt("incomingCount", 0) hasIncoming;
|
||||
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.loadEvents();
|
||||
}
|
||||
|
||||
async loadEvents() {
|
||||
this.events = await this.store.findAll(
|
||||
"web-hook-event",
|
||||
this.args.webhookId
|
||||
);
|
||||
}
|
||||
|
||||
@bind
|
||||
subscribe() {
|
||||
const channel = `/web_hook_events/${this.args.webhookId}`;
|
||||
this.messageBus.subscribe(channel, this._addIncoming);
|
||||
}
|
||||
|
||||
@bind
|
||||
unsubscribe() {
|
||||
this.messageBus.unsubscribe("/web_hook_events/*", this._addIncoming);
|
||||
}
|
||||
|
||||
@bind
|
||||
_addIncoming(data) {
|
||||
if (data.event_type === "ping") {
|
||||
this.pingEnabled = true;
|
||||
}
|
||||
|
||||
if (!this.incomingEventIds.includes(data.web_hook_event_id)) {
|
||||
this.incomingEventIds.pushObject(data.web_hook_event_id);
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
async showInserted(event) {
|
||||
event?.preventDefault();
|
||||
|
||||
const path = `/admin/api/web_hooks/${this.args.webhookId}/events/bulk`;
|
||||
const data = await ajax(path, {
|
||||
data: { ids: this.incomingEventIds },
|
||||
});
|
||||
|
||||
const objects = data.map((webHookEvent) =>
|
||||
this.store.createRecord("web-hook-event", webHookEvent)
|
||||
);
|
||||
this.events.unshiftObjects(objects);
|
||||
this.incomingEventIds = [];
|
||||
}
|
||||
|
||||
@action
|
||||
loadMore() {
|
||||
this.events.loadMore();
|
||||
}
|
||||
|
||||
@action
|
||||
async ping() {
|
||||
this.pingEnabled = false;
|
||||
|
||||
try {
|
||||
await ajax(`/admin/api/web_hooks/${this.args.webhookId}/ping`, {
|
||||
type: "POST",
|
||||
});
|
||||
} catch (error) {
|
||||
this.pingEnabled = true;
|
||||
popupAjaxError(error);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
import Controller, { inject as controller } from "@ember/controller";
|
||||
import EmberObject, { action } from "@ember/object";
|
||||
import I18n from "I18n";
|
||||
import { alias } from "@ember/object/computed";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { isEmpty } from "@ember/utils";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import { inject as service } from "@ember/service";
|
||||
|
||||
export default Controller.extend({
|
||||
adminWebHooks: controller(),
|
||||
dialog: service(),
|
||||
eventTypes: alias("adminWebHooks.eventTypes"),
|
||||
defaultEventTypes: alias("adminWebHooks.defaultEventTypes"),
|
||||
contentTypes: alias("adminWebHooks.contentTypes"),
|
||||
|
||||
@discourseComputed
|
||||
showTagsFilter() {
|
||||
return this.siteSettings.tagging_enabled;
|
||||
},
|
||||
|
||||
@discourseComputed("model.isSaving", "saved", "saveButtonDisabled")
|
||||
savingStatus(isSaving, saved, saveButtonDisabled) {
|
||||
if (isSaving) {
|
||||
return I18n.t("saving");
|
||||
} else if (!saveButtonDisabled && saved) {
|
||||
return I18n.t("saved");
|
||||
}
|
||||
// Use side effect of validation to clear saved text
|
||||
this.set("saved", false);
|
||||
return "";
|
||||
},
|
||||
|
||||
@discourseComputed("model.isNew")
|
||||
saveButtonText(isNew) {
|
||||
return isNew
|
||||
? I18n.t("admin.web_hooks.create")
|
||||
: I18n.t("admin.web_hooks.save");
|
||||
},
|
||||
|
||||
@discourseComputed("model.secret")
|
||||
secretValidation(secret) {
|
||||
if (!isEmpty(secret)) {
|
||||
if (secret.includes(" ")) {
|
||||
return EmberObject.create({
|
||||
failed: true,
|
||||
reason: I18n.t("admin.web_hooks.secret_invalid"),
|
||||
});
|
||||
}
|
||||
|
||||
if (secret.length < 12) {
|
||||
return EmberObject.create({
|
||||
failed: true,
|
||||
reason: I18n.t("admin.web_hooks.secret_too_short"),
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@discourseComputed("model.wildcard_web_hook", "model.web_hook_event_types.[]")
|
||||
eventTypeValidation(isWildcard, eventTypes) {
|
||||
if (!isWildcard && isEmpty(eventTypes)) {
|
||||
return EmberObject.create({
|
||||
failed: true,
|
||||
reason: I18n.t("admin.web_hooks.event_type_missing"),
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
@discourseComputed(
|
||||
"model.isSaving",
|
||||
"secretValidation",
|
||||
"eventTypeValidation",
|
||||
"model.payload_url"
|
||||
)
|
||||
saveButtonDisabled(
|
||||
isSaving,
|
||||
secretValidation,
|
||||
eventTypeValidation,
|
||||
payloadUrl
|
||||
) {
|
||||
return isSaving
|
||||
? false
|
||||
: secretValidation || eventTypeValidation || isEmpty(payloadUrl);
|
||||
},
|
||||
|
||||
@action
|
||||
async save() {
|
||||
this.set("saved", false);
|
||||
|
||||
try {
|
||||
await this.model.save();
|
||||
|
||||
this.set("saved", true);
|
||||
this.adminWebHooks.model.addObject(this.model);
|
||||
this.transitionToRoute("adminWebHooks.show", this.model);
|
||||
} catch (e) {
|
||||
popupAjaxError(e);
|
||||
}
|
||||
},
|
||||
});
|
|
@ -0,0 +1,36 @@
|
|||
import Controller, { inject as controller } from "@ember/controller";
|
||||
import I18n from "I18n";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import { inject as service } from "@ember/service";
|
||||
import { action } from "@ember/object";
|
||||
import { alias } from "@ember/object/computed";
|
||||
|
||||
export default Controller.extend({
|
||||
adminWebHooks: controller(),
|
||||
dialog: service(),
|
||||
contentTypes: alias("adminWebHooks.contentTypes"),
|
||||
defaultEventTypes: alias("adminWebHooks.defaultEventTypes"),
|
||||
deliveryStatuses: alias("adminWebHooks.deliveryStatuses"),
|
||||
eventTypes: alias("adminWebHooks.eventTypes"),
|
||||
model: alias("adminWebHooks.model"),
|
||||
|
||||
@action
|
||||
destroy(webhook) {
|
||||
return this.dialog.deleteConfirm({
|
||||
message: I18n.t("admin.web_hooks.delete_confirm"),
|
||||
didConfirm: async () => {
|
||||
try {
|
||||
await webhook.destroyRecord();
|
||||
this.model.removeObject(webhook);
|
||||
} catch (e) {
|
||||
popupAjaxError(e);
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
@action
|
||||
loadMore() {
|
||||
this.model.loadMore();
|
||||
},
|
||||
});
|
|
@ -1,81 +0,0 @@
|
|||
import Controller from "@ember/controller";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import { action } from "@ember/object";
|
||||
import { alias } from "@ember/object/computed";
|
||||
import discourseComputed, { bind } from "discourse-common/utils/decorators";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
|
||||
export default Controller.extend({
|
||||
pingDisabled: false,
|
||||
incomingCount: alias("incomingEventIds.length"),
|
||||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
|
||||
this.incomingEventIds = [];
|
||||
},
|
||||
|
||||
@discourseComputed("incomingCount")
|
||||
hasIncoming(incomingCount) {
|
||||
return incomingCount > 0;
|
||||
},
|
||||
|
||||
subscribe() {
|
||||
this.messageBus.subscribe(
|
||||
`/web_hook_events/${this.get("model.extras.web_hook_id")}`,
|
||||
this._addIncoming
|
||||
);
|
||||
},
|
||||
|
||||
unsubscribe() {
|
||||
this.messageBus.unsubscribe("/web_hook_events/*", this._addIncoming);
|
||||
},
|
||||
|
||||
@bind
|
||||
_addIncoming(data) {
|
||||
if (data.event_type === "ping") {
|
||||
this.set("pingDisabled", false);
|
||||
}
|
||||
|
||||
if (!this.incomingEventIds.includes(data.web_hook_event_id)) {
|
||||
this.incomingEventIds.pushObject(data.web_hook_event_id);
|
||||
}
|
||||
},
|
||||
|
||||
@action
|
||||
showInserted(event) {
|
||||
event?.preventDefault();
|
||||
const webHookId = this.get("model.extras.web_hook_id");
|
||||
|
||||
ajax(`/admin/api/web_hooks/${webHookId}/events/bulk`, {
|
||||
type: "GET",
|
||||
data: { ids: this.incomingEventIds },
|
||||
}).then((data) => {
|
||||
const objects = data.map((webHookEvent) =>
|
||||
this.store.createRecord("web-hook-event", webHookEvent)
|
||||
);
|
||||
this.model.unshiftObjects(objects);
|
||||
this.set("incomingEventIds", []);
|
||||
});
|
||||
},
|
||||
|
||||
actions: {
|
||||
loadMore() {
|
||||
this.model.loadMore();
|
||||
},
|
||||
|
||||
ping() {
|
||||
this.set("pingDisabled", true);
|
||||
|
||||
ajax(
|
||||
`/admin/api/web_hooks/${this.get("model.extras.web_hook_id")}/ping`,
|
||||
{
|
||||
type: "POST",
|
||||
}
|
||||
).catch((error) => {
|
||||
this.set("pingDisabled", false);
|
||||
popupAjaxError(error);
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
|
@ -1,121 +1,32 @@
|
|||
import Controller, { inject as controller } from "@ember/controller";
|
||||
import EmberObject from "@ember/object";
|
||||
import { action } from "@ember/object";
|
||||
import I18n from "I18n";
|
||||
import { alias } from "@ember/object/computed";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
import { isEmpty } from "@ember/utils";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import { inject as service } from "@ember/service";
|
||||
|
||||
export default Controller.extend({
|
||||
adminWebHooks: controller(),
|
||||
dialog: service(),
|
||||
eventTypes: alias("adminWebHooks.eventTypes"),
|
||||
defaultEventTypes: alias("adminWebHooks.defaultEventTypes"),
|
||||
contentTypes: alias("adminWebHooks.contentTypes"),
|
||||
router: service(),
|
||||
|
||||
@discourseComputed
|
||||
showTagsFilter() {
|
||||
return this.siteSettings.tagging_enabled;
|
||||
@action
|
||||
edit() {
|
||||
return this.router.transitionTo("adminWebHooks.edit", this.model);
|
||||
},
|
||||
|
||||
@discourseComputed("model.isSaving", "saved", "saveButtonDisabled")
|
||||
savingStatus(isSaving, saved, saveButtonDisabled) {
|
||||
if (isSaving) {
|
||||
return I18n.t("saving");
|
||||
} else if (!saveButtonDisabled && saved) {
|
||||
return I18n.t("saved");
|
||||
}
|
||||
// Use side effect of validation to clear saved text
|
||||
this.set("saved", false);
|
||||
return "";
|
||||
},
|
||||
|
||||
@discourseComputed("model.isNew")
|
||||
saveButtonText(isNew) {
|
||||
return isNew
|
||||
? I18n.t("admin.web_hooks.create")
|
||||
: I18n.t("admin.web_hooks.save");
|
||||
},
|
||||
|
||||
@discourseComputed("model.secret")
|
||||
secretValidation(secret) {
|
||||
if (!isEmpty(secret)) {
|
||||
if (secret.includes(" ")) {
|
||||
return EmberObject.create({
|
||||
failed: true,
|
||||
reason: I18n.t("admin.web_hooks.secret_invalid"),
|
||||
});
|
||||
}
|
||||
|
||||
if (secret.length < 12) {
|
||||
return EmberObject.create({
|
||||
failed: true,
|
||||
reason: I18n.t("admin.web_hooks.secret_too_short"),
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@discourseComputed("model.wildcard_web_hook", "model.web_hook_event_types.[]")
|
||||
eventTypeValidation(isWildcard, eventTypes) {
|
||||
if (!isWildcard && isEmpty(eventTypes)) {
|
||||
return EmberObject.create({
|
||||
failed: true,
|
||||
reason: I18n.t("admin.web_hooks.event_type_missing"),
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
@discourseComputed(
|
||||
"model.isSaving",
|
||||
"secretValidation",
|
||||
"eventTypeValidation",
|
||||
"model.payload_url"
|
||||
)
|
||||
saveButtonDisabled(
|
||||
isSaving,
|
||||
secretValidation,
|
||||
eventTypeValidation,
|
||||
payloadUrl
|
||||
) {
|
||||
return isSaving
|
||||
? false
|
||||
: secretValidation || eventTypeValidation || isEmpty(payloadUrl);
|
||||
},
|
||||
|
||||
actions: {
|
||||
save() {
|
||||
this.set("saved", false);
|
||||
const model = this.model;
|
||||
const isNew = model.get("isNew");
|
||||
|
||||
return model
|
||||
.save()
|
||||
.then(() => {
|
||||
this.set("saved", true);
|
||||
this.adminWebHooks.get("model").addObject(model);
|
||||
|
||||
if (isNew) {
|
||||
this.transitionToRoute("adminWebHooks.show", model.get("id"));
|
||||
}
|
||||
})
|
||||
.catch(popupAjaxError);
|
||||
},
|
||||
|
||||
destroy() {
|
||||
return this.dialog.yesNoConfirm({
|
||||
message: I18n.t("admin.web_hooks.delete_confirm"),
|
||||
didConfirm: () => {
|
||||
this.model
|
||||
.destroyRecord()
|
||||
.then(() => {
|
||||
this.adminWebHooks.get("model").removeObject(this.model);
|
||||
this.transitionToRoute("adminWebHooks");
|
||||
})
|
||||
.catch(popupAjaxError);
|
||||
},
|
||||
});
|
||||
},
|
||||
@action
|
||||
destroy() {
|
||||
return this.dialog.deleteConfirm({
|
||||
message: I18n.t("admin.web_hooks.delete_confirm"),
|
||||
didConfirm: async () => {
|
||||
try {
|
||||
await this.model.destroyRecord();
|
||||
this.adminWebHooks.model.removeObject(this.model);
|
||||
this.transitionToRoute("adminWebHooks");
|
||||
} catch (e) {
|
||||
popupAjaxError(e);
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,29 +1,3 @@
|
|||
import Controller from "@ember/controller";
|
||||
import I18n from "I18n";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import { inject as service } from "@ember/service";
|
||||
import { action } from "@ember/object";
|
||||
|
||||
export default Controller.extend({
|
||||
dialog: service(),
|
||||
|
||||
@action
|
||||
destroy(webhook) {
|
||||
return this.dialog.yesNoConfirm({
|
||||
message: I18n.t("admin.web_hooks.delete_confirm"),
|
||||
didConfirm: () => {
|
||||
webhook
|
||||
.destroyRecord()
|
||||
.then(() => {
|
||||
this.model.removeObject(webhook);
|
||||
})
|
||||
.catch(popupAjaxError);
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
@action
|
||||
loadMore() {
|
||||
this.model.loadMore();
|
||||
},
|
||||
});
|
||||
export default Controller.extend({});
|
||||
|
|
|
@ -123,7 +123,7 @@ export default function () {
|
|||
{ path: "/web_hooks", resetNamespace: true },
|
||||
function () {
|
||||
this.route("show", { path: "/:web_hook_id" });
|
||||
this.route("showEvents", { path: "/:web_hook_id/events" });
|
||||
this.route("edit", { path: "/:web_hook_id/edit" });
|
||||
}
|
||||
);
|
||||
});
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
|
||||
export default DiscourseRoute.extend({
|
||||
serialize(model) {
|
||||
return { web_hook_id: model.id || "new" };
|
||||
},
|
||||
|
||||
model(params) {
|
||||
if (params.web_hook_id === "new") {
|
||||
return this.store.createRecord("web-hook");
|
||||
}
|
||||
|
||||
return this.store.find("web-hook", params.web_hook_id);
|
||||
},
|
||||
|
||||
setupController(controller, model) {
|
||||
this._super(...arguments);
|
||||
|
||||
if (model.get("isNew")) {
|
||||
model.set(
|
||||
"web_hook_event_types",
|
||||
this.controllerFor("adminWebHooks").defaultEventTypes
|
||||
);
|
||||
}
|
||||
|
||||
controller.set("saved", false);
|
||||
},
|
||||
});
|
|
@ -1,21 +0,0 @@
|
|||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
import { get } from "@ember/object";
|
||||
|
||||
export default DiscourseRoute.extend({
|
||||
model(params) {
|
||||
return this.store.findAll("web-hook-event", get(params, "web_hook_id"));
|
||||
},
|
||||
|
||||
setupController(controller, model) {
|
||||
controller.set("model", model);
|
||||
controller.subscribe();
|
||||
},
|
||||
|
||||
deactivate() {
|
||||
this.controllerFor("adminWebHooks.showEvents").unsubscribe();
|
||||
},
|
||||
|
||||
renderTemplate() {
|
||||
this.render("admin/templates/web-hooks-show-events", { into: "adminApi" });
|
||||
},
|
||||
});
|
|
@ -1,30 +1,7 @@
|
|||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
import { get } from "@ember/object";
|
||||
|
||||
export default DiscourseRoute.extend({
|
||||
serialize(model) {
|
||||
return { web_hook_id: model.get("id") || "new" };
|
||||
},
|
||||
|
||||
model(params) {
|
||||
if (params.web_hook_id === "new") {
|
||||
return this.store.createRecord("web-hook");
|
||||
}
|
||||
return this.store.find("web-hook", get(params, "web_hook_id"));
|
||||
},
|
||||
|
||||
setupController(controller, model) {
|
||||
if (model.get("isNew")) {
|
||||
model.set("web_hook_event_types", controller.get("defaultEventTypes"));
|
||||
}
|
||||
|
||||
model.set("category_ids", model.get("category_ids"));
|
||||
model.set("tag_names", model.get("tag_names"));
|
||||
model.set("group_ids", model.get("group_ids"));
|
||||
controller.setProperties({ model, saved: false });
|
||||
},
|
||||
|
||||
renderTemplate() {
|
||||
this.render("admin/templates/web-hooks-show", { into: "adminApi" });
|
||||
return this.store.find("web-hook", params.web_hook_id);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import Route from "@ember/routing/route";
|
||||
|
||||
export default Route.extend({
|
||||
model() {
|
||||
return this.store.findAll("web-hook");
|
||||
|
|
|
@ -1,3 +1,11 @@
|
|||
<Input id={{this.typeName}} @type="checkbox" name="event-choice" @checked={{this.enabled}} />
|
||||
<label for={{this.typeName}}>{{this.name}}</label>
|
||||
<p>{{this.details}}</p>
|
||||
<label>
|
||||
<Input
|
||||
@type="checkbox"
|
||||
@checked={{this.enabled}}
|
||||
name="event-choice"
|
||||
/>
|
||||
|
||||
{{this.name}}
|
||||
|
||||
<p>{{this.details}}</p>
|
||||
</label>
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
<LinkTo @route="adminWebHooks" class="go-back">
|
||||
{{d-icon "arrow-left"}}
|
||||
{{i18n "admin.web_hooks.go_back"}}
|
||||
</LinkTo>
|
||||
|
||||
<div class="web-hook-container">
|
||||
<p>{{i18n "admin.web_hooks.detailed_instruction"}}</p>
|
||||
|
||||
<form class="web-hook form-horizontal">
|
||||
<div class="control-group">
|
||||
<label for="payload-url">{{i18n "admin.web_hooks.payload_url"}}</label>
|
||||
<TextField @name="payload-url" @value={{this.model.payload_url}} @placeholderKey="admin.web_hooks.payload_url_placeholder" />
|
||||
<InputTip @validation={{this.urlValidation}} />
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label for="content-type">{{i18n "admin.web_hooks.content_type"}}</label>
|
||||
<ComboBox @content={{this.contentTypes}} @name="content-type" @value={{this.model.content_type}} @onChange={{action (mut this.model.content_type)}} />
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label for="secret">{{i18n "admin.web_hooks.secret"}}</label>
|
||||
<TextField @name="secret" @value={{this.model.secret}} @placeholderKey="admin.web_hooks.secret_placeholder" />
|
||||
<InputTip @validation={{this.secretValidation}} />
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label>{{i18n "admin.web_hooks.event_chooser"}}</label>
|
||||
<label>
|
||||
<RadioButton @class="subscription-choice" @name="subscription-choice" @value="individual" @selection={{this.model.webHookType}} />
|
||||
{{i18n "admin.web_hooks.individual_event"}}
|
||||
<InputTip @validation={{this.eventTypeValidation}} />
|
||||
</label>
|
||||
|
||||
{{#unless this.model.wildcard_web_hook}}
|
||||
<div class="event-selector">
|
||||
{{#each this.eventTypes as |type|}}
|
||||
<AdminWebHookEventChooser @type={{type}} @model={{this.model.web_hook_event_types}} />
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/unless}}
|
||||
|
||||
<label>
|
||||
<RadioButton @class="subscription-choice" @name="subscription-choice" @value="wildcard" @selection={{this.model.webHookType}} />
|
||||
{{i18n "admin.web_hooks.wildcard_event"}}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="filters control-group">
|
||||
<div class="filter">
|
||||
<label>{{d-icon "circle" class="tracking"}}{{i18n "admin.web_hooks.categories_filter"}}</label>
|
||||
<CategorySelector @categories={{this.model.categories}} @onChange={{action (mut this.model.categories)}} />
|
||||
<div class="instructions">{{i18n "admin.web_hooks.categories_filter_instructions"}}</div>
|
||||
</div>
|
||||
|
||||
{{#if this.showTagsFilter}}
|
||||
<div class="filter">
|
||||
<label>{{d-icon "circle" class="tracking"}}{{i18n "admin.web_hooks.tags_filter"}}</label>
|
||||
<TagChooser @tags={{this.model.tag_names}} @everyTag={{true}} @excludeSynonyms={{true}} />
|
||||
<div class="instructions">{{i18n "admin.web_hooks.tags_filter_instructions"}}</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class="filter">
|
||||
<label>{{d-icon "circle" class="tracking"}}{{i18n "admin.web_hooks.groups_filter"}}</label>
|
||||
<GroupSelector @groupNames={{this.model.groupsFilterInName}} @groupFinder={{this.model.groupFinder}} />
|
||||
<div class="instructions">{{i18n "admin.web_hooks.groups_filter_instructions"}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<PluginOutlet @name="web-hook-fields" @tagName="span" @connectorTagName="div" @args={{hash model=this.model}} />
|
||||
|
||||
<label>
|
||||
<Input @type="checkbox" name="verify_certificate" @checked={{this.model.verify_certificate}} />
|
||||
{{i18n "admin.web_hooks.verify_certificate"}}
|
||||
</label>
|
||||
|
||||
<div>
|
||||
<label>
|
||||
<Input @type="checkbox" name="active" @checked={{this.model.active}} />
|
||||
{{i18n "admin.web_hooks.active"}}
|
||||
</label>
|
||||
|
||||
{{#if this.model.active}}
|
||||
<div class="instructions">{{i18n "admin.web_hooks.active_notice"}}</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="controls">
|
||||
<DButton
|
||||
@translatedLabel={{this.saveButtonText}}
|
||||
@action={{action "save"}}
|
||||
@disabled={{this.saveButtonDisabled}}
|
||||
@class="btn-primary admin-webhooks__save-button"
|
||||
/>
|
||||
|
||||
{{#if this.model.isNew}}
|
||||
<LinkTo @route="adminWebHooks" class="btn btn-default">
|
||||
{{i18n "cancel"}}
|
||||
</LinkTo>
|
||||
{{else}}
|
||||
<LinkTo @route="adminWebHooks.show" @model={{this.model}} class="btn btn-default">
|
||||
{{i18n "cancel"}}
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
|
||||
<span class="saving">{{this.savingStatus}}</span>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,68 @@
|
|||
<div class="web-hooks-listing">
|
||||
<p>{{i18n "admin.web_hooks.instruction"}}</p>
|
||||
|
||||
<div class="new-webhook">
|
||||
<LinkTo
|
||||
@route="adminWebHooks.edit"
|
||||
@model="new"
|
||||
class="btn btn-default admin-webhooks__new-button"
|
||||
>
|
||||
{{d-icon "plus"}}
|
||||
{{i18n "admin.web_hooks.new"}}
|
||||
</LinkTo>
|
||||
</div>
|
||||
|
||||
{{#if this.model}}
|
||||
<LoadMore @selector=".web-hooks tr" @action={{this.loadMore}}>
|
||||
<table class="web-hooks grid">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{i18n "admin.web_hooks.delivery_status.title"}}</th>
|
||||
<th>{{i18n "admin.web_hooks.payload_url"}}</th>
|
||||
<th>{{i18n "admin.web_hooks.description_label"}}</th>
|
||||
<th>{{i18n "admin.web_hooks.controls"}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each this.model as |webHook|}}
|
||||
<tr>
|
||||
<td class="delivery-status">
|
||||
<LinkTo @route="adminWebHooks.show" @model={{webHook}}>
|
||||
<AdminWebHookStatus
|
||||
@deliveryStatuses={{this.deliveryStatuses}}
|
||||
@model={{webHook}}
|
||||
/>
|
||||
</LinkTo>
|
||||
</td>
|
||||
<td class="payload-url">
|
||||
<LinkTo @route="adminWebHooks.edit" @model={{webHook}}>{{webHook.payload_url}}</LinkTo>
|
||||
</td>
|
||||
<td class="description">{{webHook.description}}</td>
|
||||
<td class="controls">
|
||||
<LinkTo
|
||||
@route="adminWebHooks.edit"
|
||||
@model={{webHook}}
|
||||
class="btn btn-default no-text"
|
||||
title={{i18n "admin.web_hooks.edit"}}
|
||||
>
|
||||
{{d-icon "far-edit"}}
|
||||
</LinkTo>
|
||||
|
||||
<DButton
|
||||
@class="destroy btn-danger"
|
||||
@action={{this.destroy}}
|
||||
@actionParam={{webHook}}
|
||||
@icon="times"
|
||||
@title="delete"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
<ConditionalLoadingSpinner @condition={{this.model.loadingMore}} />
|
||||
</LoadMore>
|
||||
{{else}}
|
||||
<p>{{i18n "admin.web_hooks.none"}}</p>
|
||||
{{/if}}
|
||||
</div>
|
|
@ -3,90 +3,32 @@
|
|||
{{i18n "admin.web_hooks.go_back"}}
|
||||
</LinkTo>
|
||||
|
||||
<div class="web-hook-container">
|
||||
<p>{{i18n "admin.web_hooks.detailed_instruction"}}</p>
|
||||
<form class="web-hook form-horizontal">
|
||||
<div class="control-group">
|
||||
<label for="payload-url">{{i18n "admin.web_hooks.payload_url"}}</label>
|
||||
<TextField @name="payload-url" @value={{this.model.payload_url}} @placeholderKey="admin.web_hooks.payload_url_placeholder" />
|
||||
<InputTip @validation={{this.urlValidation}} />
|
||||
</div>
|
||||
<div class="admin-webhooks__summary">
|
||||
<h1>
|
||||
{{this.model.payload_url}}
|
||||
|
||||
<div class="control-group">
|
||||
<label for="content-type">{{i18n "admin.web_hooks.content_type"}}</label>
|
||||
<ComboBox @content={{this.contentTypes}} @name="content-type" @value={{this.model.content_type}} @onChange={{action (mut this.model.content_type)}} />
|
||||
</div>
|
||||
<DButton
|
||||
@action={{this.edit}}
|
||||
@icon="far-edit"
|
||||
title={{i18n "admin.web_hooks.edit"}}
|
||||
class="btn no-text admin-webhooks__edit-button"
|
||||
/>
|
||||
|
||||
<div class="control-group">
|
||||
<label for="secret">{{i18n "admin.web_hooks.secret"}}</label>
|
||||
<TextField @name="secret" @value={{this.model.secret}} @placeholderKey="admin.web_hooks.secret_placeholder" />
|
||||
<InputTip @validation={{this.secretValidation}} />
|
||||
</div>
|
||||
<DButton
|
||||
@action={{this.destroy}}
|
||||
@icon="times"
|
||||
@title="delete"
|
||||
class="destroy btn-danger admin-webhooks__delete-button"
|
||||
/>
|
||||
</h1>
|
||||
|
||||
<div class="control-group">
|
||||
<label>{{i18n "admin.web_hooks.event_chooser"}}</label>
|
||||
<div>
|
||||
<RadioButton @class="subscription-choice" @name="subscription-choice" @value="individual" @selection={{this.model.webHookType}} />
|
||||
{{i18n "admin.web_hooks.individual_event"}}
|
||||
<InputTip @validation={{this.eventTypeValidation}} />
|
||||
</div>
|
||||
{{#unless this.model.wildcard_web_hook}}
|
||||
<div class="event-selector">
|
||||
{{#each this.eventTypes as |type|}}
|
||||
<AdminWebHookEventChooser @type={{type}} @model={{this.model.web_hook_event_types}} />
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/unless}}
|
||||
<div>
|
||||
<RadioButton @class="subscription-choice" @name="subscription-choice" @value="wildcard" @selection={{this.model.webHookType}} />
|
||||
{{i18n "admin.web_hooks.wildcard_event"}}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<span class="admin-webhooks__description-label">
|
||||
{{i18n "admin.web_hooks.description_label"}}:
|
||||
</span>
|
||||
|
||||
<div class="filters control-group">
|
||||
<div class="filter">
|
||||
<label>{{d-icon "circle" class="tracking"}}{{i18n "admin.web_hooks.categories_filter"}}</label>
|
||||
<CategorySelector @categories={{this.model.categories}} @onChange={{action (mut this.model.categories)}} />
|
||||
<div class="instructions">{{i18n "admin.web_hooks.categories_filter_instructions"}}</div>
|
||||
</div>
|
||||
{{#if this.showTagsFilter}}
|
||||
<div class="filter">
|
||||
<label>{{d-icon "circle" class="tracking"}}{{i18n "admin.web_hooks.tags_filter"}}</label>
|
||||
<TagChooser @tags={{this.model.tag_names}} @everyTag={{true}} @excludeSynonyms={{true}} />
|
||||
<div class="instructions">{{i18n "admin.web_hooks.tags_filter_instructions"}}</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
<div class="filter">
|
||||
<label>{{d-icon "circle" class="tracking"}}{{i18n "admin.web_hooks.groups_filter"}}</label>
|
||||
<GroupSelector @groupNames={{this.model.groupsFilterInName}} @groupFinder={{this.model.groupFinder}} />
|
||||
<div class="instructions">{{i18n "admin.web_hooks.groups_filter_instructions"}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<PluginOutlet @name="web-hook-fields" @tagName="span" @connectorTagName="div" @args={{hash model=this.model}} />
|
||||
|
||||
<div>
|
||||
<Input @type="checkbox" name="verify_certificate" @checked={{this.model.verify_certificate}} /> {{i18n "admin.web_hooks.verify_certificate"}}
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
<Input @type="checkbox" name="active" @checked={{this.model.active}} /> {{i18n "admin.web_hooks.active"}}
|
||||
</div>
|
||||
{{#if this.model.active}}
|
||||
<div class="instructions">{{i18n "admin.web_hooks.active_notice"}}</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="controls">
|
||||
<DButton @class="btn-default" @translatedLabel={{this.saveButtonText}} @action={{action "save"}} @disabled={{this.saveButtonDisabled}} />
|
||||
|
||||
{{#unless this.model.isNew}}
|
||||
<DButton @class="btn-danger" @label="admin.web_hooks.destroy" @action={{action "destroy"}} />
|
||||
<LinkTo @route="adminWebHooks.showEvents" @model={{this.model.id}} class="btn">
|
||||
{{i18n "admin.web_hooks.events.go_events"}}
|
||||
</LinkTo>
|
||||
{{/unless}}
|
||||
<span class="saving">{{this.savingStatus}}</span>
|
||||
{{this.model.description}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<WebhookEvents @webhookId={{this.model.id}} />
|
||||
|
|
|
@ -1,38 +1 @@
|
|||
<div class="web-hooks-listing">
|
||||
<p>{{i18n "admin.web_hooks.instruction"}}</p>
|
||||
<div class="new-webhook">
|
||||
<LinkTo @route="adminWebHooks.show" @model="new" class="btn btn-default">
|
||||
{{d-icon "plus"}} {{i18n "admin.web_hooks.new"}}
|
||||
</LinkTo>
|
||||
</div>
|
||||
{{#if this.model}}
|
||||
<LoadMore @selector=".web-hooks tr" @action={{action "loadMore"}}>
|
||||
<table class="web-hooks grid">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{i18n "admin.web_hooks.delivery_status.title"}}</th>
|
||||
<th>{{i18n "admin.web_hooks.payload_url"}}</th>
|
||||
<th>{{i18n "admin.web_hooks.description"}}</th>
|
||||
<th>{{i18n "admin.web_hooks.controls"}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each this.model as |webHook|}}
|
||||
<tr>
|
||||
<td class="delivery-status"><LinkTo @route="adminWebHooks.showEvents" @model={{webHook.id}}><AdminWebHookStatus @deliveryStatuses={{this.deliveryStatuses}} @model={{webHook}} /></LinkTo></td>
|
||||
<td class="payload-url"><LinkTo @route="adminWebHooks.show" @model={{webHook}}>{{webHook.payload_url}}</LinkTo></td>
|
||||
<td class="description">{{webHook.description}}</td>
|
||||
<td class="controls">
|
||||
<LinkTo @route="adminWebHooks.show" @model={{webHook}} class="btn btn-default no-text">{{d-icon "far-edit"}}</LinkTo>
|
||||
<DButton @class="destroy btn-danger" @action={{action "destroy"}} @actionParam={{webHook}} @icon="times" />
|
||||
</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
<ConditionalLoadingSpinner @condition={{this.model.loadingMore}} />
|
||||
</LoadMore>
|
||||
{{else}}
|
||||
<p>{{i18n "admin.web_hooks.none"}}</p>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{outlet}}
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
import { acceptance } from "discourse/tests/helpers/qunit-helpers";
|
||||
import { click, currentURL, fillIn, visit } from "@ember/test-helpers";
|
||||
import { test } from "qunit";
|
||||
import pretender, {
|
||||
parsePostData,
|
||||
response,
|
||||
} from "discourse/tests/helpers/create-pretender";
|
||||
|
||||
acceptance("Admin - Webhooks", function (needs) {
|
||||
needs.user();
|
||||
|
||||
test("adding a webhook", async function (assert) {
|
||||
pretender.get("/admin/api/web_hooks", () => {
|
||||
return response({
|
||||
web_hooks: [],
|
||||
total_rows_web_hooks: 0,
|
||||
load_more_web_hooks: "/admin/api/web_hooks.json?limit=50&offset=50",
|
||||
extras: {
|
||||
content_types: [
|
||||
{ id: 1, name: "application/json" },
|
||||
{ id: 2, name: "application/x-www-form-urlencoded" },
|
||||
],
|
||||
default_event_types: [{ id: 2, name: "post" }],
|
||||
delivery_statuses: [
|
||||
{ id: 1, name: "inactive" },
|
||||
{ id: 2, name: "failed" },
|
||||
{ id: 3, name: "successful" },
|
||||
],
|
||||
event_types: [
|
||||
{ id: 1, name: "topic" },
|
||||
{ id: 2, name: "post" },
|
||||
{ id: 3, name: "user" },
|
||||
{ id: 4, name: "group" },
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
pretender.get("/admin/api/web_hook_events/1", () => {
|
||||
return response({
|
||||
web_hook_events: [],
|
||||
load_more_web_hook_events:
|
||||
"/admin/api/web_hook_events/1.json?limit=50&offset=50",
|
||||
total_rows_web_hook_events: 15,
|
||||
extras: { web_hook_id: 1 },
|
||||
});
|
||||
});
|
||||
|
||||
pretender.post("/admin/api/web_hooks", (request) => {
|
||||
const data = parsePostData(request.requestBody);
|
||||
assert.strictEqual(
|
||||
data.web_hook.payload_url,
|
||||
"https://example.com/webhook"
|
||||
);
|
||||
|
||||
return response({
|
||||
web_hook: {
|
||||
id: 1,
|
||||
// other attrs
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
await visit("/admin/api/web_hooks");
|
||||
await click(".admin-webhooks__new-button");
|
||||
|
||||
await fillIn(`[name="payload-url"`, "https://example.com/webhook");
|
||||
await click(".admin-webhooks__save-button");
|
||||
|
||||
assert.strictEqual(currentURL(), "/admin/api/web_hooks/1");
|
||||
});
|
||||
});
|
|
@ -265,14 +265,21 @@ table.api-keys {
|
|||
}
|
||||
}
|
||||
|
||||
.web-hook-direction {
|
||||
a,
|
||||
button {
|
||||
margin-right: 10px;
|
||||
}
|
||||
.admin-webhooks__summary {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.admin-webhooks__edit-button,
|
||||
.admin-webhooks__delete-button {
|
||||
font-size: var(--font-0-rem);
|
||||
}
|
||||
|
||||
.web-hook-events {
|
||||
.heading-container {
|
||||
width: 100%;
|
||||
background-color: var(--primary-low);
|
||||
}
|
||||
|
||||
li {
|
||||
padding: 2px 0;
|
||||
}
|
||||
|
@ -283,6 +290,13 @@ table.api-keys {
|
|||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
.col.heading {
|
||||
font-weight: bold;
|
||||
padding: 4px 0;
|
||||
}
|
||||
.col.heading.actions {
|
||||
padding: 4px 0;
|
||||
}
|
||||
.col.first {
|
||||
width: 90px;
|
||||
}
|
||||
|
@ -296,18 +310,14 @@ table.api-keys {
|
|||
width: 250px;
|
||||
}
|
||||
.col.actions {
|
||||
width: 455px;
|
||||
padding-top: 0;
|
||||
a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
.col.heading.actions {
|
||||
padding: 4px 0;
|
||||
}
|
||||
.details {
|
||||
display: block;
|
||||
margin-top: 10px;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
label {
|
||||
font-size: var(--font-0);
|
||||
|
@ -317,10 +327,13 @@ table.api-keys {
|
|||
}
|
||||
}
|
||||
|
||||
.webhook-events__ping-button {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.web-hook-events-listing {
|
||||
margin-top: 15px;
|
||||
.alert {
|
||||
margin: 15px 0 0 0;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,28 +1,5 @@
|
|||
// Styles for /admin/logs
|
||||
|
||||
.web-hook-events {
|
||||
border-bottom: dotted 1px var(--primary-low-mid);
|
||||
.heading-container {
|
||||
width: 100%;
|
||||
background-color: var(--primary-low);
|
||||
}
|
||||
.col.heading {
|
||||
font-weight: bold;
|
||||
padding: 4px 0;
|
||||
}
|
||||
.col {
|
||||
display: inline-block;
|
||||
padding-top: 6px;
|
||||
vertical-align: top;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
.ember-list-item-view {
|
||||
width: 100%;
|
||||
border-top: solid 1px var(--primary-low);
|
||||
}
|
||||
}
|
||||
|
||||
.log-details-modal {
|
||||
pre {
|
||||
white-space: pre-wrap;
|
||||
|
|
|
@ -32,6 +32,10 @@ class Admin::WebHooksController < Admin::AdminController
|
|||
render_serialized(@web_hook, AdminWebHookSerializer, root: 'web_hook')
|
||||
end
|
||||
|
||||
def edit
|
||||
render_serialized(@web_hook, AdminWebHookSerializer, root: 'web_hook')
|
||||
end
|
||||
|
||||
def create
|
||||
web_hook = WebHook.new(web_hook_params)
|
||||
|
||||
|
@ -58,9 +62,6 @@ class Admin::WebHooksController < Admin::AdminController
|
|||
render json: success_json
|
||||
end
|
||||
|
||||
def new
|
||||
end
|
||||
|
||||
def list_events
|
||||
limit = 50
|
||||
offset = params[:offset].to_i
|
||||
|
|
|
@ -4724,9 +4724,9 @@ en:
|
|||
detailed_instruction: "A POST request will be sent to provided URL when chosen event happens."
|
||||
new: "New Webhook"
|
||||
create: "Create"
|
||||
edit: "Edit"
|
||||
save: "Save"
|
||||
destroy: "Delete"
|
||||
description: "Description"
|
||||
description_label: "Event triggers"
|
||||
controls: "Controls"
|
||||
go_back: "Back to list"
|
||||
payload_url: "Payload URL"
|
||||
|
@ -4807,9 +4807,6 @@ en:
|
|||
headers: "Headers"
|
||||
payload: "Payload"
|
||||
body: "Body"
|
||||
go_list: "Go to list"
|
||||
go_details: "Edit webhook"
|
||||
go_events: "Go to events"
|
||||
ping: "Ping"
|
||||
status: "Status Code"
|
||||
event_id: "ID"
|
||||
|
|
|
@ -300,7 +300,6 @@ Discourse::Application.routes.draw do
|
|||
|
||||
resources :web_hooks
|
||||
get 'web_hook_events/:id' => 'web_hooks#list_events', as: :web_hook_events
|
||||
get 'web_hooks/:id/events' => 'web_hooks#list_events'
|
||||
get 'web_hooks/:id/events/bulk' => 'web_hooks#bulk_events'
|
||||
post 'web_hooks/:web_hook_id/events/:event_id/redeliver' => 'web_hooks#redeliver_event'
|
||||
post 'web_hooks/:id/ping' => 'web_hooks#ping'
|
||||
|
|
Loading…
Reference in New Issue