';
+ let html = converters.markdownConverter.render(payload.markdown || '');
+
+ // Ghost 1.0's markdown-only renderer wrapped cards
+ if (version === 1) {
+ html = `
${html}
`;
+ }
// use the SimpleDOM document to create a raw HTML section.
// avoids parsing/rendering of potentially broken or unsupported HTML
- element = opts.env.dom.createRawHTMLSection(html);
-
- return element;
+ return opts.env.dom.createRawHTMLSection(html);
}
};
diff --git a/core/server/lib/mobiledoc/converters/mobiledoc-converter.js b/core/server/lib/mobiledoc/converters/mobiledoc-converter.js
index 50a36349dc4..7e0bdff98de 100644
--- a/core/server/lib/mobiledoc/converters/mobiledoc-converter.js
+++ b/core/server/lib/mobiledoc/converters/mobiledoc-converter.js
@@ -36,11 +36,38 @@ var SimpleDom = require('simple-dom'),
// }
module.exports = {
- render: function (mobiledoc) {
- var renderer = new Renderer(options),
- rendered = renderer.render(mobiledoc),
- serializer = new SimpleDom.HTMLSerializer(SimpleDom.voidMap),
- html = serializer.serializeChildren(rendered.result);
+ // version 1 === Ghost 1.0 markdown-only mobiledoc
+ // version 2 === Ghost 2.0 full mobiledoc
+ render: function (mobiledoc, version) {
+ version = version || 1;
+
+ // pass the version through to the card renderers.
+ // create a new object here to avoid modifying the default options
+ // object because the version can change per-render until 2.0 is released
+ let versionedOptions = Object.assign({}, options, {
+ cardOptions: {version}
+ });
+
+ let renderer = new Renderer(versionedOptions);
+ let rendered = renderer.render(mobiledoc);
+ let serializer = new SimpleDom.HTMLSerializer(SimpleDom.voidMap);
+
+ // Koenig keeps a blank paragraph at the end of a doc but we want to
+ // make sure it doesn't get rendered
+ let lastChild = rendered.result.lastChild;
+ if (lastChild && lastChild.tagName === 'P' && !lastChild.firstChild) {
+ rendered.result.removeChild(lastChild);
+ }
+
+ let html = serializer.serializeChildren(rendered.result);
+
+ // full version of Koenig wraps the content with a specific class to
+ // be targetted with our default stylesheet for vertical rhythm and
+ // card-specific styles
+ if (version === 2) {
+ html = `
\n${html}\n
`;
+ }
+
return html;
}
};
diff --git a/core/server/models/post.js b/core/server/models/post.js
index acd0d50e893..8c7b84aa76e 100644
--- a/core/server/models/post.js
+++ b/core/server/models/post.js
@@ -9,6 +9,7 @@ var _ = require('lodash'),
htmlToText = require('html-to-text'),
ghostBookshelf = require('./base'),
config = require('../config'),
+ labs = require('../services/labs'),
converters = require('../lib/mobiledoc/converters'),
urlService = require('../services/url'),
relations = require('./relations'),
@@ -185,7 +186,7 @@ Post = ghostBookshelf.Model.extend({
prevSlug = this.previous('slug'),
publishedAt = this.get('published_at'),
publishedAtHasChanged = this.hasDateChanged('published_at', {beforeWrite: true}),
- mobiledoc = this.get('mobiledoc'),
+ mobiledoc = JSON.parse(this.get('mobiledoc') || null),
generatedFields = ['html', 'plaintext'],
tagsToSave,
ops = [];
@@ -249,8 +250,37 @@ Post = ghostBookshelf.Model.extend({
}
});
+ // render mobiledoc to HTML. Switch render version if Koenig is enabled
+ // or has been edited with Koenig and is no longer compatible with the
+ // Ghost 1.0 markdown-only renderer
+ // TODO: re-render all content and remove the version toggle for Ghost 2.0
if (mobiledoc) {
- this.set('html', converters.mobiledocConverter.render(JSON.parse(mobiledoc)));
+ let version = 1;
+ let devExperimentsEnabled = config.get('enableDeveloperExperiments');
+ let koenigEnabled = labs.isSet('koenigEditor') === true;
+
+ let mobiledocIsCompatibleWithV1 = function mobiledocIsCompatibleWithV1(doc) {
+ if (doc
+ && doc.markups.length === 0
+ && doc.cards.length === 1
+ && doc.cards[0][0].match(/(?:card-)?markdown/)
+ && doc.sections.length === 1
+ && doc.sections[0].length === 2
+ && doc.sections[0][0] === 10
+ && doc.sections[0][1] === 0
+ ) {
+ return true;
+ }
+
+ return false;
+ };
+
+ if ((devExperimentsEnabled && koenigEnabled) || !mobiledocIsCompatibleWithV1(mobiledoc)) {
+ version = 2;
+ }
+
+ let html = converters.mobiledocConverter.render(mobiledoc, version);
+ this.set('html', html);
}
if (this.hasChanged('html') || !this.get('plaintext')) {
diff --git a/core/test/unit/lib/mobiledoc/atoms/soft-return_spec.js b/core/test/unit/lib/mobiledoc/atoms/soft-return_spec.js
index ff89b2d1ab0..055adb8b0e4 100644
--- a/core/test/unit/lib/mobiledoc/atoms/soft-return_spec.js
+++ b/core/test/unit/lib/mobiledoc/atoms/soft-return_spec.js
@@ -1,17 +1,18 @@
-var should = require('should'), // jshint ignore:line
- card = require('../../../../../server/lib/mobiledoc/atoms/soft-return'),
- SimpleDom = require('simple-dom'),
- opts;
+'use strict';
-describe('Soft return card', function () {
+const should = require('should'); // jshint ignore:line
+const atom = require('../../../../../server/lib/mobiledoc/atoms/soft-return');
+const SimpleDom = require('simple-dom');
+const serializer = new SimpleDom.HTMLSerializer(SimpleDom.voidMap);
+
+describe('Soft return atom', function () {
it('generates a `br` tag', function () {
- opts = {
+ let opts = {
env: {
dom: new SimpleDom.Document()
}
};
- var serializer = new SimpleDom.HTMLSerializer([]);
- serializer.serialize(card.render(opts)).should.match(' ');
+ serializer.serialize(atom.render(opts)).should.match(' ');
});
});
diff --git a/core/test/unit/lib/mobiledoc/cards/hr_spec.js b/core/test/unit/lib/mobiledoc/cards/hr_spec.js
index f52a9e696bd..6814b35d823 100644
--- a/core/test/unit/lib/mobiledoc/cards/hr_spec.js
+++ b/core/test/unit/lib/mobiledoc/cards/hr_spec.js
@@ -1,17 +1,18 @@
-var should = require('should'), // jshint ignore:line
- card = require('../../../../../server/lib/mobiledoc/cards/hr'),
- SimpleDom = require('simple-dom'),
- opts;
+'use strict';
+
+const should = require('should'); // jshint ignore:line
+const card = require('../../../../../server/lib/mobiledoc/cards/hr');
+const SimpleDom = require('simple-dom');
+const serializer = new SimpleDom.HTMLSerializer(SimpleDom.voidMap);
describe('HR card', function () {
it('generates a horizontal rule', function () {
- opts = {
+ let opts = {
env: {
dom: new SimpleDom.Document()
}
};
- var serializer = new SimpleDom.HTMLSerializer([]);
- serializer.serialize(card.render(opts)).should.match('');
+ serializer.serialize(card.render(opts)).should.match('');
});
});
diff --git a/core/test/unit/lib/mobiledoc/cards/html_spec.js b/core/test/unit/lib/mobiledoc/cards/html_spec.js
index fc69be6245e..39c130f8209 100644
--- a/core/test/unit/lib/mobiledoc/cards/html_spec.js
+++ b/core/test/unit/lib/mobiledoc/cards/html_spec.js
@@ -1,11 +1,13 @@
-var should = require('should'), // jshint ignore:line
- card = require('../../../../../server/lib/mobiledoc/cards/html'),
- SimpleDom = require('simple-dom'),
- opts;
+'use strict';
+
+const should = require('should'); // jshint ignore:line
+const card = require('../../../../../server/lib/mobiledoc/cards/html');
+const SimpleDom = require('simple-dom');
+const serializer = new SimpleDom.HTMLSerializer(SimpleDom.voidMap);
describe('HTML card', function () {
it('HTML Card renders', function () {
- opts = {
+ let opts = {
env: {
dom: new SimpleDom.Document()
},
@@ -14,12 +16,11 @@ describe('HTML card', function () {
}
};
- var serializer = new SimpleDom.HTMLSerializer([]);
- serializer.serialize(card.render(opts)).should.match('