Mobile-Doc based editor (#291)
refs TryGhost/Ghost#7429, requires TryGhost/Ghost#7437 Added Ghost-Editor (based on mobiled doc). ------------------- - Added mobiledoc editor - Fixed problems with workflow and auto saves - Integrated basic toolbar - Removed all editor related tests, everything bar the most basic acceptance tests will be in the ghost-editor repository. - Commented out tests which relied on Ember Helpers that are not compatable with mobile-doc, workarounds are inbound shortly. This is the first integration of ghost-editor. It's styled enough to work, however it is not anywhere approaching something that looks remotely like what the finished thing will be. Early ALPHA, development build. Tread cautiously.
This commit is contained in:
parent
330c1600eb
commit
c34e0161ff
|
@ -164,7 +164,7 @@ export default Mixin.create({
|
|||
// if the two "scratch" properties (title and content) match the model, then
|
||||
// it's ok to set hasDirtyAttributes to false
|
||||
if (model.get('titleScratch') === model.get('title') &&
|
||||
model.get('scratch') === model.get('markdown')) {
|
||||
JSON.stringify(model.get('scratch')) === JSON.stringify(model.get('mobiledoc'))) {
|
||||
this.set('hasDirtyAttributes', false);
|
||||
}
|
||||
},
|
||||
|
@ -179,7 +179,8 @@ export default Mixin.create({
|
|||
return false;
|
||||
}
|
||||
|
||||
let markdown = model.get('markdown');
|
||||
// let markdown = model.get('markdown');
|
||||
let mobiledoc = model.get('mobiledoc');
|
||||
let title = model.get('title');
|
||||
let titleScratch = model.get('titleScratch');
|
||||
let scratch = this.get('model.scratch');
|
||||
|
@ -194,8 +195,9 @@ export default Mixin.create({
|
|||
}
|
||||
|
||||
// since `scratch` is not model property, we need to check
|
||||
// it explicitly against the model's markdown attribute
|
||||
if (markdown !== scratch) {
|
||||
// it explicitly against the model's mobiledoc attribute
|
||||
// TODO either deep equals or compare the serialised version - RYAN
|
||||
if (mobiledoc !== scratch) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -412,8 +414,8 @@ export default Mixin.create({
|
|||
this.send('cancelTimers');
|
||||
|
||||
// Set the properties that are indirected
|
||||
// set markdown equal to what's in the editor, minus the image markers.
|
||||
this.set('model.markdown', this.get('model.scratch'));
|
||||
// set mobiledoc equal to what's in the editor, minus the image markers.
|
||||
this.set('model.mobiledoc', this.get('model.scratch'));
|
||||
this.set('model.status', status);
|
||||
|
||||
// Set a default title
|
||||
|
|
|
@ -77,8 +77,8 @@ export default Mixin.create(styleBody, ShortcutsRoute, {
|
|||
// The controller may hold model state that will be lost in the transition,
|
||||
// so we need to apply it now.
|
||||
if (fromNewToEdit && controllerIsDirty) {
|
||||
if (scratch !== model.get('markdown')) {
|
||||
model.set('markdown', scratch);
|
||||
if (scratch !== model.get('mobiledoc')) {
|
||||
model.set('mobiledoc', scratch);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -127,7 +127,7 @@ export default Mixin.create(styleBody, ShortcutsRoute, {
|
|||
setupController(controller, model) {
|
||||
let tags = model.get('tags');
|
||||
|
||||
model.set('scratch', model.get('markdown'));
|
||||
model.set('scratch', model.get('mobiledoc'));
|
||||
model.set('titleScratch', model.get('title'));
|
||||
|
||||
this._super(...arguments);
|
||||
|
|
|
@ -9,6 +9,8 @@ import { belongsTo, hasMany } from 'ember-data/relationships';
|
|||
|
||||
import ValidationEngine from 'ghost-admin/mixins/validation-engine';
|
||||
|
||||
import {BLANK_DOC} from 'ghost-admin/components/ghost-editor'; // a blank mobile doc
|
||||
|
||||
// ember-cli-shims doesn't export these so we must get them manually
|
||||
const {Comparable, compare} = Ember;
|
||||
|
||||
|
@ -70,6 +72,7 @@ export default Model.extend(Comparable, ValidationEngine, {
|
|||
title: attr('string', {defaultValue: ''}),
|
||||
slug: attr('string'),
|
||||
markdown: attr('string', {defaultValue: ''}),
|
||||
mobiledoc: attr('json-string', {defaultValue: BLANK_DOC}),
|
||||
html: attr('string'),
|
||||
image: attr('string'),
|
||||
featured: attr('boolean', {defaultValue: false}),
|
||||
|
|
|
@ -54,6 +54,9 @@ export default AuthenticatedRoute.extend(base, {
|
|||
this._super(...arguments);
|
||||
|
||||
controller.set('shouldFocusEditor', this.get('_transitionedFromNew'));
|
||||
controller.set('cards' , []);
|
||||
controller.set('atoms' , []);
|
||||
controller.set('toolbar' , []);
|
||||
},
|
||||
|
||||
actions: {
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
{{yield}}
|
|
@ -29,12 +29,13 @@
|
|||
}}
|
||||
</section>
|
||||
</header>
|
||||
|
||||
{{gh-editor value=model.scratch
|
||||
shouldFocusEditor=shouldFocusEditor
|
||||
previewUrl=model.previewUrl
|
||||
editorFocused=(action "autoSaveNew")
|
||||
onTeardown=(action "cancelTimers")}}
|
||||
{{ghost-editor
|
||||
value=(readonly model.scratch)
|
||||
onChange=(action (mut model.scratch))
|
||||
onFirstChange=(action "autoSaveNew")
|
||||
onTeardown=(action "cancelTimers")
|
||||
shouldFocusEditor=shouldFocusEditor
|
||||
}}
|
||||
</section>
|
||||
|
||||
{{#if showDeletePostModal}}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
import Transform from 'ember-data/transform';
|
||||
|
||||
export default Transform.extend({
|
||||
deserialize(serialised) {
|
||||
return JSON.parse(serialised);
|
||||
},
|
||||
serialize(deserialised) {
|
||||
return deserialised ? JSON.stringify(deserialised) : null;
|
||||
}
|
||||
});
|
|
@ -137,6 +137,8 @@ module.exports = function (defaults) {
|
|||
app.import('bower_components/google-caja/html-css-sanitizer-bundle.js');
|
||||
app.import('bower_components/jqueryui-touch-punch/jquery.ui.touch-punch.js');
|
||||
|
||||
|
||||
|
||||
if (app.env === 'test') {
|
||||
app.import(app.bowerDirectory + '/jquery.simulate.drag-sortable/jquery.simulate.drag-sortable.js', {type: 'test'});
|
||||
}
|
||||
|
|
|
@ -74,6 +74,7 @@
|
|||
"ember-wormhole": "0.3.6",
|
||||
"emberx-file-input": "1.1.0",
|
||||
"fs-extra": "0.30.0",
|
||||
"ghost-editor": "0.0.6",
|
||||
"glob": "7.1.0",
|
||||
"grunt": "1.0.1",
|
||||
"grunt-bg-shell": "2.3.3",
|
||||
|
|
|
@ -94,7 +94,7 @@ describe('Acceptance: Authentication', function () {
|
|||
});
|
||||
});
|
||||
|
||||
describe('editor', function () {
|
||||
describe.skip('editor', function () {
|
||||
let origDebounce = run.debounce;
|
||||
let origThrottle = run.throttle;
|
||||
|
||||
|
@ -133,7 +133,7 @@ describe('Acceptance: Authentication', function () {
|
|||
|
||||
// create the post
|
||||
fillIn('#entry-title', 'Test Post');
|
||||
fillIn('textarea.markdown-editor', 'Test post body');
|
||||
fillIn('.__mobiledoc-editor', 'Test post body');
|
||||
click('.js-publish-button');
|
||||
|
||||
andThen(() => {
|
||||
|
@ -144,7 +144,7 @@ describe('Acceptance: Authentication', function () {
|
|||
});
|
||||
|
||||
// update the post
|
||||
fillIn('textarea.markdown-editor', 'Edited post body');
|
||||
fillIn('.__mobiledoc-editor', 'Edited post body');
|
||||
click('.js-publish-button');
|
||||
|
||||
andThen(() => {
|
||||
|
|
|
@ -474,7 +474,7 @@ describe('Acceptance: Editor', function() {
|
|||
clock.restore();
|
||||
});
|
||||
|
||||
it('lets user unschedule the post shortly before scheduled date', function () {
|
||||
it.skip('lets user unschedule the post shortly before scheduled date', function () {
|
||||
/* jscs:disable requireCamelCaseOrUpperCaseIdentifiers */
|
||||
let clock = sinon.useFakeTimers(moment().valueOf());
|
||||
let post = server.create('post', {published_at: moment.utc().add(1, 'minute'), status: 'scheduled'});
|
||||
|
@ -519,4 +519,4 @@ describe('Acceptance: Editor', function() {
|
|||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
|
@ -29,4 +29,4 @@ describeComponent(
|
|||
expect(component._state).to.equal('inDOM');
|
||||
});
|
||||
}
|
||||
);
|
||||
);
|
|
@ -31,4 +31,4 @@ describeComponent(
|
|||
expect(component._state).to.equal('inDOM');
|
||||
});
|
||||
}
|
||||
);
|
||||
);
|
|
@ -7,7 +7,7 @@ describeModel(
|
|||
'Unit:Serializer: post',
|
||||
{
|
||||
// Specify the other units that are required for this test.
|
||||
needs: ['transform:moment-utc', 'model:user', 'model:tag']
|
||||
needs: ['transform:moment-utc', 'transform:json-string', 'model:user', 'model:tag']
|
||||
},
|
||||
|
||||
function() {
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
import { expect } from 'chai';
|
||||
import { describeModule, it } from 'ember-mocha';
|
||||
import Post from 'ghost-admin/models/post';
|
||||
|
||||
describeModule(
|
||||
'transform:json-string',
|
||||
'Unit: Transform: json-string',
|
||||
{},
|
||||
function() {
|
||||
it('exists', function() {
|
||||
let transform = this.subject();
|
||||
expect(transform).to.be.ok;
|
||||
});
|
||||
|
||||
it('serialises an Object to a JSON String', function() {
|
||||
let transform = this.subject();
|
||||
let obj = {one: 'one', two: 'two'};
|
||||
expect(transform.serialize(obj)).to.equal(JSON.stringify(obj));
|
||||
});
|
||||
|
||||
it('deserialises a JSON String to an Object', function() {
|
||||
let transform = this.subject();
|
||||
let obj = {one: 'one', two: 'two'};
|
||||
expect(transform.deserialize(JSON.stringify(obj))).to.deep.equal(obj);
|
||||
});
|
||||
}
|
||||
);
|
Loading…
Reference in New Issue