0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-02-24 23:48:13 -05:00

Made Koenig the default editor, removed Koenig labs flag

This commit is contained in:
Kevin Ansfield 2018-08-08 17:31:38 +01:00
parent 7ee7c3a9f8
commit 3d0091c9f5
8 changed files with 40 additions and 270 deletions

View file

@ -3,7 +3,6 @@ import PostModel from 'ghost-admin/models/post';
import boundOneWay from 'ghost-admin/utils/bound-one-way';
import config from 'ghost-admin/config/environment';
import isNumber from 'ghost-admin/utils/isNumber';
import {BLANK_MARKDOWN} from 'ghost-admin/models/post';
import {alias, mapBy, reads} from '@ember/object/computed';
import {computed} from '@ember/object';
import {inject as controller} from '@ember/controller';
@ -100,7 +99,6 @@ export default Controller.extend({
showDeletePostModal: false,
showLeaveEditorModal: false,
showReAuthenticateModal: false,
useKoenig: false,
// koenig related properties
wordcount: null,
@ -131,14 +129,6 @@ export default Controller.extend({
_tagNames: mapBy('post.tags', 'name'),
markdown: computed('post.mobiledoc', function () {
if (this.get('post').isCompatibleWithMarkdownEditor()) {
let mobiledoc = this.get('post.mobiledoc');
let markdown = mobiledoc.cards[0][1].markdown;
return markdown;
}
}),
hasDirtyAttributes: computed(...watchedProps, {
get() {
return this._hasDirtyAttributes();
@ -170,15 +160,6 @@ export default Controller.extend({
// force save at 60 seconds
this.get('_timedSave').perform();
},
// TODO: Only used by the markdown editor, ensure it's removed when
// we switch to Koenig
updateMarkdown(markdown) {
let mobiledoc = copy(BLANK_MARKDOWN, true);
mobiledoc.cards[0][1].markdown = markdown;
this.send('updateScratch', mobiledoc);
},
updateTitleScratch(title) {
this.set('post.titleScratch', title);
},
@ -526,16 +507,6 @@ export default Controller.extend({
// called by the new/edit routes to change the post model
setPost(post) {
// switch between markdown/koenig depending on feature flag and post
// compatibility
let koenigEnabled = this.get('feature.koenigEditor');
let postIsMarkdownCompatible = post.isCompatibleWithMarkdownEditor();
if (koenigEnabled || !postIsMarkdownCompatible) {
this.set('useKoenig', true);
} else {
this.set('useKoenig', false);
}
// don't do anything else if we're setting the same post
if (post === this.get('post')) {
// set autofocus as change signal to the persistent editor on new->edit
@ -548,12 +519,6 @@ export default Controller.extend({
this.set('post', post);
// display an info message if Koenig is disabled by we had to use it
// for post compatibility
if (!koenigEnabled && this.useKoenig) {
// this.set('infoMessage', 'This post can only be edited with the Koenig editor.');
}
// autofocus the editor if we have a new post
this.set('shouldFocusEditor', post.get('isNew'));

View file

@ -4,11 +4,9 @@ import ValidationEngine from 'ghost-admin/mixins/validation-engine';
import attr from 'ember-data/attr';
import boundOneWay from 'ghost-admin/utils/bound-one-way';
import moment from 'moment';
import {BLANK_DOC as BLANK_MOBILEDOC} from 'koenig-editor/components/koenig-editor';
import {belongsTo, hasMany} from 'ember-data/relationships';
import {compare} from '@ember/utils';
import {computed, observer} from '@ember/object';
import {copy} from '@ember/object/internals';
import {equal, filterBy} from '@ember/object/computed';
import {isBlank} from '@ember/utils';
import {inject as service} from '@ember/service';
@ -16,21 +14,6 @@ import {inject as service} from '@ember/service';
// ember-cli-shims doesn't export these so we must get them manually
const {Comparable} = Ember;
// for our markdown-only editor we need to build a blank mobiledoc
const MOBILEDOC_VERSION = '0.3.1';
export const BLANK_MARKDOWN = {
version: MOBILEDOC_VERSION,
markups: [],
atoms: [],
cards: [
['card-markdown', {
cardName: 'card-markdown',
markdown: ''
}]
],
sections: [[10, 0]]
};
function statusCompare(postA, postB) {
let status1 = postA.get('status');
let status2 = postB.get('status');
@ -135,30 +118,6 @@ export default Model.extend(Comparable, ValidationEngine, {
return this.get('authors.firstObject');
}),
init() {
// HACK: we can't use the defaultValue property on the mobiledoc attr
// because it won't have access to `this` for the feature check so we do
// it manually here instead
if (!this.get('mobiledoc')) {
let defaultValue;
if (this.get('feature.koenigEditor')) {
defaultValue = copy(BLANK_MOBILEDOC, true);
} else {
defaultValue = copy(BLANK_MARKDOWN, true);
}
// using this.set() adds the property to the changedAttributes list
// which means the editor always sees new posts as dirty. By setting
// the internal model data property first it's not seen as having
// changed so the changedAttributes key is removed
this._internalModel._data.mobiledoc = defaultValue;
this.set('mobiledoc', defaultValue);
}
this._super(...arguments);
},
scratch: null,
titleScratch: null,
@ -360,25 +319,5 @@ export default Model.extend(Comparable, ValidationEngine, {
let publishedAtBlogTZ = this.get('publishedAtBlogTZ');
let publishedAtUTC = publishedAtBlogTZ ? publishedAtBlogTZ.utc() : null;
this.set('publishedAtUTC', publishedAtUTC);
},
// the markdown editor expects a very specific mobiledoc format, if it
// doesn't match then we'll need to handle it by forcing Koenig
isCompatibleWithMarkdownEditor() {
let mobiledoc = this.get('mobiledoc');
if (mobiledoc
&& mobiledoc.markups.length === 0
&& mobiledoc.cards.length === 1
&& mobiledoc.cards[0][0] === 'card-markdown'
&& mobiledoc.sections.length === 1
&& mobiledoc.sections[0].length === 2
&& mobiledoc.sections[0][0] === 10
&& mobiledoc.sections[0][1] === 0
) {
return true;
}
return false;
}
});

View file

@ -21,39 +21,26 @@ export default AuthenticatedRoute.extend(ShortcutsRoute, {
activate() {
this._super(...arguments);
if (this.feature.koenigEditor) {
this.ui.set('isFullScreen', true);
}
this.ui.set('isFullScreen', true);
},
setupController() {
this._super(...arguments);
// display a warning if we detect an unsupported browser
if (this.feature.koenigEditor) {
// IE is definitely not supported and will not work at all in Ghost 2.0
if (this.userAgent.browser.isIE) {
this.notifications.showAlert(
htmlSafe('Internet Explorer is not supported in Koenig and will no longer work in Ghost 2.0. Please switch to <a href="https://ghost.org/downloads/" target="_blank" rel="noopener">Ghost Desktop</a> or a recent version of Chrome/Firefox/Safari.'),
{type: 'info', key: 'koenig.browserSupport'}
);
}
// edge has known issues
if (this.userAgent.browser.isEdge) {
this.notifications.showAlert(
htmlSafe('Microsoft Edge is not currently supported in Koenig. Please switch to <a href="https://ghost.org/downloads/" target="_blank" rel="noopener">Ghost Desktop</a> or a recent version of Chrome/Firefox/Safari.'),
{type: 'info', key: 'koenig.browserSupport'}
);
}
// edge has known issues
if (this.userAgent.browser.isEdge) {
this.notifications.showAlert(
htmlSafe('Microsoft Edge is not currently supported in Koenig. Please switch to <a href="https://ghost.org/downloads/" target="_blank" rel="noopener">Ghost Desktop</a> or a recent version of Chrome/Firefox/Safari.'),
{type: 'info', key: 'koenig.browserSupport'}
);
}
// mobile browsers are not currently supported
if (this.userAgent.device.isMobile || this.userAgent.device.isTablet) {
this.notifications.showAlert(
htmlSafe('Mobile editing is not currently supported in Koenig. Please use a desktop browser or <a href="https://ghost.org/downloads/" target="_blank" rel="noopener">Ghost Desktop</a>.'),
{type: 'info', key: 'koenig.browserSupport'}
);
}
// mobile browsers are not currently supported
if (this.userAgent.device.isMobile || this.userAgent.device.isTablet) {
this.notifications.showAlert(
htmlSafe('Mobile editing is not currently supported in Koenig. Please use a desktop browser or <a href="https://ghost.org/downloads/" target="_blank" rel="noopener">Ghost Desktop</a>.'),
{type: 'info', key: 'koenig.browserSupport'}
);
}
},

View file

@ -50,7 +50,6 @@ export default Service.extend({
notifications: service(),
lazyLoader: service(),
koenigEditor: feature('koenigEditor'),
publicAPI: feature('publicAPI'),
subscribers: feature('subscribers'),
nightShift: feature('nightShift', {user: true, onChange: '_setAdminTheme'}),

View file

@ -14,6 +14,7 @@
focus-out=(action "onTitleFocusOut")
keyDown=(action "onTitleKeydown")
didCreateTextarea=(action "onTitleCreated")
data-test-editor-title-input=true
}}
{{koenig-editor

View file

@ -66,134 +66,33 @@
</section>
</header>
{{#if useKoenig}}
{{!--
gh-koenig-editor acts as a wrapper around the title input and
koenig editor canvas to support Ghost-specific editor behaviour
--}}
{{gh-koenig-editor
title=(readonly post.titleScratch)
titlePlaceholder="Story Title"
onTitleChange=(action "updateTitleScratch")
onTitleBlur=(action (perform saveTitle))
body=(readonly post.scratch)
bodyPlaceholder="Begin writing your story..."
bodyAutofocus=shouldFocusEditor
onBodyChange=(action "updateScratch")
headerOffset=editor.headerHeight
scrollContainerSelector=".gh-koenig-editor"
scrollOffsetTopSelector=".gh-editor-header-small"
scrollOffsetBottomSelector=".gh-mobile-nav-bar"
onEditorCreated=(action "setKoenigEditor")
onWordCountChange=(action "updateWordCount")
}}
{{!--
gh-koenig-editor acts as a wrapper around the title input and
koenig editor canvas to support Ghost-specific editor behaviour
--}}
{{gh-koenig-editor
title=(readonly post.titleScratch)
titlePlaceholder="Story Title"
onTitleChange=(action "updateTitleScratch")
onTitleBlur=(action (perform saveTitle))
body=(readonly post.scratch)
bodyPlaceholder="Begin writing your story..."
bodyAutofocus=shouldFocusEditor
onBodyChange=(action "updateScratch")
headerOffset=editor.headerHeight
scrollContainerSelector=".gh-koenig-editor"
scrollOffsetTopSelector=".gh-editor-header-small"
scrollOffsetBottomSelector=".gh-mobile-nav-bar"
onEditorCreated=(action "setKoenigEditor")
onWordCountChange=(action "updateWordCount")
}}
<div class="absolute flex items-center br3 bg-white {{if editor.headerClass "right-4 bottom-4" "right-6 bottom-6"}}">
<div class="midgrey-l2 {{if editor.headerClass "f-supersmall pl2 pr2" "f8 pl4 pr3"}} fw3">
{{pluralize wordCount.wordCount "word"}}
</div>
<a href="https://help.ghost.org/article/29-ghost-editor-overview" class="flex {{if editor.headerClass "pa2" "pa3"}}" target="_blank">{{svg-jar "help" class="w4 h4 stroke-midgrey-l2"}}</a>
<div class="absolute flex items-center br3 bg-white {{if editor.headerClass "right-4 bottom-4" "right-6 bottom-6"}}">
<div class="midgrey-l2 {{if editor.headerClass "f-supersmall pl2 pr2" "f8 pl4 pr3"}} fw3">
{{pluralize wordCount.wordCount "word"}}
</div>
{{else}}
{{!--
NOTE: title is part of the markdown editor container so that it has
access to the markdown editor's "focus" action
--}}
{{#gh-markdown-editor
tabindex="2"
placeholder="Begin writing your story..."
autofocus=shouldFocusEditor
uploadedImageUrls=editor.uploadedImageUrls
markdown=(readonly markdown)
isFullScreen=editor.isFullScreen
onChange=(action "updateMarkdown")
onFullScreenToggle=(action editor.toggleFullScreen)
onPreviewToggle=(action editor.togglePreview)
onSplitScreenToggle=(action editor.toggleSplitScreen)
onImageFilesSelected=(action editor.uploadImages)
imageMimeTypes=editor.imageMimeTypes
as |markdown|
}}
<div class="gh-markdown-editor-pane">
{{gh-textarea
class="gh-editor-title"
placeholder="Post Title"
tabindex="1"
autoExpand=".gh-markdown-editor-pane"
value=(readonly post.titleScratch)
input=(action "updateTitleScratch" value="target.value")
focus-out=(action (perform saveTitle))
keyEvents=(hash
Tab=(action markdown.focus 'bottom')
Enter=(action markdown.focus 'top')
)
data-test-editor-title-input=true
}}
{{markdown.editor}}
</div>
{{#if markdown.isSplitScreen}}
<div class="gh-markdown-editor-preview">
<h1 class="gh-markdown-editor-preview-title">{{post.titleScratch}}</h1>
<div class="gh-markdown-editor-preview-content"></div>
</div>
{{/if}}
{{gh-tour-item "using-the-editor"
target=".gh-editor-footer"
throbberAttachment="top left"
throbberOffset="0 20%"
popoverTriangleClass="bottom"
}}
{{/gh-markdown-editor}}
{{!-- TODO: put tool/status bar in here so that scroll area can be fixed --}}
<footer class="gh-editor-footer"></footer>
{{!-- files are dragged over editor pane --}}
{{#if editor.isDraggedOver}}
<div class="drop-target gh-editor-drop-target">
<div class="drop-target-message">
<h3>Drop image(s) here to upload</h3>
</div>
</div>
{{/if}}
{{!-- files have been dropped ready to be uploaded --}}
{{#if editor.droppedFiles}}
{{#gh-uploader
files=editor.droppedFiles
accept=editor.imageMimeTypes
extensions=editor.imageExtensions
onComplete=(action editor.uploadComplete)
onCancel=(action editor.uploadCancelled)
as |upload|
}}
<div class="gh-editor-image-upload {{if upload.errors "-error"}}">
<div class="gh-editor-image-upload-content">
{{#if upload.errors}}
<h3>Upload failed</h3>
{{#each upload.errors as |error|}}
<div class="failed">{{error.fileName}} - {{error.message}}</div>
{{/each}}
<button class="gh-btn gh-btn-grey gh-btn-icon" {{action upload.cancel}}>
<span>{{svg-jar "close"}} Close</span>
</button>
{{else}}
<h3>Uploading {{pluralize upload.files.length "image"}}...</h3>
{{upload.progressBar}}
{{/if}}
</div>
</div>
{{/gh-uploader}}
{{/if}}
{{/if}} {{!-- end Koenig conditional --}}
<a href="https://help.ghost.org/article/29-ghost-editor-overview" class="flex {{if editor.headerClass "pa2" "pa3"}}" target="_blank">{{svg-jar "help" class="w4 h4 stroke-midgrey-l2"}}</a>
</div>
{{/gh-editor}}

View file

@ -82,15 +82,6 @@
</div>
<div class="gh-setting-header">Beta features</div>
<div class="gh-setting">
<div class="gh-setting-content">
<div class="gh-setting-title flex items-center">Koenig editor <span class="gh-badge gh-badge-green ml1">New</span></div>
<div class="gh-setting-desc">Participate in our new rich text editor beta! Wed love <a href="https://forum.ghost.org/t/koenig-beta-release/1284" target="_blank" rel="noopener">your feedback</a></div>
</div>
<div class="gh-setting-action">
<div class="for-checkbox">{{gh-feature-flag "koenigEditor"}}</div>
</div>
</div>
<div class="gh-setting">
<div class="gh-setting-content">
<div class="gh-setting-title">Public API</div>

View file

@ -817,16 +817,5 @@ describe('Acceptance: Editor', function () {
'facebook title not present after closing subview'
).to.equal(0);
});
it('has unsplash icon when server doesn\'t return unsplash settings key', async function () {
server.createList('post', 1, {authors: [author]});
await visit('/editor/1');
expect(
find('.editor-toolbar .fa-camera'),
'unsplash toolbar button'
).to.exist;
});
});
});