From c6753a0efdfa0e1039e902c7a65f8be5fa65c281 Mon Sep 17 00:00:00 2001 From: Kevin Ansfield Date: Tue, 27 Oct 2020 14:42:36 +0000 Subject: [PATCH] Hid snippet management UI from staff users without permissions no issue - snippets can only be created and deleted by owners/admins/editors - added a property in the editor controller to determine if the logged in user has sufficient permissions, then only pass the appropriate save/delete snippet actions to the editor component if the check is passed - updates koenig menus and toolbars to skip rendering of buttons if the associated action function is not available --- ghost/admin/app/controllers/editor.js | 72 ++++++++++--------- ghost/admin/app/helpers/noop.js | 5 ++ ghost/admin/app/templates/editor.hbs | 8 +-- .../addon/components/koenig-editor.hbs | 4 +- .../addon/components/koenig-editor.js | 20 ++++-- .../addon/components/koenig-plus-menu.js | 13 ++-- .../addon/components/koenig-slash-menu.js | 13 ++-- .../addon/components/koenig-toolbar.hbs | 2 +- 8 files changed, 82 insertions(+), 55 deletions(-) create mode 100644 ghost/admin/app/helpers/noop.js diff --git a/ghost/admin/app/controllers/editor.js b/ghost/admin/app/controllers/editor.js index bc5c1d5eb6..587de4fbd0 100644 --- a/ghost/admin/app/controllers/editor.js +++ b/ghost/admin/app/controllers/editor.js @@ -4,8 +4,8 @@ import boundOneWay from 'ghost-admin/utils/bound-one-way'; import config from 'ghost-admin/config/environment'; import isNumber from 'ghost-admin/utils/isNumber'; import moment from 'moment'; +import {action, computed} from '@ember/object'; import {alias, mapBy} from '@ember/object/computed'; -import {computed} from '@ember/object'; import {inject as controller} from '@ember/controller'; import {get} from '@ember/object'; import {htmlSafe} from '@ember/string'; @@ -147,6 +147,14 @@ export default Controller.extend({ return this._snippets.reject(snippet => snippet.get('isNew')); }), + canManageSnippets: computed('session.user.{isOwnerOrAdmin,isEditor}', function () { + let {user} = this.session; + if (user.get('isOwnerOrAdmin') || user.get('isEditor')) { + return true; + } + return false; + }), + _autosaveRunning: computed('_autosave.isRunning', '_timedSave.isRunning', function () { let autosave = this.get('_autosave.isRunning'); let timedsave = this.get('_timedSave.isRunning'); @@ -290,38 +298,38 @@ export default Controller.extend({ updateWordCount(counts) { this.set('wordCount', counts); - }, - - saveSnippet(snippet) { - let snippetRecord = this.store.createRecord('snippet', snippet); - return snippetRecord.save().then(() => { - this.notifications.closeAlerts('snippet.save'); - this.notifications.showNotification( - `Snippet saved as "${snippet.name}"`, - {type: 'success'} - ); - return snippetRecord; - }).catch((error) => { - if (!snippetRecord.errors.isEmpty) { - this.notifications.showAlert( - `Snippet save failed: ${snippetRecord.errors.messages.join('. ')}`, - {type: 'error', key: 'snippet.save'} - ); - } - snippetRecord.rollbackAttributes(); - throw error; - }); - }, - - toggleDeleteSnippetModal(snippet) { - this.set('snippetToDelete', snippet); - }, - - deleteSnippet(snippet) { - return snippet.destroyRecord(); } }, + saveSnippet: action(function (snippet) { + let snippetRecord = this.store.createRecord('snippet', snippet); + return snippetRecord.save().then(() => { + this.notifications.closeAlerts('snippet.save'); + this.notifications.showNotification( + `Snippet saved as "${snippet.name}"`, + {type: 'success'} + ); + return snippetRecord; + }).catch((error) => { + if (!snippetRecord.errors.isEmpty) { + this.notifications.showAlert( + `Snippet save failed: ${snippetRecord.errors.messages.join('. ')}`, + {type: 'error', key: 'snippet.save'} + ); + } + snippetRecord.rollbackAttributes(); + throw error; + }); + }), + + toggleDeleteSnippetModal: action(function (snippet) { + this.set('snippetToDelete', snippet); + }), + + deleteSnippet: action(function (snippet) { + return snippet.destroyRecord(); + }), + /* Public tasks ----------------------------------------------------------*/ // separate task for autosave so that it doesn't override a manual save @@ -613,11 +621,11 @@ export default Controller.extend({ let membersResponse = yield this.store.query('member', {limit: 1, filter: 'subscribed:true'}); this.set('memberCount', get(membersResponse, 'meta.pagination.total')); } - - yield this.store.query('snippet', {limit: 'all'}); } catch (error) { this.set('memberCount', 0); } + + yield this.store.query('snippet', {limit: 'all'}); }).restartable(), /* Public methods --------------------------------------------------------*/ diff --git a/ghost/admin/app/helpers/noop.js b/ghost/admin/app/helpers/noop.js new file mode 100644 index 0000000000..cfe0197930 --- /dev/null +++ b/ghost/admin/app/helpers/noop.js @@ -0,0 +1,5 @@ +import {helper} from '@ember/component/helper'; + +export default helper(function noop() { + return () => {}; +}); diff --git a/ghost/admin/app/templates/editor.hbs b/ghost/admin/app/templates/editor.hbs index 361e048d18..ceb7d5f9da 100644 --- a/ghost/admin/app/templates/editor.hbs +++ b/ghost/admin/app/templates/editor.hbs @@ -82,8 +82,8 @@ @onEditorCreated={{action "setKoenigEditor"}} @onWordCountChange={{action "updateWordCount"}} @snippets={{this.snippets}} - @saveSnippet={{action "saveSnippet"}} - @deleteSnippet={{action "toggleDeleteSnippetModal"}} + @saveSnippet={{if this.canManageSnippets this.saveSnippet}} + @deleteSnippet={{if this.canManageSnippets this.toggleDeleteSnippetModal}} />
@@ -139,8 +139,8 @@ {{/if}} diff --git a/ghost/admin/lib/koenig-editor/addon/components/koenig-editor.hbs b/ghost/admin/lib/koenig-editor/addon/components/koenig-editor.hbs index 63c5b06e67..7b56a88e98 100644 --- a/ghost/admin/lib/koenig-editor/addon/components/koenig-editor.hbs +++ b/ghost/admin/lib/koenig-editor/addon/components/koenig-editor.hbs @@ -12,7 +12,7 @@ @toggleSection={{action "toggleSection"}} @toggleHeaderSection={{action "toggleHeaderSection"}} @editLink={{action "editLink"}} - @addSnippet={{this.addSnippet}} + @addSnippet={{this.addSnippetIfPossible}} /> {{!-- pop-up link hover toolbar --}} @@ -86,7 +86,7 @@ deselectCard=(action "deselectCard" card) editCard=(action "editCard" card) deleteCard=(action "deleteCard" card) - saveAsSnippet=(action "saveCardAsSnippet" card) + saveAsSnippet=this.saveCardAsSnippetIfPossible moveCursorToPrevSection=(action "moveCursorToPrevSection" card) moveCursorToNextSection=(action "moveCursorToNextSection" card) addParagraphAfterCard=(action "addParagraphAfterCard" card) diff --git a/ghost/admin/lib/koenig-editor/addon/components/koenig-editor.js b/ghost/admin/lib/koenig-editor/addon/components/koenig-editor.js index dcba170095..5f07bcd646 100644 --- a/ghost/admin/lib/koenig-editor/addon/components/koenig-editor.js +++ b/ghost/admin/lib/koenig-editor/addon/components/koenig-editor.js @@ -233,6 +233,14 @@ export default Component.extend({ }, options); }), + addSnippetIfPossible: computed('saveSnippet', function () { + return this.saveSnippet ? this.addSnippet : undefined; + }), + + saveCardAsSnippetIfPossible: computed('saveSnippet', function () { + return this.saveSnippet ? this.saveCardAsSnippet : undefined; + }), + /* lifecycle hooks ------------------------------------------------------ */ init() { @@ -618,12 +626,6 @@ export default Component.extend({ this.deleteCard(card, cursorMovement); }, - saveCardAsSnippet(card) { - let section = this.getSectionFromCard(card); - this.set('snippetRect', card.component.element.getBoundingClientRect()); - this.set('snippetRange', section.toRange()); - }, - moveCursorToPrevSection(card) { let section = this.getSectionFromCard(card); @@ -681,6 +683,12 @@ export default Component.extend({ this.set('snippetRange', selectedRange); }), + saveCardAsSnippet: action(function (card) { + let section = this.getSectionFromCard(card); + this.set('snippetRect', card.component.element.getBoundingClientRect()); + this.set('snippetRange', section.toRange()); + }), + cancelAddSnippet: action(function () { this.set('snippetRange', null); this.set('snippetRect', null); diff --git a/ghost/admin/lib/koenig-editor/addon/components/koenig-plus-menu.js b/ghost/admin/lib/koenig-editor/addon/components/koenig-plus-menu.js index c8f62e3157..35ead80bd5 100644 --- a/ghost/admin/lib/koenig-editor/addon/components/koenig-plus-menu.js +++ b/ghost/admin/lib/koenig-editor/addon/components/koenig-plus-menu.js @@ -49,17 +49,20 @@ export default Component.extend({ }; snippets.forEach((snippet) => { - snippetsSection.items.push({ + let snippetItem = { label: snippet.name, icon: snippetIcon(snippet), type: 'snippet', - matches: [snippet.name.toLowerCase()], - deleteClicked: (event) => { + matches: [snippet.name.toLowerCase()] + }; + if (this.deleteSnippet) { + snippetItem.deleteClicked = (event) => { event.preventDefault(); event.stopImmediatePropagation(); this.deleteSnippet(snippet); - } - }); + }; + } + snippetsSection.items.push(snippetItem); }); itemSections.push(snippetsSection); diff --git a/ghost/admin/lib/koenig-editor/addon/components/koenig-slash-menu.js b/ghost/admin/lib/koenig-editor/addon/components/koenig-slash-menu.js index 9bc471a631..37b9ae1cd8 100644 --- a/ghost/admin/lib/koenig-editor/addon/components/koenig-slash-menu.js +++ b/ghost/admin/lib/koenig-editor/addon/components/koenig-slash-menu.js @@ -68,17 +68,20 @@ export default class KoenigSlashMenuComponent extends Component { }; snippets.forEach((snippet) => { - snippetsSection.items.push({ + let snippetItem = { label: snippet.name, icon: snippetIcon(snippet), type: 'snippet', - matches: [snippet.name.toLowerCase()], - deleteClicked: (event) => { + matches: [snippet.name.toLowerCase()] + }; + if (this.args.deleteSnippet) { + snippetItem.deleteClicked = (event) => { event.preventDefault(); event.stopImmediatePropagation(); this.args.deleteSnippet(snippet); - } - }); + }; + } + snippetsSection.items.push(snippetItem); }); itemSections.push(snippetsSection); diff --git a/ghost/admin/lib/koenig-editor/addon/components/koenig-toolbar.hbs b/ghost/admin/lib/koenig-editor/addon/components/koenig-toolbar.hbs index bd3e62ac17..0df2494b0e 100644 --- a/ghost/admin/lib/koenig-editor/addon/components/koenig-toolbar.hbs +++ b/ghost/admin/lib/koenig-editor/addon/components/koenig-toolbar.hbs @@ -67,7 +67,7 @@ - {{#if (enable-developer-experiments)}} + {{#if (and @addSnippet (enable-developer-experiments))}} {{#unless this.basicOnly}}