From 35ecaee6d3fc77aea1f13e1184991bb90b64ef79 Mon Sep 17 00:00:00 2001 From: Aileen Nowak Date: Tue, 29 Mar 2016 10:40:44 +0200 Subject: [PATCH] Slack integration closes #6584 - Frontend Changes: - adds 'Apps' to Navigation Menu - adds 'Slack' as nested page to Apps - adds `apps.css` - adds `slack-integration` model and uses `slack-settings` custom transform to parse JSON file - adds validation for `slack` model - adds fixtures and `slack/test` API endpoint to Mirage - adds acceptance tests for `apps-test` and `slack-test` - adds unit tests for `slack-settings` and `slack-integration` - Backend Changes: - adds API endpoint `slack/test` to send Test Notification - adds default-values for slack model - sends payload to slack: - text: the url of the blogpost / test message - icon_url: url to ghost logo - username: Ghost - adds `slack/index.js` to send webhook to slack if - a new post is published (if slack webhook url is saved in settings) - user clicks on 'Send Test Notification' in UI - adds `slack.init()` to `server.index.js` to add event listener - adds unit test for `slack/index` --- .../app/controllers/settings/apps/index.js | 14 + .../app/controllers/settings/apps/slack.js | 75 ++++++ core/client/app/html/apps.html | 4 +- core/client/app/html/themes.html | 2 +- core/client/app/mirage/config.js | 6 + core/client/app/mirage/fixtures/settings.js | 11 + core/client/app/mixins/validation-engine.js | 4 +- core/client/app/models/setting.js | 3 +- core/client/app/models/slack-integration.js | 19 ++ core/client/app/router.js | 3 + core/client/app/routes/settings/apps.js | 20 ++ core/client/app/routes/settings/apps/slack.js | 19 ++ core/client/app/styles/app.css | 1 + .../app/styles/components/power-select.css | 15 ++ core/client/app/styles/layouts/apps.css | 252 ++++++++++++++++++ core/client/app/styles/layouts/main.css | 5 +- core/client/app/styles/layouts/packages.css | 24 +- core/client/app/styles/patterns/icons.css | 7 +- .../app/templates/components/gh-nav-menu.hbs | 3 +- core/client/app/templates/settings/apps.hbs | 3 + .../app/templates/settings/apps/index.hbs | 33 +++ .../app/templates/settings/apps/slack.hbs | 42 +++ core/client/app/transforms/slack-settings.js | 37 +++ .../app/validators/slack-integration.js | 24 ++ .../client/public/assets/fonts/ghosticons.eot | Bin 17228 -> 22584 bytes .../client/public/assets/fonts/ghosticons.svg | 159 +++++------ .../client/public/assets/fonts/ghosticons.ttf | Bin 17052 -> 22420 bytes .../public/assets/fonts/ghosticons.woff | Bin 11036 -> 22496 bytes core/client/public/assets/img/slackicon.png | Bin 0 -> 18136 bytes .../tests/acceptance/settings/apps-test.js | 89 +++++++ .../tests/acceptance/settings/slack-test.js | 121 +++++++++ .../unit/transforms/slack-settings-test.js | 37 +++ .../unit/validators/slack-integration-test.js | 66 +++++ core/server/api/index.js | 4 +- core/server/api/slack.js | 27 ++ core/server/data/schema/default-settings.json | 3 + core/server/data/slack/index.js | 106 ++++++++ core/server/index.js | 5 +- core/server/routes/api.js | 3 + core/test/unit/slack_spec.js | 184 +++++++++++++ 40 files changed, 1326 insertions(+), 104 deletions(-) create mode 100644 core/client/app/controllers/settings/apps/index.js create mode 100644 core/client/app/controllers/settings/apps/slack.js create mode 100644 core/client/app/models/slack-integration.js create mode 100644 core/client/app/routes/settings/apps.js create mode 100644 core/client/app/routes/settings/apps/slack.js create mode 100644 core/client/app/styles/layouts/apps.css create mode 100644 core/client/app/templates/settings/apps.hbs create mode 100644 core/client/app/templates/settings/apps/index.hbs create mode 100644 core/client/app/templates/settings/apps/slack.hbs create mode 100644 core/client/app/transforms/slack-settings.js create mode 100644 core/client/app/validators/slack-integration.js mode change 100644 => 100755 core/client/public/assets/fonts/ghosticons.eot mode change 100644 => 100755 core/client/public/assets/fonts/ghosticons.svg mode change 100644 => 100755 core/client/public/assets/fonts/ghosticons.ttf mode change 100644 => 100755 core/client/public/assets/fonts/ghosticons.woff create mode 100644 core/client/public/assets/img/slackicon.png create mode 100644 core/client/tests/acceptance/settings/apps-test.js create mode 100644 core/client/tests/acceptance/settings/slack-test.js create mode 100644 core/client/tests/unit/transforms/slack-settings-test.js create mode 100644 core/client/tests/unit/validators/slack-integration-test.js create mode 100644 core/server/api/slack.js create mode 100644 core/server/data/slack/index.js create mode 100644 core/test/unit/slack_spec.js diff --git a/core/client/app/controllers/settings/apps/index.js b/core/client/app/controllers/settings/apps/index.js new file mode 100644 index 0000000000..5017fbc1d7 --- /dev/null +++ b/core/client/app/controllers/settings/apps/index.js @@ -0,0 +1,14 @@ +import Ember from 'ember'; + +const { + computed, + inject: {controller} +} = Ember; + +const {alias} = computed; + +export default Ember.Controller.extend({ + appsController: controller('settings.apps'), + + slack: alias('appsController.model.slack.firstObject') +}); diff --git a/core/client/app/controllers/settings/apps/slack.js b/core/client/app/controllers/settings/apps/slack.js new file mode 100644 index 0000000000..26877cd8a6 --- /dev/null +++ b/core/client/app/controllers/settings/apps/slack.js @@ -0,0 +1,75 @@ +import Ember from 'ember'; +import { invoke } from 'ember-invoke-action'; + +const { + Controller, + computed: {empty}, + inject: {service} +} = Ember; + +export default Controller.extend({ + ghostPaths: service(), + ajax: service(), + notifications: service(), + + // will be set by route + settings: null, + + isSaving: false, + savePromise: null, + isSendingTest: false, + + testNotificationDisabled: empty('model.url'), + + actions: { + sendTestNotification() { + let notifications = this.get('notifications'); + let slackApi = this.get('ghostPaths.url').api('slack', 'test'); + + if (this.get('isSendingTest')) { + return; + } + + this.set('isSendingTest', true); + + invoke(this, 'save').then(() => { + this.get('ajax').post(slackApi).then(() => { + notifications.showAlert('Check your slack channel test message.', {type: 'info', key: 'slack-test.send.success'}); + }).catch((error) => { + notifications.showAPIError(error, {key: 'slack-test:send'}); + }); + }).catch(() => { + // noop - error already handled in .save + }).finally(() => { + this.set('isSendingTest', false); + }); + }, + + updateURL(value) { + this.set('model.url', value); + this.get('model.errors').clear(); + }, + + save() { + let slack = this.get('model'); + let settings = this.get('settings'); + + if (this.get('isSaving')) { + return; + } + + return slack.validate().then(() => { + settings.get('slack').clear().pushObject(slack); + + this.set('isSaving', true); + + return settings.save().catch((err) => { + this.get('notifications').showErrors(err); + throw err; + }).finally(() => { + this.set('isSaving', false); + }); + }); + } + } +}); diff --git a/core/client/app/html/apps.html b/core/client/app/html/apps.html index ba3a41e0e8..ec8783bdd8 100644 --- a/core/client/app/html/apps.html +++ b/core/client/app/html/apps.html @@ -47,8 +47,8 @@
  • Navigation
  • Tags
  • Code Injection
  • -
  • Labs
  • -
  • Apps
  • +
  • Labs
  • +
  • Apps