mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-25 11:55:03 +03:00
Fixed mobiledoc errors when upgrading from v1/v2 to v4 (#12741)
no issue Upgrading from v1 or v2 can result in successful upgrades but with mobiledoc errors showing in the logs: ``` NAME: InternalServerError MESSAGE: Mobiledoc card 'card-markdown' not found. ``` The errors do not signify a problem as long as the 4.0 migrations run because those rename the deprecated card before re-rendering. - `@tryghost/kg-default-cards` dropped support for `card-markdown` cards. 4.0 migrations handled this by renaming all `card-markdown` cards to `markdown` before re-generating any content - 2.0 and 3.0 also had migrations that re-generated content but they are run before the 4.0 card rename migration meaning that the mobiledoc renderer sees cards that it doesn't know about. The behaviour for unknown cards is to log an error and skip rendering of that card - by NOOPing the 2.0 and 3.0 migrations we eliminate the incompatibility errors and reduce the amount of processing the upgrade needs to perform
This commit is contained in:
parent
a87d17e570
commit
cbbf5b4ad4
@ -1,118 +1,7 @@
|
||||
const _ = require('lodash');
|
||||
const Promise = require('bluebird');
|
||||
const logging = require('../../../../../shared/logging');
|
||||
const mobiledocLib = require('../../../../lib/mobiledoc');
|
||||
const message1 = 'Updating posts: apply new editor format and set comment_id field.';
|
||||
const message2 = 'Updated posts: apply new editor format and set comment_id field.';
|
||||
const message3 = 'Rollback: Updating posts: use old editor format';
|
||||
const message4 = 'Rollback: Updated posts: use old editor format';
|
||||
|
||||
module.exports.config = {
|
||||
transaction: true
|
||||
module.exports.up = () => {
|
||||
// noop - superceded by later majors performing re-render
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
module.exports.up = (options) => {
|
||||
const postAllColumns = ['id', 'comment_id', 'html', 'mobiledoc'];
|
||||
|
||||
let localOptions = _.merge({
|
||||
context: {internal: true},
|
||||
migrating: true
|
||||
}, options);
|
||||
|
||||
logging.info(message1);
|
||||
|
||||
// @NOTE: raw knex query, because of https://github.com/TryGhost/Ghost/issues/9983
|
||||
return localOptions
|
||||
.transacting('posts')
|
||||
.select(postAllColumns)
|
||||
.then((posts) => {
|
||||
return Promise.map(posts, function (post) {
|
||||
let mobiledoc;
|
||||
let html;
|
||||
|
||||
try {
|
||||
mobiledoc = JSON.parse(post.mobiledoc || null);
|
||||
|
||||
if (!mobiledoc) {
|
||||
mobiledoc = mobiledocLib.blankDocument;
|
||||
}
|
||||
} catch (err) {
|
||||
logging.warn(`Invalid mobiledoc structure for ${post.id}. Falling back to blank structure.`);
|
||||
mobiledoc = mobiledocLib.blankDocument;
|
||||
}
|
||||
|
||||
// CASE: convert all old editor posts to the new editor format
|
||||
// CASE: if mobiledoc field is null, we auto set a blank structure in the model layer
|
||||
// CASE: if html field is null, we auto generate the html in the model layer
|
||||
if (mobiledoc && post.html && post.html.match(/^<div class="kg-card-markdown">/)) {
|
||||
html = mobiledocLib.mobiledocHtmlRenderer.render(mobiledoc);
|
||||
}
|
||||
return localOptions
|
||||
.transacting('posts')
|
||||
.where('id', '=', post.id)
|
||||
.update({
|
||||
comment_id: post.comment_id || post.id,
|
||||
html: html || post.html,
|
||||
mobiledoc: JSON.stringify(mobiledoc)
|
||||
});
|
||||
}, {concurrency: 100});
|
||||
}).then(() => {
|
||||
logging.info(message2);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.down = (options) => {
|
||||
const postAllColumns = ['id', 'html', 'mobiledoc'];
|
||||
|
||||
let localOptions = _.merge({
|
||||
context: {internal: true},
|
||||
migrating: true
|
||||
}, options);
|
||||
|
||||
logging.info(message3);
|
||||
return localOptions
|
||||
.transacting('posts')
|
||||
.select(postAllColumns)
|
||||
.then((posts) => {
|
||||
return Promise.map(posts, function (post) {
|
||||
let version = 1;
|
||||
let html;
|
||||
let mobiledoc = JSON.parse(post.mobiledoc || null);
|
||||
|
||||
if (!mobiledocIsCompatibleWithV1(mobiledoc)) {
|
||||
version = 2;
|
||||
}
|
||||
|
||||
// CASE: revert: all new editor posts to the old editor format
|
||||
if (mobiledoc && post.html) {
|
||||
html = mobiledocLib.mobiledocHtmlRenderer.render(mobiledoc, {version});
|
||||
}
|
||||
|
||||
return localOptions
|
||||
.transacting('posts')
|
||||
.where('id', '=', post.id)
|
||||
.update({
|
||||
html: html || post.html
|
||||
});
|
||||
}, {concurrency: 100});
|
||||
})
|
||||
.then(() => {
|
||||
logging.info(message4);
|
||||
});
|
||||
module.exports.down = () => {
|
||||
// noop - superceded by later majors performing re-render
|
||||
};
|
||||
|
@ -1,79 +1,7 @@
|
||||
const _ = require('lodash');
|
||||
const Promise = require('bluebird');
|
||||
const htmlToText = require('html-to-text');
|
||||
const logging = require('../../../../../shared/logging');
|
||||
const mobiledocLib = require('../../../../lib/mobiledoc');
|
||||
|
||||
module.exports.config = {
|
||||
transaction: true
|
||||
module.exports.up = () => {
|
||||
// noop - superceded by later majors performing re-render
|
||||
};
|
||||
|
||||
module.exports.up = (options) => {
|
||||
const columns = ['id', 'html', 'mobiledoc', 'plaintext'];
|
||||
|
||||
let localOptions = _.merge({
|
||||
context: {internal: true},
|
||||
migrating: true
|
||||
}, options);
|
||||
|
||||
logging.info('Starting re-generation of posts html.');
|
||||
return localOptions
|
||||
.transacting('posts')
|
||||
.select(columns)
|
||||
.then((posts) => {
|
||||
return Promise.map(posts, function (post) {
|
||||
let mobiledoc;
|
||||
|
||||
try {
|
||||
mobiledoc = JSON.parse(post.mobiledoc || null);
|
||||
|
||||
if (!mobiledoc) {
|
||||
logging.warn(`No mobiledoc for ${post.id}. Skipping.`);
|
||||
return Promise.resolve();
|
||||
}
|
||||
} catch (err) {
|
||||
logging.warn(`Invalid JSON structure for ${post.id}. Skipping.`);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const html = mobiledocLib.mobiledocHtmlRenderer.render(mobiledoc);
|
||||
|
||||
const updatedAttrs = {
|
||||
html: html
|
||||
};
|
||||
|
||||
// NOTE: block comes straight from the Post model (https://github.com/TryGhost/Ghost/blob/3.0.0/core/server/models/post.js#L416)
|
||||
if (html !== post.html || !post.plaintext) {
|
||||
const plaintext = htmlToText.fromString(post.html, {
|
||||
wordwrap: 80,
|
||||
ignoreImage: true,
|
||||
hideLinkHrefIfSameAsText: true,
|
||||
preserveNewlines: true,
|
||||
returnDomByDefault: true,
|
||||
uppercaseHeadings: false
|
||||
});
|
||||
|
||||
// CASE: html is e.g. <p></p>
|
||||
// @NOTE: Otherwise we will always update the resource to `plaintext: ''` and Bookshelf thinks that this
|
||||
// value was modified.
|
||||
if (plaintext || plaintext !== post.plaintext) {
|
||||
updatedAttrs.plaintext = plaintext;
|
||||
}
|
||||
}
|
||||
|
||||
return localOptions
|
||||
.transacting('posts')
|
||||
.where('id', '=', post.id)
|
||||
.update(updatedAttrs);
|
||||
}, {concurrency: 100});
|
||||
})
|
||||
.then(() => {
|
||||
logging.info('Finished re-generation of posts html.');
|
||||
});
|
||||
};
|
||||
|
||||
// There's nothing we can do on rollback, getting back to the previous html
|
||||
// would only be possible if the rollback was run against the 2.x codebase
|
||||
module.exports.down = () => {
|
||||
return Promise.resolve();
|
||||
// noop - superceded by later majors performing re-render
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user