如何修改古腾堡应用于粘贴内容的过滤?

时间:2019-11-07 作者:davemackey

Note: 我正在添加我发现的信息,这些信息似乎会导致注释中的解决方案。

问题是:

When pasting content from a source external to Gutenberg into Gutenberg some HTML/CSS formatting is lost.[1] Gutenberg保留了大多数HTML(语义)元素,但删除了CSS(样式/非语义)元素。这意味着在粘贴事件期间,字体大小、文本对齐方式、文本颜色等属性都将被删除。

不是问题:

我们可以讨论用于将外部内容源(如谷歌文档)转换为WP友好内容的插件、自定义HTML块等(如Wordable、JetPack),但这个问题显然与这些解决方案无关。相反this question is exclusively focused on how to programmatically alter 古腾堡的糊处理行为。

看到问题在行动

这个问题在很多情况下都会发生。例如,尝试将以下HTML块粘贴到Gutenberg中的段落块中:

<p style="color:red">Hello WordPress StackExchange!</p>

然后查看该段落块的HTML,您将看到:

<p>Hello WordPress StackExchange!</p>

这个style="color:red" 已被剥离。

查看段落块

受此剥离影响的一个块是段落块(/gutenberg/packages/block-library/src/paragraph). 此块[2]使用RichText 组件(/gutenberg/packages/block-editor/rich-text) 实现其富文本编辑功能。

查看中的RichText组件/rich-text/index.js 我们发现onPaste 段落块继承的方法。此函数依次调用pasteHandler 功能(/gutenberg/packages/blocks/src/api/raw-handling/paste-handler.js).

查看粘贴处理程序pasteHandler 函数“将HTML字符串转换为已知块。删除所有其他内容。”根据JSDoc。

此函数采用五个参数:

  • HTML = 如果为HTML格式,则要转换的源内容plainText = 以文本格式转换的源内容mode = 是将内容粘贴为块还是将内联内容粘贴到现有块中
  • tagName = 我们正在将内容插入的标签
  • canUserUseUnfilteredHTML = 起初我认为这决定了一个人是否可以使用任何想要的HTML/CSS,但它似乎更有限,因为它只决定iframeRemover 函数针对粘贴的内容运行,粘贴的内容仅与内容相关pasteHandler 已导入(index.js):

    import { 
      pasteHandler, 
      children as childrenSource, 
      getBlockTransforms, 
      findTransform, 
      isUnmodifiedDefaultBlock
     } from \'@wordpress/blocks\';
    
    pasteHandler 然后从调用onPaste:

    onPaste( { value, onChange, html, plainText, files } ) {
    
    ...
    
    if ( files && files.length && ! html ) {
      const content = pasteHandler( {
        HTML: filePasteHandler( files),
        mode: \'BLOCKS\',
        tagName,
    } );
    
    ...
    
    const content = pasteHandler ( {
      HTML: html,
      plainText,
      mode,
      tagName,
      canUserUseUnfilteredHTML,
    } );
    
    ...
    
    }
    
    出于我们的目的,我们只对pasteHandler函数的一部分感兴趣:

    const rawTransforms = getRawTransformations();
    const phrasingContentSchema = getPhrasingContentSchema( \'paste\' );
    const blockContentSchema = getBlockContentSchema( rawTransforms, phrasingContentSchema, true );
    
    const blocks = compact( flatMap( pieces, ( piece ) => {
        ...
    
        if ( ! canUserUseUnfilteredHTML ) {
            // Should run before `figureContentReducer`.
            filters.unshift( iframeRemover );
        }
    
        const schema = {
            ...blockContentSchema,
            // Keep top-level phrasing content, normalised by `normaliseBlocks`.
            ...phrasingContentSchema,
        };
    
        piece = deepFilterHTML( piece, filters, blockContentSchema );
        piece = removeInvalidHTML( piece, schema );
        piece = normaliseBlocks( piece );
        piece = deepFilterHTML( piece, [
            htmlFormattingRemover,
            brRemover,
            emptyParagraphRemover,
        ], blockContentSchema );
    
        ...
    
        return htmlToBlocks( { html: piece, rawTransforms } );
    } ) );
    
    即使在这里,发生的大多数事情与我们当前的问题无关。例如,我们不关心Google文档UID被删除或单词列表被转换。

    相反,我们感兴趣的是:

    • rawTransforms - 包含调用的结果getRawTransformations, 也在中定义paste-handler.js.<我不认为这段代码涉及其中,但也许有人可以帮助我理解它的功能:)
  • phrasingContentSchema - 包含调用的结果getPhrasingContentSchema, 定义于phrasing-content.js.<这似乎删除了一些不可见的属性(u、abbr、data等),这些属性可能是这个问题的一部分,但人们更可能遇到的问题是CSS样式,而不是这些属性
  • blockContentSchema - 包含调用的结果getBlockContentSchema, 定义于utils.js.<再一次,我不完全确定我理解它的作用,但我认为它与此无关
  • phrasingContentReducer - 过滤器之一,在中定义phrasing-content-reducer.js.<我不确定,但我怀疑可能涉及到这个片段:
  • if ( node.nodeName === \'SPAN\' && node.style ) {
      const {
        fontWeight,
        fontStyle,
        textDecorationLine,
        textDecoration,
        verticalAlign,
      } = node.style;
    
    • deepFilterHTML - 定义于utils.js, 本质上是deepFilterNodeList, 也可在中找到utils.js.<再一次,我不确定我是否理解这段代码,可能涉及其中
  • removeInvalidHTML - 定义于utils.js, 本质上是cleanNodeList, 也可在中找到utils.js.<相信这其中有牵连,cleanNodeList JSDoc指出,“给定一个模式,展开或删除节点上的节点、属性和类”
  • 您会注意到列表中没有列出的几个函数-在查看它们的代码后,我认为它们与当前问题无关(例如。,normaliseBlocks, brRemover, emptyParagraphRemover, 等等)。

    结论是,我刚刚重写了这个问题的大部分内容,稍后我将尝试对其进行细化,并分享更多关于我不理解的特定代码片段在我有机会查看时的作用。希望这可能对其他人有帮助/有人可以向我解释我错过了什么。。。或者我可以继续努力。:)

    [1] 从技术上讲,这并不总是正确的。某些块可能接受粘贴到其中的大部分/所有内容,例如HTML块。但保留粘贴的内容是一个例外,而不是规则。

    [2] 您可以在中找到参考/paragraph/edit.jsParagraphBlock 作用

    2 个回复
    SO网友:Spcaeyob

    在我的街区。js,位于wp includes/js/dist/I查找RemoveInvalidHTML 函数,它似乎负责从粘贴的HTML中删除样式。

    /**
     * Given a schema, unwraps or removes nodes, attributes and classes on HTML.
     *
     * @param {string} HTML   The HTML to clean up.
     * @param {Object} schema Schema for the HTML.
     * @param {Object} inline Whether to clean for inline mode.
     *
     * @return {string} The cleaned up HTML.
     */
    
    
    function removeInvalidHTML(HTML, schema, inline) {
      var doc = document.implementation.createHTMLDocument(\'\');
      doc.body.innerHTML = HTML;
      cleanNodeList(doc.body.childNodes, doc, schema, inline);
      return doc.body.innerHTML;
    }
    
    如您所见,这会返回only innerHTML。修改(创建自定义版本)此功能可能会解决您的问题。

    SO网友:Robert

    我没有和古腾堡玩太多,但我很可能需要在将来解决类似的问题,

    我要做的就是直接编辑函数,以便快速调试。

    const rawTransforms = getRawTransformations();
    const phrasingContentSchema = getPhrasingContentSchema( \'paste\' );
    const blockContentSchema = getBlockContentSchema( rawTransforms, phrasingContentSchema, true );
    
    const blocks = compact( flatMap( pieces, ( piece ) => {
        ...
        // console.log( "Piece - deepFilterHTML" )
        // console.log( piece );
    
        piece = deepFilterHTML( piece, filters, blockContentSchema );
    
        // console.log( "Piece - removeInvalidHTML" )
        // console.log( piece );
    
        piece = removeInvalidHTML( piece, schema );
    
        // console.log( "Piece - normaliseBlocks" )
        // console.log( piece );
    
        piece = normaliseBlocks( piece );
    
        // console.log( "Piece - deepFilterHTML" )
        // console.log( piece );
    
        piece = deepFilterHTML( piece, [
            htmlFormattingRemover,
            brRemover,
            emptyParagraphRemover,
        ], blockContentSchema );
    
        ...
    
        return htmlToBlocks( { html: piece, rawTransforms } );
    } ) );
    
    我敢肯定,doc粘贴与HTML的不同之处在于“cleanNodeList”函数,它有4个参数:nodeList、doc、schema、inline

    这个schema 参数是“可以随提供的节点变化的函数数组”

    我想知道当你的html/doc被过滤时,与未过滤时相比,这会产生什么结果

    然后我想知道如何更改该参数,因为我认为当它被“过滤”时,它将处于默认状态。

    这就是默认情况下运行的代码的长度:

      cleanNodeList(node.childNodes, doc, schema, inline); // For inline mode, insert a line break when unwrapping nodes that
      // are not phrasing content.
    
      if (inline && !isPhrasingContent(node) && node.nextElementSibling) {
        Object(external_this_wp_dom_["insertAfter"])(doc.createElement(\'br\'), node);
      }
    
      Object(external_this_wp_dom_["unwrap"])(node);
    
    external_this_wp_dom_["unwrap"] 运行默认筛选时,从外观来看,最有可能依赖于phrasing\\u content\\u phrasingContentSchema<span> 标记根本没有定义。

    var phrasing_content_phrasingContentSchema = {
      strong: {},
      em: {},
      s: {},
      del: {},
      ins: {},
      a: {
        attributes: [\'href\', \'target\', \'rel\']
      },
      code: {},
      abbr: {
        attributes: [\'title\']
      },
      sub: {},
      sup: {},
      br: {},
      \'#text\': {}
    }
    
    希望这会有所帮助。

    相关推荐

    apply_filters to $GLOBALS

    我正在使用Pootle页面构建插件构建一个单页网站。使用get\\u page,我在1页上显示每个单独的页面。为了针对Pootle页面生成器内容,我使用以下代码:$content = $GLOBALS[\'Pootle_Page_Builder_Render_Layout\']->panels_render( $page_data->ID ); 当我尝试实现一个像旋转木马这样的插件时,我需要使用一个短代码,但短代码不起作用。为了使短代码有效,我需要使用apply_filters(\'