array_get( $block, 'blockName', '' ); return 'divi/layout' === $block_name; } /** * Check if given block is reusable block * * @since 4.1.0 * * @todo being set as static so it is easier to be used outside this class. If being used quite * frequently, probably consider wrap this into function. Not needed at the moment tho * * @param array $block Parsed block. * * @return bool */ public static function is_reusable_block( $block = array() ) { $block_name = et_()->array_get( $block, 'blockName', '' ); return 'core/block' === $block_name && et_()->array_get( $block, 'attrs.ref' ) > 0; } /** * Get reusable block's parsed content. NOTE: WordPress has built in `render_block_core_block()` * but it renders the block and its content instead of parse its content. * * @since 4.1.0 * * @see render_block_core_block() * * @todo being set as static so it is easier to be used outside this class. If being used quite * frequently, probably consider wrap this into function. Not needed at the moment tho * * @param array $block Parsed block. * * @return array */ public static function get_reusable_block_content( $block ) { $block_id = et_()->array_get( $block, 'attrs.ref' ); $block_data = get_post( $block_id ); if ( ! $block_data || 'wp_block' !== $block_data->post_type || 'publish' !== $block_data->post_status ) { return array(); } return parse_blocks( $block_data->post_content ); } /** * Parse reusable block by getting its content and append it as innerBlocks * * @since 4.1.0 * * @param array Parsed block. * * @return array Modified parsed block. */ public static function parse_reusable_block( $block ) { $reusable_block_data = self::get_reusable_block_content( $block ); $block['innerBlocks'] = array_merge( $block['innerBlocks'], $reusable_block_data ); // Unset reusable block's ref attribute so reusable block content is no longer fetched unset( $block['attrs']['ref'] ); // Change block into group so its content is being rendered $block['blockName'] = 'core/group'; // Recreate innerContent which is used by block parser to render innerBlock. // See: `render_block()`'s `$block['innerContent'] as $chunk` loop $block['innerContent'] = array_merge( array( '
' ), array_fill( 0, count( $block['innerBlocks'] ), null ), array( '
' ) ); return $block; } /** * Pull layout block that is located deep inside inner blocks. Layout block contains section; * in builder, section has to be on the first level of document * * @since 4.1.0 * * @param array $block Parsed block. */ private function pull_layout_block( $block ) { // Pull and populate layout block. Layout block contains section(s) so it should be rendered // on first level layout, below Gutenberg content inside text module if ( self::is_layout_block( $block ) ) { // Pull layout block and populate list of layout block located on inner blocks $this->deep_layout_blocks[] = $block; // Remove innerContent and innerHTML value because inner block can't be simply removed // due to nested block rendering relies on `$block['innerContent']` making cross reference // on `$block['innerBlocks']` and removing them causes error (see: `render_block()`'s // `$block['innerContent'] as $chunk` loop). Thus, set deep layout block's content empty // so it doesn't get rendered $block['innerHTML'] = ''; $block['innerContent'] = array(); return $block; } // Reusable block's content is not saved inside block; Thus Get reusable block's content, // append it as innerBlock, and pull layout block if exist. if ( self::is_reusable_block( $block ) ) { $block = self::parse_reusable_block( $block ); } // Recursively loop over block then pull Layout Block if ( ! empty( $block['innerBlocks'] ) ) { $block['innerBlocks'] = array_map( array( $this, 'pull_layout_block' ), $block['innerBlocks'] ); } return $block; } /** * Convert serialized block into shortcode layout * * @since 4.1.0 * * @param string $serialized_block * * @return string */ public function block_to_shortcode( $serialized_block = '' ) { // Wrapper div needs to be trimmed $layout_open_tag = '
'; $layout_open_length = strlen( $layout_open_tag ); $layout_close_tag = '
'; $layout_close_length = strlen( $layout_close_tag ); // Parsed blocks $blocks = parse_blocks( $serialized_block ); // Loop blocks foreach ( $blocks as $block ) { if ( self::is_layout_block( $block ) ) { // Append currently populated non-Layout Block into one before layout block is appended if ( ! empty( $this->text_module_content ) ) { $this->layout_list[] = $this->text_module_content; // Reset text module content so next non-layout block is placed below current layout block $this->text_module_content = ''; } $this->layout_list[] = $block; } else { // Reusable block's content is not saved inside block; Thus Get reusable block's // content, append it as innerBlock, and pull layout block if exist. if ( self::is_reusable_block( $block ) ) { $block = self::parse_reusable_block( $block ); } // Pull any Layout Block inside nested block if there's any if ( ! empty( $block['innerBlocks'] ) ) { $block['innerBlocks'] = array_map( array( $this, 'pull_layout_block' ), $block['innerBlocks'] ); } // Populate block into temporary text module content buffer $this->text_module_content .= render_block( $block ); } } // Populate remaining non-layout block into layout list if ( ! empty( $this->text_module_content ) ) { $this->layout_list[] = $this->text_module_content; // Reset $this->text_module_content = ''; } // Loop over populated content and render it into shortcode layout foreach ( array_merge( $this->layout_list, $this->deep_layout_blocks ) as $item ) { if ( self::is_layout_block( $item ) ) { $shortcode_layout = trim( et_()->array_get( $item, 'innerHTML', '' ) ); // Remove layout content opening
if ( $layout_open_tag === substr( $shortcode_layout, 0, $layout_open_length ) ) { $shortcode_layout = substr( $shortcode_layout, $layout_open_length ); } // Remove layout content closing
if ( $layout_close_tag === substr( $shortcode_layout, ( 0 - $layout_close_length ) ) ) { $shortcode_layout = substr( $shortcode_layout, 0, ( 0 - $layout_close_length ) ); } $this->shortcode_layout .= $shortcode_layout; } else { $text_module = '[et_pb_text]' . $item . '[/et_pb_text]'; $column = '[et_pb_column type="4_4"]' . $text_module . '[/et_pb_column]'; $row = '[et_pb_row admin_label="row"]' . $column . '[/et_pb_row]'; $this->shortcode_layout .= '[et_pb_section admin_label="section"]' . $row . '[/et_pb_section]'; } } return $this->shortcode_layout; } } /** * Convert gutenberg block layout into shortcode. * NOTE: There is JS version for activation via Gutenberg. See: `convertBlockToShortcode()` * * @since 4.1.0 * * @param string $post_content Post content / serialized block. * * @return string Shortcode layout. */ function et_builder_convert_block_to_shortcode( $post_content ) { $conversion = new ET_GB_Utils_Conversion(); return $conversion->block_to_shortcode( $post_content ); }