将 Markdown 表格迁移到 Contentful 中的富文本创建内联条目
Migrating Markdown Tables to Rich Text in Contentful creating inline Entries
我想将用 Markdown 编写的长文章迁移到富文本字段。除了 table 之外,我可以处理所有事情。 In the documentation it says:
Migrate the content into a linked entry which only has a Markdown field with the supported content, which is similar to the action performed in step 8
但我无法让它工作。在提到的第 8 步中,没有创建新条目,仅链接现有条目。我还没有弄清楚如何即时创建类型为 table 的新条目(这只是一个带有名称内容的 Markdown 类型的字段)并在富文本内容中引用它。 deriveLinkedEntries() 想要将引用放入特定字段,但这是内联的并且可以存在多次。
我可以在富文本字段中手动创建一个 table 并内联对内容的引用,但我想用迁移脚本创建它,但不知道如何创建。
这些是我的内容类型:
- Article with old content field and new one
- Table element to be inlined
- Inline Table element in rich text field
这就是我在 table 的控制台上得到的:
{
type: 'table',
align: [ null, 'center', 'right' ],
children: [
{ type: 'tableRow', children: [Array], position: [Position] },
{ type: 'tableRow', children: [Array], position: [Position] }
],
position: Position {
start: { line: 14, column: 1, offset: 970 },
end: { line: 25, column: 58, offset: 1660 },
indent: [
1, 1
]
}
}
这是我到目前为止的代码(为简洁起见进行了清理):
const {richTextFromMarkdown} = require('@contentful/rich-text-from-markdown')
module.exports = function(migration) {
migration.transformEntries({
contentType: 'article',
from: ['content'],
to: ['contentV2'],
transformEntryForLocale: async function(fromFields, currentLocale)
{
let copy = fromFields.content[currentLocale]
const content = await richTextFromMarkdown(copy,
(node) => {
let ret = null
let didSomething = true
node.deriveLinkedEntries()
switch (node.type) {
case 'image':
// ...
break;
case 'html':
// ...
break;
default:
didSomething = false
}
if (false === didSomething) {
console.log(node)
}
return ret
}
)
return {
contentV2: {
nodeType: 'document',
content: content.content,
data: {}
},
}
}
})
}
经过更多的头痛和良好的睡眠后,我找到了解决方案:
const { richTextFromMarkdown } = require('@contentful/rich-text-from-markdown')
const { createClient } = require('contentful-management')
module.exports = function(migration)
{
const managementClient = createClient({ accessToken: context.accessToken })
const space = await managementClient.getSpace(context.spaceId)
const environment = await space.getEnvironment(config.activeEnvironmentId)
/**
* Creates a simple hash for a string
* @param string
*/
function createHash(string)
{
let hash = 0,
i,
chr
if (string.length === 0) {
return hash
}
for (i = 0; i < string.length; i++)
{
chr = string.charCodeAt(i);
hash = ((hash << 5) - hash) + chr;
hash |= 0 // Convert to 32bit integer
}
return hash
}
/**
* Extracts part from MarkDown that is the table, transfers content into an
* entry that is just a MarkDown field. Entry is created if does not exist
* @param table
* @param copy
*/
async function linkTableEntry(table, copy)
{
// extract MarkDown for the table from the entered text
const pos = table.position
const strLen = pos.end.offset - pos.start.offset
const tableStr = copy.substr(pos.start.offset, strLen)
const id = createHash(tableStr) // avoid creating same table twice
let entry
try {
entry = await environment.getEntry(id)
} catch (e) { }
// create new since it does not exist yet
if ('undefined' === typeof entry) {
entry = await createTableEntry(tableStr, id)
}
return {
nodeType: 'embedded-entry-block',
content: [],
data: {
target: {
sys: {
type: 'Link',
linkType: 'Entry',
id: entry.sys.id
}
}
}
}
}
/**
* Creates a new Entry element with a string representing the table MarkDown
* The table header is transformed into a title for the Entry
* @param tablestring
* @param id
*/
async function createTableEntry(tablestring, id)
{
// create entry title from table header
const title = tablestring
.match(/^.+|\n/g)[0] // first line
.split('|')
.filter(Boolean) // remove empty elements
.join(', ') // Column-Names joined by comma
.replace(/\t/g, '') // remove tabs
.replace(/[ ]+,/g, ', ') // no spaces before comma
.replace(/[ ]+/g, ' ') // no multiple spaces
.trim()
let entry = await environment.createEntryWithId('table', id, {
fields: {
title: {
'de-DE': title
},
content: {
'de-DE': tablestring
}
}
})
return await entry.publish()
}
migration.transformEntries({
contentType: 'article',
from: ['content'],
to: ['contentV2'],
transformEntryForLocale: async function(fromFields, currentLocale)
{
let copy = fromFields.content[currentLocale]
const content = await richTextFromMarkdown(copy,
async (node) => {
let ret = null
let didSomething = true
node.deriveLinkedEntries()
switch (node.type) {
case 'image':
// ...
break;
case 'html':
// ...
break;
case 'table':
ret = await linkTableEntry(node, copy)
break
default:
didSomething = false
}
if (false === didSomething) {
console.log(node)
}
return ret
}
)
return {
contentV2: {
nodeType: 'document',
content: content.content,
data: {}
},
}
}
})
}
基本上这里发生了什么:
- 我创建了一个新的内容模型
table
,它只有一个 title
和一个长文本字段 content
- 我们使用
richTextFromMarkdown()
给出的位置信息(例如参见问题)从内容 中提取 table 的 MarkDown 字符串
- 我们为该字符串生成一个简单的散列,因此如果我们需要重新运行 迁移
,我们不会创建相同的 table 两次
- 如果 table 已经存在,我们内联现有的,如果不存在,我们使用 ContentFul 的
managementClient
动态创建一个新的
- 通过此条目的
sys.id
,我们可以将链接条目放入富文本字段
我希望这对某些人有所帮助,因为我找不到有关将 tables 从 MarkDown 迁移到 Contentful 中的富文本的任何内容。
我想将用 Markdown 编写的长文章迁移到富文本字段。除了 table 之外,我可以处理所有事情。 In the documentation it says:
Migrate the content into a linked entry which only has a Markdown field with the supported content, which is similar to the action performed in step 8
但我无法让它工作。在提到的第 8 步中,没有创建新条目,仅链接现有条目。我还没有弄清楚如何即时创建类型为 table 的新条目(这只是一个带有名称内容的 Markdown 类型的字段)并在富文本内容中引用它。 deriveLinkedEntries() 想要将引用放入特定字段,但这是内联的并且可以存在多次。
我可以在富文本字段中手动创建一个 table 并内联对内容的引用,但我想用迁移脚本创建它,但不知道如何创建。
这些是我的内容类型:
- Article with old content field and new one
- Table element to be inlined
- Inline Table element in rich text field
这就是我在 table 的控制台上得到的:
{
type: 'table',
align: [ null, 'center', 'right' ],
children: [
{ type: 'tableRow', children: [Array], position: [Position] },
{ type: 'tableRow', children: [Array], position: [Position] }
],
position: Position {
start: { line: 14, column: 1, offset: 970 },
end: { line: 25, column: 58, offset: 1660 },
indent: [
1, 1
]
}
}
这是我到目前为止的代码(为简洁起见进行了清理):
const {richTextFromMarkdown} = require('@contentful/rich-text-from-markdown')
module.exports = function(migration) {
migration.transformEntries({
contentType: 'article',
from: ['content'],
to: ['contentV2'],
transformEntryForLocale: async function(fromFields, currentLocale)
{
let copy = fromFields.content[currentLocale]
const content = await richTextFromMarkdown(copy,
(node) => {
let ret = null
let didSomething = true
node.deriveLinkedEntries()
switch (node.type) {
case 'image':
// ...
break;
case 'html':
// ...
break;
default:
didSomething = false
}
if (false === didSomething) {
console.log(node)
}
return ret
}
)
return {
contentV2: {
nodeType: 'document',
content: content.content,
data: {}
},
}
}
})
}
经过更多的头痛和良好的睡眠后,我找到了解决方案:
const { richTextFromMarkdown } = require('@contentful/rich-text-from-markdown')
const { createClient } = require('contentful-management')
module.exports = function(migration)
{
const managementClient = createClient({ accessToken: context.accessToken })
const space = await managementClient.getSpace(context.spaceId)
const environment = await space.getEnvironment(config.activeEnvironmentId)
/**
* Creates a simple hash for a string
* @param string
*/
function createHash(string)
{
let hash = 0,
i,
chr
if (string.length === 0) {
return hash
}
for (i = 0; i < string.length; i++)
{
chr = string.charCodeAt(i);
hash = ((hash << 5) - hash) + chr;
hash |= 0 // Convert to 32bit integer
}
return hash
}
/**
* Extracts part from MarkDown that is the table, transfers content into an
* entry that is just a MarkDown field. Entry is created if does not exist
* @param table
* @param copy
*/
async function linkTableEntry(table, copy)
{
// extract MarkDown for the table from the entered text
const pos = table.position
const strLen = pos.end.offset - pos.start.offset
const tableStr = copy.substr(pos.start.offset, strLen)
const id = createHash(tableStr) // avoid creating same table twice
let entry
try {
entry = await environment.getEntry(id)
} catch (e) { }
// create new since it does not exist yet
if ('undefined' === typeof entry) {
entry = await createTableEntry(tableStr, id)
}
return {
nodeType: 'embedded-entry-block',
content: [],
data: {
target: {
sys: {
type: 'Link',
linkType: 'Entry',
id: entry.sys.id
}
}
}
}
}
/**
* Creates a new Entry element with a string representing the table MarkDown
* The table header is transformed into a title for the Entry
* @param tablestring
* @param id
*/
async function createTableEntry(tablestring, id)
{
// create entry title from table header
const title = tablestring
.match(/^.+|\n/g)[0] // first line
.split('|')
.filter(Boolean) // remove empty elements
.join(', ') // Column-Names joined by comma
.replace(/\t/g, '') // remove tabs
.replace(/[ ]+,/g, ', ') // no spaces before comma
.replace(/[ ]+/g, ' ') // no multiple spaces
.trim()
let entry = await environment.createEntryWithId('table', id, {
fields: {
title: {
'de-DE': title
},
content: {
'de-DE': tablestring
}
}
})
return await entry.publish()
}
migration.transformEntries({
contentType: 'article',
from: ['content'],
to: ['contentV2'],
transformEntryForLocale: async function(fromFields, currentLocale)
{
let copy = fromFields.content[currentLocale]
const content = await richTextFromMarkdown(copy,
async (node) => {
let ret = null
let didSomething = true
node.deriveLinkedEntries()
switch (node.type) {
case 'image':
// ...
break;
case 'html':
// ...
break;
case 'table':
ret = await linkTableEntry(node, copy)
break
default:
didSomething = false
}
if (false === didSomething) {
console.log(node)
}
return ret
}
)
return {
contentV2: {
nodeType: 'document',
content: content.content,
data: {}
},
}
}
})
}
基本上这里发生了什么:
- 我创建了一个新的内容模型
table
,它只有一个title
和一个长文本字段content
- 我们使用
richTextFromMarkdown()
给出的位置信息(例如参见问题)从内容 中提取 table 的 MarkDown 字符串
- 我们为该字符串生成一个简单的散列,因此如果我们需要重新运行 迁移 ,我们不会创建相同的 table 两次
- 如果 table 已经存在,我们内联现有的,如果不存在,我们使用 ContentFul 的
managementClient
动态创建一个新的 - 通过此条目的
sys.id
,我们可以将链接条目放入富文本字段
我希望这对某些人有所帮助,因为我找不到有关将 tables 从 MarkDown 迁移到 Contentful 中的富文本的任何内容。