mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-28 22:43:30 +03:00
✨ Added custom integrations - Content API Keys and "Site rebuild" webhooks (#1063)
closes TryGhost/Ghost#9942 - move custom integrations UI out from behind the developer experiments flag - put Admin API key and web hook secret fields behind the developer experiments flag - do not show "unsaved changes" modal when adding/editing a webhook - fixed all webhooks showing for each custom integration
This commit is contained in:
parent
7308fcba79
commit
ac17053863
@ -8,6 +8,7 @@ import {inject as service} from '@ember/service';
|
||||
import {task} from 'ember-concurrency';
|
||||
|
||||
export default ModalComponent.extend({
|
||||
config: service(),
|
||||
router: service(),
|
||||
|
||||
availableEvents: null,
|
||||
|
@ -6,9 +6,12 @@ import {
|
||||
import {alias} from '@ember/object/computed';
|
||||
import {computed} from '@ember/object';
|
||||
import {htmlSafe} from '@ember/string';
|
||||
import {inject as service} from '@ember/service';
|
||||
import {task, timeout} from 'ember-concurrency';
|
||||
|
||||
export default Controller.extend({
|
||||
config: service(),
|
||||
|
||||
imageExtensions: IMAGE_EXTENSIONS,
|
||||
imageMimeTypes: IMAGE_MIME_TYPES,
|
||||
|
||||
@ -18,7 +21,7 @@ export default Controller.extend({
|
||||
return this.store.peekAll('webhook');
|
||||
}),
|
||||
|
||||
filteredWebhooks: computed('allWebhooks.@each.{isNew,isDeleted}', function () {
|
||||
filteredWebhooks: computed('integration.id', 'allWebhooks.@each.{isNew,isDeleted}', function () {
|
||||
return this.allWebhooks.filter((webhook) => {
|
||||
let matchesIntegration = webhook.belongsTo('integration').id() === this.integration.id;
|
||||
|
||||
|
@ -5,7 +5,6 @@ import {inject as service} from '@ember/service';
|
||||
import {task} from 'ember-concurrency';
|
||||
|
||||
export default Controller.extend({
|
||||
config: service(),
|
||||
settings: service(),
|
||||
store: service(),
|
||||
|
||||
|
@ -46,7 +46,16 @@ export default AuthenticatedRoute.extend(styleBody, CurrentUserSettings, {
|
||||
willTransition(transition) {
|
||||
let {controller} = this;
|
||||
|
||||
if (!controller.integration.isDeleted && controller.integration.hasDirtyAttributes) {
|
||||
// check to see if we're navigating away from the custom integration
|
||||
// route - we want to allow editing webhooks without showing the
|
||||
// "unsaved changes" confirmation modal
|
||||
let isExternalRoute =
|
||||
// allow sub-routes of settings.integration
|
||||
!transition.targetName.match(/^settings\.integration\./)
|
||||
// do not allow changes in integration
|
||||
|| transition.params['settings.integration'].integration_id !== controller.integration.id;
|
||||
|
||||
if (isExternalRoute && !controller.integration.isDeleted && controller.integration.hasDirtyAttributes) {
|
||||
transition.abort();
|
||||
controller.send('toggleUnsavedChangesModal', transition);
|
||||
return;
|
||||
|
@ -61,24 +61,26 @@
|
||||
{{gh-error-message errors=webhook.errors property="targetUrl" data-test-error="new-webhook-targetUrl"}}
|
||||
{{/gh-form-group}}
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
{{#gh-form-group errors=webhook.errors hasValidated=webhook.hasValidated property="secret"}}
|
||||
<label for="new-webhook-secret" class="fw6">Secret</label>
|
||||
{{gh-text-input
|
||||
value=(readonly webhook.secret)
|
||||
oninput=(action (mut webhook.secret) value="target.value")
|
||||
focus-out=(action "validate" "secret" target=webhook)
|
||||
id="new-webhook-secret"
|
||||
name="secret"
|
||||
class="gh-input mt1"
|
||||
placeholder="Webhook secret..."
|
||||
autofocus="autofocus"
|
||||
autocapitalize="off"
|
||||
autocorrect="off"
|
||||
data-test-input="new-webhook-secret"}}
|
||||
{{gh-error-message errors=webhook.errors property="secret" data-test-error="new-webhook-secret"}}
|
||||
{{/gh-form-group}}
|
||||
</fieldset>
|
||||
{{#if config.enableDeveloperExperiments}}
|
||||
<fieldset>
|
||||
{{#gh-form-group errors=webhook.errors hasValidated=webhook.hasValidated property="secret"}}
|
||||
<label for="new-webhook-secret" class="fw6">Secret</label>
|
||||
{{gh-text-input
|
||||
value=(readonly webhook.secret)
|
||||
oninput=(action (mut webhook.secret) value="target.value")
|
||||
focus-out=(action "validate" "secret" target=webhook)
|
||||
id="new-webhook-secret"
|
||||
name="secret"
|
||||
class="gh-input mt1"
|
||||
placeholder="Webhook secret..."
|
||||
autofocus="autofocus"
|
||||
autocapitalize="off"
|
||||
autocorrect="off"
|
||||
data-test-input="new-webhook-secret"}}
|
||||
{{gh-error-message errors=webhook.errors property="secret" data-test-error="new-webhook-secret"}}
|
||||
{{/gh-form-group}}
|
||||
</fieldset>
|
||||
{{/if}}
|
||||
{{#if error}}
|
||||
<p class="red">{{error}}</p>
|
||||
{{/if}}
|
||||
|
@ -124,36 +124,38 @@
|
||||
</div>
|
||||
{{/gh-validation-status-container}}
|
||||
{{/with}}
|
||||
{{#with integration.adminKey as |adminKey|}}
|
||||
{{#gh-validation-status-container class="flex flex-column w-100 ml3"}}
|
||||
<div class="flex">
|
||||
<label for="admin_key" class="flex-grow-1 darkgrey fw7 f8">
|
||||
Admin API Key
|
||||
</label>
|
||||
<span class="db f8 midgrey">
|
||||
{{#if copyAdminKey.isRunning}}
|
||||
Copied to clipboard
|
||||
{{else}}
|
||||
{{adminKey.lastSeenAtUTC}}
|
||||
{{/if}}
|
||||
</span>
|
||||
</div>
|
||||
<div class="relative hide-child mt1">
|
||||
<input id="admin_key"
|
||||
class="w-100 pa3 bg-whitegrey-l2 midlightgrey ba b--whitegrey br3"
|
||||
type="text"
|
||||
value={{adminKey.secret}}
|
||||
disabled="true"
|
||||
data-test-input="admin_key">
|
||||
{{#if config.enableDeveloperExperiments}}
|
||||
{{#with integration.adminKey as |adminKey|}}
|
||||
{{#gh-validation-status-container class="flex flex-column w-100 ml3"}}
|
||||
<div class="flex">
|
||||
<label for="admin_key" class="flex-grow-1 darkgrey fw7 f8">
|
||||
Admin API Key
|
||||
</label>
|
||||
<span class="db f8 midgrey">
|
||||
{{#if copyAdminKey.isRunning}}
|
||||
Copied to clipboard
|
||||
{{else}}
|
||||
{{adminKey.lastSeenAtUTC}}
|
||||
{{/if}}
|
||||
</span>
|
||||
</div>
|
||||
<div class="relative hide-child mt1">
|
||||
<input id="admin_key"
|
||||
class="w-100 pa3 bg-whitegrey-l2 midlightgrey ba b--whitegrey br3"
|
||||
type="text"
|
||||
value={{adminKey.secret}}
|
||||
disabled="true"
|
||||
data-test-input="admin_key">
|
||||
|
||||
<div class="absolute top-0 right-1">
|
||||
<div class="pt1 pr3 pb1 pl3 bg-black-70 child br3 f8 nudge-top--6 nudge-right--1">
|
||||
<button type="button" {{action (perform copyAdminKey)}} class="white fw4">Copy</button>
|
||||
<div class="absolute top-0 right-1">
|
||||
<div class="pt1 pr3 pb1 pl3 bg-black-70 child br3 f8 nudge-top--6 nudge-right--1">
|
||||
<button type="button" {{action (perform copyAdminKey)}} class="white fw4">Copy</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/gh-validation-status-container}}
|
||||
{{/with}}
|
||||
{{/gh-validation-status-container}}
|
||||
{{/with}}
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<h4 class="mt15 midgrey f7 fw4">Webhooks</h4>
|
||||
|
@ -101,59 +101,57 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{{#if config.enableDeveloperExperiments}}
|
||||
<section class="apps-grid-container pt6">
|
||||
<div class="flex flex-row items-center pb2">
|
||||
<span class="dib flex-grow-1 midgrey">Custom integrations</span>
|
||||
{{#link-to "settings.integrations.new" class="gh-btn gh-btn-green" data-test-button="new-integration"}}
|
||||
<span>Add custom integration</span>
|
||||
{{/link-to}}
|
||||
</div>
|
||||
<section class="apps-grid-container pt6">
|
||||
<div class="flex flex-row items-center pb2">
|
||||
<span class="dib flex-grow-1 midgrey">Custom integrations</span>
|
||||
{{#link-to "settings.integrations.new" class="gh-btn gh-btn-green" data-test-button="new-integration"}}
|
||||
<span>Add custom integration</span>
|
||||
{{/link-to}}
|
||||
</div>
|
||||
|
||||
<div class="apps-grid">
|
||||
{{#each integrations as |integration|}}
|
||||
<div class="apps-grid-cell" data-test-custom-integration>
|
||||
{{#link-to "settings.integration" integration data-test-integration=integration.id}}
|
||||
<article class="apps-card-app">
|
||||
<div class="apps-card-left">
|
||||
<figure class="apps-card-app-icon flex items-center" style={{integration-icon-style integration}}>
|
||||
{{#unless integration.iconImage}}
|
||||
{{svg-jar "integration" class="w-100 stroke-darkgrey"}}
|
||||
{{/unless}}
|
||||
</figure>
|
||||
<div class="apps-card-meta">
|
||||
<h3 class="apps-card-app-title" data-test-text="name">
|
||||
{{integration.name}}
|
||||
</h3>
|
||||
<p class="apps-card-app-desc" data-test-text="description">
|
||||
{{integration.description}}
|
||||
</p>
|
||||
</div>
|
||||
<div class="apps-grid">
|
||||
{{#each integrations as |integration|}}
|
||||
<div class="apps-grid-cell" data-test-custom-integration>
|
||||
{{#link-to "settings.integration" integration data-test-integration=integration.id}}
|
||||
<article class="apps-card-app">
|
||||
<div class="apps-card-left">
|
||||
<figure class="apps-card-app-icon flex items-center" style={{integration-icon-style integration}}>
|
||||
{{#unless integration.iconImage}}
|
||||
{{svg-jar "integration" class="w-100 stroke-darkgrey"}}
|
||||
{{/unless}}
|
||||
</figure>
|
||||
<div class="apps-card-meta">
|
||||
<h3 class="apps-card-app-title" data-test-text="name">
|
||||
{{integration.name}}
|
||||
</h3>
|
||||
<p class="apps-card-app-desc" data-test-text="description">
|
||||
{{integration.description}}
|
||||
</p>
|
||||
</div>
|
||||
<div class="gh-card-right">
|
||||
<div class="apps-configured">
|
||||
<span>Configure</span>
|
||||
{{svg-jar "arrow-right"}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="gh-card-right">
|
||||
<div class="apps-configured">
|
||||
<span>Configure</span>
|
||||
{{svg-jar "arrow-right"}}
|
||||
</div>
|
||||
</article>
|
||||
{{/link-to}}
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="flex flex-column justify-center items-center mih40 miw-100" data-test-blank="custom-integrations">
|
||||
{{#if fetchIntegrations.isRunning}}
|
||||
<div class="gh-loading-spinner"></div>
|
||||
{{else}}
|
||||
<p class="ma0 pa0 tc midgrey">
|
||||
Use API keys and webhooks to create custom integrations.<br>
|
||||
No custom integrations.
|
||||
</p>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
</section>
|
||||
{{/if}}
|
||||
</div>
|
||||
</article>
|
||||
{{/link-to}}
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="flex flex-column justify-center items-center mih40 miw-100" data-test-blank="custom-integrations">
|
||||
{{#if fetchIntegrations.isRunning}}
|
||||
<div class="gh-loading-spinner"></div>
|
||||
{{else}}
|
||||
<p class="ma0 pa0 tc midgrey">
|
||||
Use API keys and webhooks to create custom integrations.<br>
|
||||
No custom integrations.
|
||||
</p>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
{{outlet}}
|
Loading…
Reference in New Issue
Block a user