mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-26 04:13:30 +03:00
Added feature to convert and open mobiledoc posts in the lexical editor (#17453)
refs TryGhost/Product#3638 - Added `convert_to_lexical` flag to the posts/pages edit endpoint - Added 'convertToLexical' feature flag so we can enable/disable this feature independently from the main lexical beta flag - Modified admin posts/pages list to point to the lexical editor for _all_ posts, regardless of mobiledoc vs lexical (if the flag is on) - Added call to edit endpoint with `convert_to_lexical` in the lexical editor admin route if the page/post is currently in mobiledoc and the flag is enabled
This commit is contained in:
parent
d8259fb4fe
commit
9ea4fbd7a7
@ -11,6 +11,11 @@ export default class Page extends ApplicationAdapter {
|
|||||||
parsedUrl.searchParams.append('save_revision', saveRevision);
|
parsedUrl.searchParams.append('save_revision', saveRevision);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (snapshot?.adapterOptions?.convertToLexical) {
|
||||||
|
const convertToLexical = snapshot.adapterOptions.convertToLexical;
|
||||||
|
parsedUrl.searchParams.append('convert_to_lexical', convertToLexical);
|
||||||
|
}
|
||||||
|
|
||||||
return parsedUrl.toString();
|
return parsedUrl.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,6 +24,12 @@ export default class Post extends ApplicationAdapter {
|
|||||||
const saveRevision = snapshot.adapterOptions.saveRevision;
|
const saveRevision = snapshot.adapterOptions.saveRevision;
|
||||||
parsedUrl.searchParams.append('save_revision', saveRevision);
|
parsedUrl.searchParams.append('save_revision', saveRevision);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (snapshot?.adapterOptions?.convertToLexical) {
|
||||||
|
const convertToLexical = snapshot.adapterOptions.convertToLexical;
|
||||||
|
parsedUrl.searchParams.append('convert_to_lexical', convertToLexical);
|
||||||
|
}
|
||||||
|
|
||||||
return parsedUrl.toString();
|
return parsedUrl.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,12 +45,23 @@
|
|||||||
{{/unless}}
|
{{/unless}}
|
||||||
</a>
|
</a>
|
||||||
{{else}}
|
{{else}}
|
||||||
<LinkTo @route="editor.edit" @models={{array this.post.displayName this.post.id}} class="permalink gh-list-data gh-post-list-title">
|
<LinkTo @route={{this.editorRoute}} @models={{array this.post.displayName this.post.id}} class="permalink gh-list-data gh-post-list-title">
|
||||||
<h3 class="gh-content-entry-title">
|
<h3 class="gh-content-entry-title">
|
||||||
{{#if @post.featured}}
|
{{#if @post.featured}}
|
||||||
{{svg-jar "star-fill" class="gh-featured-post"}}
|
{{svg-jar "star-fill" class="gh-featured-post"}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{@post.title}}
|
{{@post.title}}
|
||||||
|
|
||||||
|
{{! Display lexical/mobiledoc indicators for easier testing of the feature --}}
|
||||||
|
{{#if (feature 'convertToLexical')}}
|
||||||
|
{{#if @post.lexical}}
|
||||||
|
<span class="gh-lexical-indicator">L</span>
|
||||||
|
{{/if}}
|
||||||
|
{{#if @post.mobiledoc}}
|
||||||
|
<span class="gh-lexical-indicator">M</span>
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
</h3>
|
</h3>
|
||||||
{{#unless @hideAuthor }}
|
{{#unless @hideAuthor }}
|
||||||
<p class="gh-content-entry-meta">
|
<p class="gh-content-entry-meta">
|
||||||
@ -141,7 +152,7 @@
|
|||||||
</span>
|
</span>
|
||||||
</LinkTo>
|
</LinkTo>
|
||||||
{{else}}
|
{{else}}
|
||||||
<LinkTo @route="editor.edit" @models={{array this.post.displayName this.post.id}} class="permalink gh-list-data">
|
<LinkTo @route={{this.editorRoute}} @models={{array this.post.displayName this.post.id}} class="permalink gh-list-data">
|
||||||
{{!-- Empty on purpose --}}
|
{{!-- Empty on purpose --}}
|
||||||
</LinkTo>
|
</LinkTo>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
@ -175,7 +186,7 @@
|
|||||||
</span>
|
</span>
|
||||||
</LinkTo>
|
</LinkTo>
|
||||||
{{else}}
|
{{else}}
|
||||||
<LinkTo @route="editor.edit" @models={{array this.post.displayName this.post.id}} class="permalink gh-list-data">
|
<LinkTo @route={{this.editorRoute}} @models={{array this.post.displayName this.post.id}} class="permalink gh-list-data">
|
||||||
{{!-- Empty on purpose --}}
|
{{!-- Empty on purpose --}}
|
||||||
</LinkTo>
|
</LinkTo>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
@ -196,7 +207,7 @@
|
|||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
{{else}}
|
{{else}}
|
||||||
<LinkTo @route="editor.edit" @models={{array this.post.displayName this.post.id}} class="permalink gh-list-data gh-post-list-button" title="">
|
<LinkTo @route={{this.editorRoute}} @models={{array this.post.displayName this.post.id}} class="permalink gh-list-data gh-post-list-button" title="">
|
||||||
<span class="gh-post-list-cta edit {{if this.isHovered "is-hovered"}}" title="Go to Editor" data-ignore-select>
|
<span class="gh-post-list-cta edit {{if this.isHovered "is-hovered"}}" title="Go to Editor" data-ignore-select>
|
||||||
{{svg-jar "pen" title="Go to Editor"}}
|
{{svg-jar "pen" title="Go to Editor"}}
|
||||||
</span>
|
</span>
|
||||||
|
@ -11,6 +11,8 @@ export default class PostsListItemClicks extends Component {
|
|||||||
|
|
||||||
@tracked isHovered = false;
|
@tracked isHovered = false;
|
||||||
|
|
||||||
|
editorRoute = this.feature.get('convertToLexical') ? 'lexical-editor.edit' : 'editor.edit';
|
||||||
|
|
||||||
get post() {
|
get post() {
|
||||||
return this.args.post;
|
return this.args.post;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
import AuthenticatedRoute from 'ghost-admin/routes/authenticated';
|
import AuthenticatedRoute from 'ghost-admin/routes/authenticated';
|
||||||
import {pluralize} from 'ember-inflector';
|
import {pluralize} from 'ember-inflector';
|
||||||
|
import {inject as service} from '@ember/service';
|
||||||
export default class EditRoute extends AuthenticatedRoute {
|
export default class EditRoute extends AuthenticatedRoute {
|
||||||
|
@service feature;
|
||||||
|
|
||||||
beforeModel(transition) {
|
beforeModel(transition) {
|
||||||
super.beforeModel(...arguments);
|
super.beforeModel(...arguments);
|
||||||
|
|
||||||
@ -29,10 +31,15 @@ export default class EditRoute extends AuthenticatedRoute {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const records = await this.store.query(modelName, query);
|
const records = await this.store.query(modelName, query);
|
||||||
const post = records.firstObject;
|
let post = records.firstObject;
|
||||||
|
|
||||||
|
// CASE: Post is in mobiledoc — convert to lexical or redirect
|
||||||
if (post.mobiledoc) {
|
if (post.mobiledoc) {
|
||||||
return this.router.transitionTo('editor.edit', post);
|
if (this.feature.get('convertToLexical') && this.feature.get('lexicalEditor')) {
|
||||||
|
post = await post.save({adapterOptions: {convertToLexical: 1}});
|
||||||
|
} else {
|
||||||
|
return this.replaceWith('editor.edit', post);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return post;
|
return post;
|
||||||
|
@ -78,6 +78,7 @@ export default class FeatureService extends Service {
|
|||||||
@feature('headerUpgrade') headerUpgrade;
|
@feature('headerUpgrade') headerUpgrade;
|
||||||
@feature('importMemberTier') importMemberTier;
|
@feature('importMemberTier') importMemberTier;
|
||||||
@feature('tipsAndDonations') tipsAndDonations;
|
@feature('tipsAndDonations') tipsAndDonations;
|
||||||
|
@feature('convertToLexical') convertToLexical;
|
||||||
|
|
||||||
_user = null;
|
_user = null;
|
||||||
|
|
||||||
|
@ -100,7 +100,9 @@
|
|||||||
<div class="gh-editor-wordcount">
|
<div class="gh-editor-wordcount">
|
||||||
{{gh-pluralize this.wordCount "word"}}
|
{{gh-pluralize this.wordCount "word"}}
|
||||||
</div>
|
</div>
|
||||||
{{!-- <a href="https://github.com/TryGhost/Koenig/tree/main/packages/koenig-lexical" target="_blank" rel="noopener noreferrer" class="gh-lexical-indicator">Lexical</a> --}}
|
{{#if (feature 'convertToLexical')}}
|
||||||
|
<a href="https://github.com/TryGhost/Koenig/tree/main/packages/koenig-lexical" target="_blank" rel="noopener noreferrer" class="gh-lexical-indicator">Lexical</a>
|
||||||
|
{{/if}}
|
||||||
<a href="https://ghost.org/help/using-the-editor/" class="flex" target="_blank" rel="noopener noreferrer">{{svg-jar "help"}}</a>
|
<a href="https://ghost.org/help/using-the-editor/" class="flex" target="_blank" rel="noopener noreferrer">{{svg-jar "help"}}</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -339,6 +339,20 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="gh-expandable-block">
|
||||||
|
<div class="gh-expandable-header">
|
||||||
|
<div>
|
||||||
|
<h4 class="gh-expandable-title">Convert to Lexical</h4>
|
||||||
|
<p class="gh-expandable-description">
|
||||||
|
Convert mobiledoc posts to lexical upon opening in the editor.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="for-switch">
|
||||||
|
<GhFeatureFlag @flag="convertToLexical" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="gh-expandable-block">
|
<div class="gh-expandable-block">
|
||||||
<div class="gh-expandable-header">
|
<div class="gh-expandable-header">
|
||||||
<div>
|
<div>
|
||||||
|
@ -141,6 +141,7 @@ module.exports = {
|
|||||||
'source',
|
'source',
|
||||||
'force_rerender',
|
'force_rerender',
|
||||||
'save_revision',
|
'save_revision',
|
||||||
|
'convert_to_lexical',
|
||||||
// NOTE: only for internal context
|
// NOTE: only for internal context
|
||||||
'forUpdate',
|
'forUpdate',
|
||||||
'transacting'
|
'transacting'
|
||||||
|
@ -192,6 +192,7 @@ module.exports = {
|
|||||||
'newsletter',
|
'newsletter',
|
||||||
'force_rerender',
|
'force_rerender',
|
||||||
'save_revision',
|
'save_revision',
|
||||||
|
'convert_to_lexical',
|
||||||
// NOTE: only for internal context
|
// NOTE: only for internal context
|
||||||
'forUpdate',
|
'forUpdate',
|
||||||
'transacting'
|
'transacting'
|
||||||
|
@ -19,6 +19,8 @@ const {Tag} = require('./tag');
|
|||||||
const {Newsletter} = require('./newsletter');
|
const {Newsletter} = require('./newsletter');
|
||||||
const {BadRequestError} = require('@tryghost/errors');
|
const {BadRequestError} = require('@tryghost/errors');
|
||||||
const {PostRevisions} = require('@tryghost/post-revisions');
|
const {PostRevisions} = require('@tryghost/post-revisions');
|
||||||
|
const {mobiledocToLexical} = require('@tryghost/kg-converters');
|
||||||
|
const labs = require('../../shared/labs');
|
||||||
|
|
||||||
const messages = {
|
const messages = {
|
||||||
isAlreadyPublished: 'Your post is already published, please reload your page.',
|
isAlreadyPublished: 'Your post is already published, please reload your page.',
|
||||||
@ -913,6 +915,16 @@ Post = ghostBookshelf.Model.extend({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CASE: Convert post to lexical on the fly
|
||||||
|
if (labs.isSet('convertToLexical') && labs.isSet('lexicalEditor') && options.convert_to_lexical) {
|
||||||
|
ops.push(async function convertToLexical() {
|
||||||
|
const mobiledoc = model.get('mobiledoc');
|
||||||
|
const lexical = mobiledocToLexical(mobiledoc);
|
||||||
|
model.set('lexical', lexical);
|
||||||
|
model.set('mobiledoc', null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (this.get('tiers')) {
|
if (this.get('tiers')) {
|
||||||
this.set('tiers', this.get('tiers').map(t => ({
|
this.set('tiers', this.get('tiers').map(t => ({
|
||||||
id: t.id
|
id: t.id
|
||||||
@ -1154,9 +1166,10 @@ Post = ghostBookshelf.Model.extend({
|
|||||||
const validOptions = {
|
const validOptions = {
|
||||||
findOne: ['columns', 'importing', 'withRelated', 'require', 'filter'],
|
findOne: ['columns', 'importing', 'withRelated', 'require', 'filter'],
|
||||||
findPage: ['status'],
|
findPage: ['status'],
|
||||||
|
|
||||||
findAll: ['columns', 'filter'],
|
findAll: ['columns', 'filter'],
|
||||||
destroy: ['destroyAll', 'destroyBy'],
|
destroy: ['destroyAll', 'destroyBy'],
|
||||||
edit: ['filter', 'email_segment', 'force_rerender', 'newsletter', 'save_revision']
|
edit: ['filter', 'email_segment', 'force_rerender', 'newsletter', 'save_revision', 'convert_to_lexical']
|
||||||
};
|
};
|
||||||
|
|
||||||
// The post model additionally supports having a formats option
|
// The post model additionally supports having a formats option
|
||||||
|
@ -41,8 +41,9 @@ const ALPHA_FEATURES = [
|
|||||||
'mailEvents',
|
'mailEvents',
|
||||||
'collectionsCard',
|
'collectionsCard',
|
||||||
'headerUpgrade',
|
'headerUpgrade',
|
||||||
|
'tipsAndDonations',
|
||||||
'importMemberTier',
|
'importMemberTier',
|
||||||
'tipsAndDonations'
|
'convertToLexical'
|
||||||
];
|
];
|
||||||
|
|
||||||
module.exports.GA_KEYS = [...GA_FEATURES];
|
module.exports.GA_KEYS = [...GA_FEATURES];
|
||||||
|
@ -101,6 +101,7 @@
|
|||||||
"@tryghost/importer-revue": "0.0.0",
|
"@tryghost/importer-revue": "0.0.0",
|
||||||
"@tryghost/job-manager": "0.0.0",
|
"@tryghost/job-manager": "0.0.0",
|
||||||
"@tryghost/kg-card-factory": "4.0.9",
|
"@tryghost/kg-card-factory": "4.0.9",
|
||||||
|
"@tryghost/kg-converters": "0.0.7",
|
||||||
"@tryghost/kg-default-atoms": "4.0.2",
|
"@tryghost/kg-default-atoms": "4.0.2",
|
||||||
"@tryghost/kg-default-cards": "9.1.2",
|
"@tryghost/kg-default-cards": "9.1.2",
|
||||||
"@tryghost/kg-default-nodes": "0.1.17",
|
"@tryghost/kg-default-nodes": "0.1.17",
|
||||||
|
@ -1,5 +1,112 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`Pages API Convert can convert a mobiledoc page to lexical 1: [body] 1`] = `
|
||||||
|
Object {
|
||||||
|
"pages": Array [
|
||||||
|
Object {
|
||||||
|
"authors": Any<Array>,
|
||||||
|
"canonical_url": null,
|
||||||
|
"codeinjection_foot": null,
|
||||||
|
"codeinjection_head": null,
|
||||||
|
"comment_id": Any<String>,
|
||||||
|
"count": Object {
|
||||||
|
"negative_feedback": 0,
|
||||||
|
"paid_conversions": 0,
|
||||||
|
"positive_feedback": 0,
|
||||||
|
"signups": 0,
|
||||||
|
},
|
||||||
|
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
|
"custom_excerpt": null,
|
||||||
|
"custom_template": null,
|
||||||
|
"excerpt": "This is some great content.",
|
||||||
|
"feature_image": null,
|
||||||
|
"feature_image_alt": null,
|
||||||
|
"feature_image_caption": null,
|
||||||
|
"featured": false,
|
||||||
|
"frontmatter": null,
|
||||||
|
"html": "<p>This is some great content.</p>",
|
||||||
|
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||||
|
"lexical": "{\\"root\\":{\\"children\\":[{\\"children\\":[{\\"detail\\":0,\\"format\\":0,\\"mode\\":\\"normal\\",\\"style\\":\\"\\",\\"text\\":\\"This is some great content.\\",\\"type\\":\\"text\\",\\"version\\":1}],\\"direction\\":\\"ltr\\",\\"format\\":\\"\\",\\"indent\\":0,\\"type\\":\\"paragraph\\",\\"version\\":1}],\\"direction\\":\\"ltr\\",\\"format\\":\\"\\",\\"indent\\":0,\\"type\\":\\"root\\",\\"version\\":1}}",
|
||||||
|
"meta_description": null,
|
||||||
|
"meta_title": null,
|
||||||
|
"mobiledoc": null,
|
||||||
|
"og_description": null,
|
||||||
|
"og_image": null,
|
||||||
|
"og_title": null,
|
||||||
|
"post_revisions": Any<Array>,
|
||||||
|
"primary_author": Any<Object>,
|
||||||
|
"primary_tag": Any<Object>,
|
||||||
|
"published_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
|
"reading_time": 0,
|
||||||
|
"show_title_and_feature_image": Any<Boolean>,
|
||||||
|
"slug": "test-post",
|
||||||
|
"status": "published",
|
||||||
|
"tags": Any<Array>,
|
||||||
|
"tiers": Array [
|
||||||
|
Object {
|
||||||
|
"active": true,
|
||||||
|
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
|
"currency": null,
|
||||||
|
"description": null,
|
||||||
|
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||||
|
"monthly_price": null,
|
||||||
|
"monthly_price_id": null,
|
||||||
|
"name": "Free",
|
||||||
|
"slug": "free",
|
||||||
|
"trial_days": 0,
|
||||||
|
"type": "free",
|
||||||
|
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
|
"visibility": "public",
|
||||||
|
"welcome_page_url": null,
|
||||||
|
"yearly_price": null,
|
||||||
|
"yearly_price_id": null,
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"active": true,
|
||||||
|
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
|
"currency": "usd",
|
||||||
|
"description": null,
|
||||||
|
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||||
|
"monthly_price": 500,
|
||||||
|
"monthly_price_id": null,
|
||||||
|
"name": "Default Product",
|
||||||
|
"slug": "default-product",
|
||||||
|
"trial_days": 0,
|
||||||
|
"type": "paid",
|
||||||
|
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
|
"visibility": "public",
|
||||||
|
"welcome_page_url": null,
|
||||||
|
"yearly_price": 5000,
|
||||||
|
"yearly_price_id": null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"title": "Test Post",
|
||||||
|
"twitter_description": null,
|
||||||
|
"twitter_image": null,
|
||||||
|
"twitter_title": null,
|
||||||
|
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
|
"url": Any<String>,
|
||||||
|
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||||
|
"visibility": "public",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Pages API Convert can convert a mobiledoc page to lexical 2: [headers] 1`] = `
|
||||||
|
Object {
|
||||||
|
"access-control-allow-origin": "http://127.0.0.1:2369",
|
||||||
|
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||||
|
"content-length": "4013",
|
||||||
|
"content-type": "application/json; charset=utf-8",
|
||||||
|
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
||||||
|
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||||
|
"vary": "Accept-Version, Origin, Accept-Encoding",
|
||||||
|
"x-cache-invalidate": "/*",
|
||||||
|
"x-powered-by": "Express",
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`Pages API Copy Can copy a page 1: [body] 1`] = `
|
exports[`Pages API Copy Can copy a page 1: [body] 1`] = `
|
||||||
Object {
|
Object {
|
||||||
"pages": Array [
|
"pages": Array [
|
||||||
|
@ -790,6 +790,116 @@ Object {
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`Posts API Convert can convert a mobiledoc post to lexical 1: [body] 1`] = `
|
||||||
|
Object {
|
||||||
|
"posts": Array [
|
||||||
|
Object {
|
||||||
|
"authors": Any<Array>,
|
||||||
|
"canonical_url": null,
|
||||||
|
"codeinjection_foot": null,
|
||||||
|
"codeinjection_head": null,
|
||||||
|
"comment_id": Any<String>,
|
||||||
|
"count": Object {
|
||||||
|
"clicks": 0,
|
||||||
|
"negative_feedback": 0,
|
||||||
|
"positive_feedback": 0,
|
||||||
|
},
|
||||||
|
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
|
"custom_excerpt": null,
|
||||||
|
"custom_template": null,
|
||||||
|
"email": null,
|
||||||
|
"email_only": false,
|
||||||
|
"email_segment": "all",
|
||||||
|
"email_subject": null,
|
||||||
|
"excerpt": "This is some great content.",
|
||||||
|
"feature_image": null,
|
||||||
|
"feature_image_alt": null,
|
||||||
|
"feature_image_caption": null,
|
||||||
|
"featured": false,
|
||||||
|
"frontmatter": null,
|
||||||
|
"html": "<p>This is some great content.</p>",
|
||||||
|
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||||
|
"lexical": "{\\"root\\":{\\"children\\":[{\\"children\\":[{\\"detail\\":0,\\"format\\":0,\\"mode\\":\\"normal\\",\\"style\\":\\"\\",\\"text\\":\\"This is some great content.\\",\\"type\\":\\"text\\",\\"version\\":1}],\\"direction\\":\\"ltr\\",\\"format\\":\\"\\",\\"indent\\":0,\\"type\\":\\"paragraph\\",\\"version\\":1}],\\"direction\\":\\"ltr\\",\\"format\\":\\"\\",\\"indent\\":0,\\"type\\":\\"root\\",\\"version\\":1}}",
|
||||||
|
"meta_description": null,
|
||||||
|
"meta_title": null,
|
||||||
|
"mobiledoc": null,
|
||||||
|
"newsletter": null,
|
||||||
|
"og_description": null,
|
||||||
|
"og_image": null,
|
||||||
|
"og_title": null,
|
||||||
|
"post_revisions": Any<Array>,
|
||||||
|
"primary_author": Any<Object>,
|
||||||
|
"primary_tag": Any<Object>,
|
||||||
|
"published_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
|
"reading_time": 0,
|
||||||
|
"slug": "test-post-2",
|
||||||
|
"status": "published",
|
||||||
|
"tags": Any<Array>,
|
||||||
|
"tiers": Array [
|
||||||
|
Object {
|
||||||
|
"active": true,
|
||||||
|
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
|
"currency": null,
|
||||||
|
"description": null,
|
||||||
|
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||||
|
"monthly_price": null,
|
||||||
|
"monthly_price_id": null,
|
||||||
|
"name": "Free",
|
||||||
|
"slug": "free",
|
||||||
|
"trial_days": 0,
|
||||||
|
"type": "free",
|
||||||
|
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
|
"visibility": "public",
|
||||||
|
"welcome_page_url": null,
|
||||||
|
"yearly_price": null,
|
||||||
|
"yearly_price_id": null,
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"active": true,
|
||||||
|
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
|
"currency": "usd",
|
||||||
|
"description": null,
|
||||||
|
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||||
|
"monthly_price": 500,
|
||||||
|
"monthly_price_id": null,
|
||||||
|
"name": "Default Product",
|
||||||
|
"slug": "default-product",
|
||||||
|
"trial_days": 0,
|
||||||
|
"type": "paid",
|
||||||
|
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
|
"visibility": "public",
|
||||||
|
"welcome_page_url": null,
|
||||||
|
"yearly_price": 5000,
|
||||||
|
"yearly_price_id": null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"title": "Test Post",
|
||||||
|
"twitter_description": null,
|
||||||
|
"twitter_image": null,
|
||||||
|
"twitter_title": null,
|
||||||
|
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
|
"url": Any<String>,
|
||||||
|
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
|
||||||
|
"visibility": "public",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Posts API Convert can convert a mobiledoc post to lexical 2: [headers] 1`] = `
|
||||||
|
Object {
|
||||||
|
"access-control-allow-origin": "http://127.0.0.1:2369",
|
||||||
|
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||||
|
"content-length": "4050",
|
||||||
|
"content-type": "application/json; charset=utf-8",
|
||||||
|
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
||||||
|
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||||
|
"vary": "Accept-Version, Origin, Accept-Encoding",
|
||||||
|
"x-cache-invalidate": "/*",
|
||||||
|
"x-powered-by": "Express",
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`Posts API Copy Can copy a post 1: [body] 1`] = `
|
exports[`Posts API Copy Can copy a post 1: [body] 1`] = `
|
||||||
Object {
|
Object {
|
||||||
"posts": Array [
|
"posts": Array [
|
||||||
|
@ -113,4 +113,79 @@ describe('Pages API', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Convert', function () {
|
||||||
|
it('can convert a mobiledoc page to lexical', async function () {
|
||||||
|
const mobiledoc = JSON.stringify({
|
||||||
|
version: '0.3.1',
|
||||||
|
ghostVersion: '4.0',
|
||||||
|
markups: [],
|
||||||
|
atoms: [],
|
||||||
|
cards: [],
|
||||||
|
sections: [
|
||||||
|
[1, 'p', [
|
||||||
|
[0, [], 0, 'This is some great content.']
|
||||||
|
]]
|
||||||
|
]
|
||||||
|
});
|
||||||
|
const expectedLexical = JSON.stringify({
|
||||||
|
root: {
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
detail: 0,
|
||||||
|
format: 0,
|
||||||
|
mode: 'normal',
|
||||||
|
style: '',
|
||||||
|
text: 'This is some great content.',
|
||||||
|
type: 'text',
|
||||||
|
version: 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
direction: 'ltr',
|
||||||
|
format: '',
|
||||||
|
indent: 0,
|
||||||
|
type: 'paragraph',
|
||||||
|
version: 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
direction: 'ltr',
|
||||||
|
format: '',
|
||||||
|
indent: 0,
|
||||||
|
type: 'root',
|
||||||
|
version: 1
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const pageData = {
|
||||||
|
title: 'Test Post',
|
||||||
|
status: 'published',
|
||||||
|
mobiledoc: mobiledoc,
|
||||||
|
lexical: null
|
||||||
|
};
|
||||||
|
|
||||||
|
const {body: pageBody} = await agent
|
||||||
|
.post('/pages/?formats=mobiledoc,lexical,html', {
|
||||||
|
headers: {
|
||||||
|
'content-type': 'application/json'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.body({pages: [pageData]})
|
||||||
|
.expectStatus(201);
|
||||||
|
|
||||||
|
const [pageResponse] = pageBody.pages;
|
||||||
|
|
||||||
|
await agent
|
||||||
|
.put(`/pages/${pageResponse.id}/?formats=mobiledoc,lexical,html&convert_to_lexical=true`)
|
||||||
|
.body({pages: [Object.assign({}, pageResponse)]})
|
||||||
|
.expectStatus(200)
|
||||||
|
.matchBodySnapshot({
|
||||||
|
pages: [Object.assign({}, matchPageShallowIncludes, {lexical: expectedLexical, mobiledoc: null})]
|
||||||
|
})
|
||||||
|
.matchHeaderSnapshot({
|
||||||
|
'content-version': anyContentVersion,
|
||||||
|
etag: anyEtag
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -628,4 +628,40 @@ describe('Posts API', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Convert', function () {
|
||||||
|
it('can convert a mobiledoc post to lexical', async function () {
|
||||||
|
const mobiledoc = createMobiledoc('This is some great content.');
|
||||||
|
const expectedLexical = createLexical('This is some great content.');
|
||||||
|
const postData = {
|
||||||
|
title: 'Test Post',
|
||||||
|
status: 'published',
|
||||||
|
mobiledoc: mobiledoc,
|
||||||
|
lexical: null
|
||||||
|
};
|
||||||
|
|
||||||
|
const {body} = await agent
|
||||||
|
.post('/posts/?formats=mobiledoc,lexical,html', {
|
||||||
|
headers: {
|
||||||
|
'content-type': 'application/json'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.body({posts: [postData]})
|
||||||
|
.expectStatus(201);
|
||||||
|
|
||||||
|
const [postResponse] = body.posts;
|
||||||
|
|
||||||
|
await agent
|
||||||
|
.put(`/posts/${postResponse.id}/?formats=mobiledoc,lexical,html&convert_to_lexical=true`)
|
||||||
|
.body({posts: [Object.assign({}, postResponse)]})
|
||||||
|
.expectStatus(200)
|
||||||
|
.matchBodySnapshot({
|
||||||
|
posts: [Object.assign({}, matchPostShallowIncludes, {lexical: expectedLexical, mobiledoc: null})]
|
||||||
|
})
|
||||||
|
.matchHeaderSnapshot({
|
||||||
|
'content-version': anyContentVersion,
|
||||||
|
etag: anyEtag
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user