From c580ac9b20e162a6edb42d971c9f432d93a7cd05 Mon Sep 17 00:00:00 2001 From: Kanishk Dudeja Date: Sun, 8 Apr 2018 21:27:38 +0530 Subject: [PATCH 01/24] Added class for managing the storage of block types parsed from the HTML Added a class "WP_Parsed_Block_Types_Registry" to manage the storage of block types parsed from the HTML (while stripping block comments) --- lib/class-wp-parsed-block-types-registry.php | 122 +++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 lib/class-wp-parsed-block-types-registry.php diff --git a/lib/class-wp-parsed-block-types-registry.php b/lib/class-wp-parsed-block-types-registry.php new file mode 100644 index 00000000000000..e9e4e1d90af1ce --- /dev/null +++ b/lib/class-wp-parsed-block-types-registry.php @@ -0,0 +1,122 @@ +name; + } + + if ( ! is_string( $block_type ) ) { + $message = __( 'Block type names must be strings.', 'gutenberg' ); + _doing_it_wrong( __METHOD__, $message, '2.6.0' ); + return false; + } + + if ( preg_match( '/[A-Z]+/', $block_type ) ) { + $message = __( 'Block type names must not contain uppercase characters.', 'gutenberg' ); + _doing_it_wrong( __METHOD__, $message, '2.6.0' ); + return false; + } + + $name_matcher = '/^[a-z0-9-]+\/[a-z0-9-]+$/'; + if ( ! preg_match( $name_matcher, $block_type ) ) { + $message = __( 'Block type names must contain a namespace prefix. Example: my-plugin/my-custom-block-type', 'gutenberg' ); + _doing_it_wrong( __METHOD__, $message, '2.6.0' ); + return false; + } + + if ( ! WP_Block_Type_Registry::get_instance()->is_registered( $block_type ) ) { + /* translators: 1: block name */ + $message = sprintf( __( 'Block type "%s" isn\'t registered yet.', 'gutenberg' ), $block_type ); + _doing_it_wrong( __METHOD__, $message, '0.1.0' ); + return false; + } + + $this->block_types_in_current_page[] = $block_type; + + return true; + } + + /** + * Retrieves all block types present in the web page being currently served + * + * @since 2.6.0 + * @access public + * + * @return array Array of block types present on the web page being currently served (As an array of strings) + */ + public function get_block_types_present_in_current_page() { + return $this->block_types_in_current_page; + } + + /** + * Checks if a block type is present on the current web page + * + * @since 2.6.0 + * @access public + * + * @param string $block_type Block type name including namespace. + * @return bool True if the block type is present, false otherwise. + */ + public function is_block_type_present_on_current_page( $block_type ) { + return isset( $this->block_types_in_current_page[ $block_type ] ); + } + + /** + * Utility method to retrieve the main instance of the class. + * + * The instance will be created if it does not exist yet. + * + * @access public + * @static + * + * @return WP_Parsed_Block_Types_Registry The main instance. + */ + public static function get_instance() { + if ( null === self::$instance ) { + self::$instance = new self(); + } + + return self::$instance; + } +} \ No newline at end of file From 27c49648034d86c88de1134c6e930141c9962454 Mon Sep 17 00:00:00 2001 From: Kanishk Dudeja Date: Sun, 8 Apr 2018 21:32:44 +0530 Subject: [PATCH 02/24] Added @since version to WP_Parsed_Block_Types_Registry class --- lib/class-wp-parsed-block-types-registry.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/class-wp-parsed-block-types-registry.php b/lib/class-wp-parsed-block-types-registry.php index e9e4e1d90af1ce..00b438f5ae9203 100644 --- a/lib/class-wp-parsed-block-types-registry.php +++ b/lib/class-wp-parsed-block-types-registry.php @@ -107,6 +107,7 @@ public function is_block_type_present_on_current_page( $block_type ) { * * The instance will be created if it does not exist yet. * + * @since 2.6.0 * @access public * @static * From b91904bd7f22b0df9b001c0bcd1564ec0e6554d4 Mon Sep 17 00:00:00 2001 From: Kanishk Dudeja Date: Sun, 8 Apr 2018 22:06:42 +0530 Subject: [PATCH 03/24] Added logic to find block type while stripping block comments from the HTML --- lib/blocks.php | 32 ++++++++++++++++++++++++++++---- lib/load.php | 1 + 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/lib/blocks.php b/lib/blocks.php index e28971bad032c2..e0db3a8f2570fc 100644 --- a/lib/blocks.php +++ b/lib/blocks.php @@ -123,7 +123,7 @@ function gutenberg_render_block( $block ) { * @param string $content Post content. * @return string Updated post content. */ -function do_blocks( $content ) { +function render_dynamic_blocks( $content ) { $rendered_content = ''; $dynamic_block_names = get_dynamic_block_names(); @@ -199,9 +199,33 @@ function do_blocks( $content ) { // Append remaining unmatched content. $rendered_content .= $content; + return $rendered_content; +} + +function gutenberg_strip_block_comments( $content ) { + // Strip remaining block comment demarcations. - $rendered_content = preg_replace( '/\r?\n?/m', '', $rendered_content ); + $content = preg_replace_callback( '/\r?\n?/m', 'gutenberg_process_block_comment', $content ); - return $rendered_content; + return $content; } -add_filter( 'the_content', 'do_blocks', 9 ); // BEFORE do_shortcode(). + +function gutenberg_process_block_comment( $matches ) { + + $block_comment = $matches[0]; + + // Only process the block comment if it's not a closing tag for a block. If it's a closing tag, we can just return. + if ( preg_match( '/\/wp:/m', $block_comment ) !== 1 ) { + $matches2 = Array(); + + preg_match( '/wp:(.*?)\s+/m', $block_comment, $matches2); //Should it be ' +' or '/s+'. Research more about this. + + $block_type_name = $matches2[1]; + //WP_Parsed_Block_Types_Registry::get_instance()->add( $block_type_name ); + } + + return ''; +} + +add_filter( 'the_content', 'render_dynamic_blocks', 8 ); // BEFORE do_shortcode(). +add_filter( 'the_content', 'gutenberg_strip_block_comments', 9); \ No newline at end of file diff --git a/lib/load.php b/lib/load.php index 0da31fddf577cc..a693ea85d4f496 100644 --- a/lib/load.php +++ b/lib/load.php @@ -12,6 +12,7 @@ require dirname( __FILE__ ) . '/meta-box-partial-page.php'; require dirname( __FILE__ ) . '/class-wp-block-type.php'; require dirname( __FILE__ ) . '/class-wp-block-type-registry.php'; +require dirname( __FILE__ ) . '/class-wp-parsed-block-types-registry.php'; require dirname( __FILE__ ) . '/class-wp-rest-blocks-controller.php'; require dirname( __FILE__ ) . '/blocks.php'; require dirname( __FILE__ ) . '/client-assets.php'; From 855bb0e4520613b8618cd81944e1bcef42b149b9 Mon Sep 17 00:00:00 2001 From: Kanishk Dudeja Date: Mon, 9 Apr 2018 01:47:20 +0530 Subject: [PATCH 04/24] Added a function to prefix the core namespace to the block type Core block types parsed from the HTML may not contain 'core/'. Added a function gutenberg_prefix_core_namespace_if_not_found() to do the same. --- lib/blocks.php | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/lib/blocks.php b/lib/blocks.php index e0db3a8f2570fc..41cf20e65bb2fd 100644 --- a/lib/blocks.php +++ b/lib/blocks.php @@ -211,21 +211,44 @@ function gutenberg_strip_block_comments( $content ) { } function gutenberg_process_block_comment( $matches ) { - $block_comment = $matches[0]; // Only process the block comment if it's not a closing tag for a block. If it's a closing tag, we can just return. if ( preg_match( '/\/wp:/m', $block_comment ) !== 1 ) { - $matches2 = Array(); + + $match = Array(); - preg_match( '/wp:(.*?)\s+/m', $block_comment, $matches2); //Should it be ' +' or '/s+'. Research more about this. + preg_match( '/wp:(.*?)\s+/m', $block_comment, $match); //Should it be ' +' or '/s+'. Research more about this. - $block_type_name = $matches2[1]; + $block_type_name = $match[1]; + $block_type_name = gutenberg_prefix_core_namespace_if_not_found( $block_type_name ); + //WP_Parsed_Block_Types_Registry::get_instance()->add( $block_type_name ); } return ''; } +/** + * Prefixes 'core/' as the namespace to the block type name if the namespace isn't found in the block type name + * + * @since 2.6.0 + * + * @param string $block_type Name of the block type. + * @return string Name of the block type, prefixed by the core namespace if needed. + */ +function gutenberg_prefix_core_namespace_if_not_found( $block_type ) { + $block_type = trim( $block_type ); + + $index_of_slash = strpos( $block_type, '/' ); + + // If namespace isn't found in the block type name, prefix it with 'core/' + if ( $index_of_slash === FALSE ) { + return 'core/' . $block_type; + } + + return $block_type; +} + add_filter( 'the_content', 'render_dynamic_blocks', 8 ); // BEFORE do_shortcode(). add_filter( 'the_content', 'gutenberg_strip_block_comments', 9); \ No newline at end of file From d2d119e5a9230edf7a7bc8d3bd1fde1bbccfdf86 Mon Sep 17 00:00:00 2001 From: Kanishk Dudeja Date: Thu, 12 Apr 2018 02:33:48 +0530 Subject: [PATCH 05/24] WIP: Just adds a function gutenberg_enqueue_required_block_styles() This is work in progress and will be completely overhauled. Just committing it so that I don't lose it. --- lib/blocks.php | 2 +- lib/class-wp-parsed-block-types-registry.php | 4 +-- lib/client-assets.php | 27 ++++++++++++++++++++ 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/lib/blocks.php b/lib/blocks.php index 41cf20e65bb2fd..d414a79ac20b8f 100644 --- a/lib/blocks.php +++ b/lib/blocks.php @@ -223,7 +223,7 @@ function gutenberg_process_block_comment( $matches ) { $block_type_name = $match[1]; $block_type_name = gutenberg_prefix_core_namespace_if_not_found( $block_type_name ); - //WP_Parsed_Block_Types_Registry::get_instance()->add( $block_type_name ); + WP_Parsed_Block_Types_Registry::get_instance()->add( $block_type_name ); } return ''; diff --git a/lib/class-wp-parsed-block-types-registry.php b/lib/class-wp-parsed-block-types-registry.php index 00b438f5ae9203..e2a9a2c9954731 100644 --- a/lib/class-wp-parsed-block-types-registry.php +++ b/lib/class-wp-parsed-block-types-registry.php @@ -66,7 +66,7 @@ public function add( $block_type ) { } if ( ! WP_Block_Type_Registry::get_instance()->is_registered( $block_type ) ) { - /* translators: 1: block name */ + // translators: 1: block name $message = sprintf( __( 'Block type "%s" isn\'t registered yet.', 'gutenberg' ), $block_type ); _doing_it_wrong( __METHOD__, $message, '0.1.0' ); return false; @@ -85,7 +85,7 @@ public function add( $block_type ) { * * @return array Array of block types present on the web page being currently served (As an array of strings) */ - public function get_block_types_present_in_current_page() { + public function get_block_types_in_current_page() { return $this->block_types_in_current_page; } diff --git a/lib/client-assets.php b/lib/client-assets.php index 97ee28aefad14e..e39cded4f5f021 100644 --- a/lib/client-assets.php +++ b/lib/client-assets.php @@ -341,6 +341,7 @@ function gutenberg_register_scripts_and_styles() { filemtime( gutenberg_dir_path() . 'plugins/build/index.js' ) ); } + add_action( 'wp_enqueue_scripts', 'gutenberg_register_scripts_and_styles', 5 ); add_action( 'admin_enqueue_scripts', 'gutenberg_register_scripts_and_styles', 5 ); @@ -765,6 +766,32 @@ function gutenberg_enqueue_registered_block_scripts_and_styles() { } } } + +function gutenberg_enqueue_required_block_styles() { + + if( is_single() || is_page() ) { + + var_dump('here'); + + if( $isEnabled = true ) { + // Enable intelligent style loading + + $block_types_present = WP_Parsed_Block_Types_Registry::get_instance()->get_block_types_in_current_page(); + + foreach ( $block_types_present as $block_type_name ) { + $block_type_obj = WP_Block_Type_Registry::get_instance()->get_registered( $block_type_name ); + + if ( ! empty( $block_type_obj->style ) ) { + wp_enqueue_style( $block_type_obj->style ); + } + } + + return; + } + } +} + +add_action( 'the_content', 'gutenberg_enqueue_required_block_styles', 9); add_action( 'enqueue_block_assets', 'gutenberg_enqueue_registered_block_scripts_and_styles' ); add_action( 'enqueue_block_editor_assets', 'gutenberg_enqueue_registered_block_scripts_and_styles' ); From 76752c137ace95eb1411ac09b25a6fe20217700f Mon Sep 17 00:00:00 2001 From: Kanishk Dudeja Date: Fri, 13 Apr 2018 00:50:26 +0530 Subject: [PATCH 06/24] Added documentation blocks to functions responsible for stripping block comments --- lib/blocks.php | 89 ++++++++++++++++++++++++++++++------------- lib/client-assets.php | 2 - 2 files changed, 62 insertions(+), 29 deletions(-) diff --git a/lib/blocks.php b/lib/blocks.php index d414a79ac20b8f..39bb9a5028de1a 100644 --- a/lib/blocks.php +++ b/lib/blocks.php @@ -115,6 +115,27 @@ function gutenberg_render_block( $block ) { return ''; } +/** + * Prefixes the core namespace ('core/') to the block type name if the namespace isn't found in the block type name + * + * @since 2.7.0 + * + * @param string $block_type Name of the block type. + * @return string Name of the block type, prefixed by the core namespace if needed. + */ +function gutenberg_prefix_core_namespace_if_not_found( $block_type ) { + $block_type = trim( $block_type ); + + $index_of_slash = strpos( $block_type, '/' ); + + // If namespace isn't found in the block type name, prefix it with 'core/' + if ( $index_of_slash === FALSE ) { + return 'core/' . $block_type; + } + + return $block_type; +} + /** * Parses dynamic blocks out of `post_content` and re-renders them. * @@ -123,7 +144,8 @@ function gutenberg_render_block( $block ) { * @param string $content Post content. * @return string Updated post content. */ -function render_dynamic_blocks( $content ) { +function gutenberg_render_dynamic_blocks( $content ) { + $rendered_content = ''; $dynamic_block_names = get_dynamic_block_names(); @@ -202,14 +224,43 @@ function render_dynamic_blocks( $content ) { return $rendered_content; } +/** + * Strips block comments from the post's HTML. This is hooked as a filter + * to 'the_content' and gets executed after dynamic blocks are rendered in + * the post's HTML. + * + * It registers gutenberg_process_block_comment() as a callback function for + * preg_replace_callback(). For each block comment (matched by the Regex) in + * the post's HTML, gutenberg_process_block_comment() will get called. + * + * @since 2.7.0 + * + * @param string $content Post content. + * @return string Updated post content (without block comments) + */ function gutenberg_strip_block_comments( $content ) { - - // Strip remaining block comment demarcations. + $content = preg_replace_callback( '/\r?\n?/m', 'gutenberg_process_block_comment', $content ); return $content; } +/** + * Registered as a callback function to preg_replace_callback() and is called once for each + * block comment parsed from the post's HTML. It returns an empty string for each instantiation + * so that the post's HTML gets stripped of block comments. + * + * Also, it uses WP_Parsed_Block_Types_Registry to store block types parsed from the block comments. + * Those can be later used if needed. + * + * @since 2.7.0 + * + * @param string $matches An array filled with the results of search. + * $matches[0] will contain the text that matched the full pattern, + * $matches[1] will have the text that matched the first captured parenthesized subpattern, + * and so on. + * @return string Returns an empty string to preg_replace_callback() for each of the block comments + */ function gutenberg_process_block_comment( $matches ) { $block_comment = $matches[0]; @@ -218,7 +269,7 @@ function gutenberg_process_block_comment( $matches ) { $match = Array(); - preg_match( '/wp:(.*?)\s+/m', $block_comment, $match); //Should it be ' +' or '/s+'. Research more about this. + preg_match( '/wp:(.*?)\s+/m', $block_comment, $match); $block_type_name = $match[1]; $block_type_name = gutenberg_prefix_core_namespace_if_not_found( $block_type_name ); @@ -229,26 +280,10 @@ function gutenberg_process_block_comment( $matches ) { return ''; } -/** - * Prefixes 'core/' as the namespace to the block type name if the namespace isn't found in the block type name - * - * @since 2.6.0 - * - * @param string $block_type Name of the block type. - * @return string Name of the block type, prefixed by the core namespace if needed. - */ -function gutenberg_prefix_core_namespace_if_not_found( $block_type ) { - $block_type = trim( $block_type ); - - $index_of_slash = strpos( $block_type, '/' ); - - // If namespace isn't found in the block type name, prefix it with 'core/' - if ( $index_of_slash === FALSE ) { - return 'core/' . $block_type; - } - - return $block_type; -} - -add_filter( 'the_content', 'render_dynamic_blocks', 8 ); // BEFORE do_shortcode(). -add_filter( 'the_content', 'gutenberg_strip_block_comments', 9); \ No newline at end of file +/* + * If both filters have the same priority (9 in this case), they are executed + * in the order in which they were added to the action. Therefore, in this case, + * gutenberg_render_dynamic_blocks() will get executed before gutenberg_strip_block_comments() + */ +add_filter( 'the_content', 'gutenberg_render_dynamic_blocks', 9 ); // BEFORE do_shortcode(). +add_filter( 'the_content', 'gutenberg_strip_block_comments', 9); // AFTER the above filter \ No newline at end of file diff --git a/lib/client-assets.php b/lib/client-assets.php index e39cded4f5f021..7e23a1d0365c27 100644 --- a/lib/client-assets.php +++ b/lib/client-assets.php @@ -770,8 +770,6 @@ function gutenberg_enqueue_registered_block_scripts_and_styles() { function gutenberg_enqueue_required_block_styles() { if( is_single() || is_page() ) { - - var_dump('here'); if( $isEnabled = true ) { // Enable intelligent style loading From 8ad1ec1b4a9f5e59ecfe23ab919a4666fec18537 Mon Sep 17 00:00:00 2001 From: Kanishk Dudeja Date: Fri, 13 Apr 2018 02:27:14 +0530 Subject: [PATCH 07/24] Added logic for enqueueing required frontend styles if they need to be enqueued The new logic checks if the current requested web page is in the frontend. And if it's page or a post. If the answer is yes to both, it only enqueues styles for blocks which are present in the post/page. --- lib/client-assets.php | 49 +++++++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/lib/client-assets.php b/lib/client-assets.php index 7e23a1d0365c27..afa83e633d55e6 100644 --- a/lib/client-assets.php +++ b/lib/client-assets.php @@ -741,12 +741,21 @@ function gutenberg_common_scripts_and_styles() { * @since 2.0.0 */ function gutenberg_enqueue_registered_block_scripts_and_styles() { + $is_editor = ( 'enqueue_block_editor_assets' === current_action() ); + $is_frontend = ! $is_editor; + + $is_post_or_page = is_single() || is_page(); + + $enqueue_only_required_styles = $is_frontend && $is_post_or_page; + $enqueue_styles_for_all_blocks = ! $enqueue_only_required_styles; $block_registry = WP_Block_Type_Registry::get_instance(); + foreach ( $block_registry->get_all_registered() as $block_name => $block_type ) { + // Front-end styles. - if ( ! empty( $block_type->style ) ) { + if ( ! empty( $block_type->style ) && $enqueue_styles_for_all_blocks ) { wp_enqueue_style( $block_type->style ); } @@ -765,33 +774,37 @@ function gutenberg_enqueue_registered_block_scripts_and_styles() { wp_enqueue_script( $block_type->editor_script ); } } + + if ( $enqueue_only_required_styles ) { + add_action( 'the_content', 'enqueue_required_frontend_block_styles', 10 ); + } } -function gutenberg_enqueue_required_block_styles() { +add_action( 'enqueue_block_assets', 'gutenberg_enqueue_registered_block_scripts_and_styles' ); +add_action( 'enqueue_block_editor_assets', 'gutenberg_enqueue_registered_block_scripts_and_styles' ); - if( is_single() || is_page() ) { - - if( $isEnabled = true ) { - // Enable intelligent style loading +function enqueue_required_frontend_block_styles( $content ) { + + $all_block_types_registry = WP_Block_Type_Registry::get_instance(); + $parsed_block_types_registry = WP_Parsed_Block_Types_Registry::get_instance(); - $block_types_present = WP_Parsed_Block_Types_Registry::get_instance()->get_block_types_in_current_page(); + $block_types_in_current_page = $parsed_block_types_registry->get_block_types_in_current_page(); - foreach ( $block_types_present as $block_type_name ) { - $block_type_obj = WP_Block_Type_Registry::get_instance()->get_registered( $block_type_name ); + foreach ( $block_types_in_current_page as $block_type_name ) { + + $block_type = $all_block_types_registry->get_registered( $block_type_name ); - if ( ! empty( $block_type_obj->style ) ) { - wp_enqueue_style( $block_type_obj->style ); - } + if ( ! isset( $block_type ) ) { + // Log the error here + } else { + if ( isset( $block_type->style ) ) { + wp_enqueue_style( $block_type->style ); } - - return; } } -} -add_action( 'the_content', 'gutenberg_enqueue_required_block_styles', 9); -add_action( 'enqueue_block_assets', 'gutenberg_enqueue_registered_block_scripts_and_styles' ); -add_action( 'enqueue_block_editor_assets', 'gutenberg_enqueue_registered_block_scripts_and_styles' ); + return $content; +} /** * The code editor settings that were last captured by From c89713a451b06879b2e123fffd19e13155e94699 Mon Sep 17 00:00:00 2001 From: Kanishk Dudeja Date: Fri, 13 Apr 2018 13:33:22 +0530 Subject: [PATCH 08/24] Added documentation for enqueue_required_frontend_block_styles() --- lib/client-assets.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lib/client-assets.php b/lib/client-assets.php index fc79fc6ac7c695..fdf74194718527 100644 --- a/lib/client-assets.php +++ b/lib/client-assets.php @@ -789,6 +789,20 @@ function gutenberg_enqueue_registered_block_scripts_and_styles() { add_action( 'enqueue_block_assets', 'gutenberg_enqueue_registered_block_scripts_and_styles' ); add_action( 'enqueue_block_editor_assets', 'gutenberg_enqueue_registered_block_scripts_and_styles' ); +/** + * Enqueues frontend styles for block types present in the current page/post. + * This is hooked as a filter to 'the_content' and is executed after block comments + * are stripped from the HTML. + * + * It doesn't actually filter the post's content. It returns the post's content as is. + * It's only hooked as a filter so that it gets executed right after block comments are + * stripped from the HTML. This will make sure that we enqueue styles as early as we can. + * + * @since 2.7.0 + * + * @param string $content Post content + * @return string Post content returned as-is. + */ function enqueue_required_frontend_block_styles( $content ) { $all_block_types_registry = WP_Block_Type_Registry::get_instance(); From e3275b63cf57cbad6b9834bf214b25ef0684c68c Mon Sep 17 00:00:00 2001 From: Kanishk Dudeja Date: Sat, 14 Apr 2018 14:37:35 +0530 Subject: [PATCH 09/24] Removed trailing and unintended spaces using EditorConfig plugin --- lib/blocks.php | 22 ++++++++++---------- lib/class-wp-parsed-block-types-registry.php | 6 +++--- lib/client-assets.php | 8 +++---- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/lib/blocks.php b/lib/blocks.php index 39bb9a5028de1a..922643996bb9af 100644 --- a/lib/blocks.php +++ b/lib/blocks.php @@ -225,10 +225,10 @@ function gutenberg_render_dynamic_blocks( $content ) { } /** - * Strips block comments from the post's HTML. This is hooked as a filter + * Strips block comments from the post's HTML. This is hooked as a filter * to 'the_content' and gets executed after dynamic blocks are rendered in * the post's HTML. - * + * * It registers gutenberg_process_block_comment() as a callback function for * preg_replace_callback(). For each block comment (matched by the Regex) in * the post's HTML, gutenberg_process_block_comment() will get called. @@ -249,31 +249,31 @@ function gutenberg_strip_block_comments( $content ) { * Registered as a callback function to preg_replace_callback() and is called once for each * block comment parsed from the post's HTML. It returns an empty string for each instantiation * so that the post's HTML gets stripped of block comments. - * + * * Also, it uses WP_Parsed_Block_Types_Registry to store block types parsed from the block comments. * Those can be later used if needed. * * @since 2.7.0 * - * @param string $matches An array filled with the results of search. + * @param string $matches An array filled with the results of search. * $matches[0] will contain the text that matched the full pattern, * $matches[1] will have the text that matched the first captured parenthesized subpattern, - * and so on. + * and so on. * @return string Returns an empty string to preg_replace_callback() for each of the block comments */ function gutenberg_process_block_comment( $matches ) { $block_comment = $matches[0]; - - // Only process the block comment if it's not a closing tag for a block. If it's a closing tag, we can just return. + + // Only process the block comment if it's not a closing tag for a block. If it's a closing tag, we can just return. if ( preg_match( '/\/wp:/m', $block_comment ) !== 1 ) { - + $match = Array(); preg_match( '/wp:(.*?)\s+/m', $block_comment, $match); $block_type_name = $match[1]; $block_type_name = gutenberg_prefix_core_namespace_if_not_found( $block_type_name ); - + WP_Parsed_Block_Types_Registry::get_instance()->add( $block_type_name ); } @@ -284,6 +284,6 @@ function gutenberg_process_block_comment( $matches ) { * If both filters have the same priority (9 in this case), they are executed * in the order in which they were added to the action. Therefore, in this case, * gutenberg_render_dynamic_blocks() will get executed before gutenberg_strip_block_comments() - */ + */ add_filter( 'the_content', 'gutenberg_render_dynamic_blocks', 9 ); // BEFORE do_shortcode(). -add_filter( 'the_content', 'gutenberg_strip_block_comments', 9); // AFTER the above filter \ No newline at end of file +add_filter( 'the_content', 'gutenberg_strip_block_comments', 9); // AFTER the above filter diff --git a/lib/class-wp-parsed-block-types-registry.php b/lib/class-wp-parsed-block-types-registry.php index e2a9a2c9954731..6e6b28f93b0d33 100644 --- a/lib/class-wp-parsed-block-types-registry.php +++ b/lib/class-wp-parsed-block-types-registry.php @@ -41,7 +41,7 @@ class WP_Parsed_Block_Types_Registry { * @return true|false True on success, or false on failure. */ public function add( $block_type ) { - + if ( $block_type instanceof WP_Block_Type ) { $block_type = $block_type->name; } @@ -66,7 +66,7 @@ public function add( $block_type ) { } if ( ! WP_Block_Type_Registry::get_instance()->is_registered( $block_type ) ) { - // translators: 1: block name + // translators: 1: block name $message = sprintf( __( 'Block type "%s" isn\'t registered yet.', 'gutenberg' ), $block_type ); _doing_it_wrong( __METHOD__, $message, '0.1.0' ); return false; @@ -120,4 +120,4 @@ public static function get_instance() { return self::$instance; } -} \ No newline at end of file +} diff --git a/lib/client-assets.php b/lib/client-assets.php index fdf74194718527..75fbad6afb1cca 100644 --- a/lib/client-assets.php +++ b/lib/client-assets.php @@ -782,7 +782,7 @@ function gutenberg_enqueue_registered_block_scripts_and_styles() { } if ( $enqueue_only_required_styles ) { - add_action( 'the_content', 'enqueue_required_frontend_block_styles', 10 ); + add_action( 'the_content', 'enqueue_required_frontend_block_styles', 10 ); } } @@ -793,7 +793,7 @@ function gutenberg_enqueue_registered_block_scripts_and_styles() { * Enqueues frontend styles for block types present in the current page/post. * This is hooked as a filter to 'the_content' and is executed after block comments * are stripped from the HTML. - * + * * It doesn't actually filter the post's content. It returns the post's content as is. * It's only hooked as a filter so that it gets executed right after block comments are * stripped from the HTML. This will make sure that we enqueue styles as early as we can. @@ -804,14 +804,14 @@ function gutenberg_enqueue_registered_block_scripts_and_styles() { * @return string Post content returned as-is. */ function enqueue_required_frontend_block_styles( $content ) { - + $all_block_types_registry = WP_Block_Type_Registry::get_instance(); $parsed_block_types_registry = WP_Parsed_Block_Types_Registry::get_instance(); $block_types_in_current_page = $parsed_block_types_registry->get_block_types_in_current_page(); foreach ( $block_types_in_current_page as $block_type_name ) { - + $block_type = $all_block_types_registry->get_registered( $block_type_name ); if ( ! isset( $block_type ) ) { From 4beb6a1f1c994ff5ec7544a0d269ad343ee838c4 Mon Sep 17 00:00:00 2001 From: Kanishk Dudeja Date: Sat, 14 Apr 2018 14:48:36 +0530 Subject: [PATCH 10/24] Syntax fixes and renamed function which normalized block type Renamed gutenberg_prefix_core_namespace_if_not_found() to gutenberg_normalize_block_type() Removed '$match = Array()' since we don't need to declare a variable for preg_match() Fixed syntax to suit coding standards --- lib/blocks.php | 8 +++----- lib/client-assets.php | 10 ++++------ 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/lib/blocks.php b/lib/blocks.php index 922643996bb9af..507526bc99ecd4 100644 --- a/lib/blocks.php +++ b/lib/blocks.php @@ -123,7 +123,7 @@ function gutenberg_render_block( $block ) { * @param string $block_type Name of the block type. * @return string Name of the block type, prefixed by the core namespace if needed. */ -function gutenberg_prefix_core_namespace_if_not_found( $block_type ) { +function gutenberg_normalize_block_type( $block_type ) { $block_type = trim( $block_type ); $index_of_slash = strpos( $block_type, '/' ); @@ -267,12 +267,10 @@ function gutenberg_process_block_comment( $matches ) { // Only process the block comment if it's not a closing tag for a block. If it's a closing tag, we can just return. if ( preg_match( '/\/wp:/m', $block_comment ) !== 1 ) { - $match = Array(); - - preg_match( '/wp:(.*?)\s+/m', $block_comment, $match); + preg_match( '/wp:(.*?)\s+/m', $block_comment, $match ); $block_type_name = $match[1]; - $block_type_name = gutenberg_prefix_core_namespace_if_not_found( $block_type_name ); + $block_type_name = gutenberg_normalize_block_type( $block_type_name ); WP_Parsed_Block_Types_Registry::get_instance()->add( $block_type_name ); } diff --git a/lib/client-assets.php b/lib/client-assets.php index 75fbad6afb1cca..534a29d10f638c 100644 --- a/lib/client-assets.php +++ b/lib/client-assets.php @@ -749,11 +749,11 @@ function gutenberg_common_scripts_and_styles() { function gutenberg_enqueue_registered_block_scripts_and_styles() { $is_editor = ( 'enqueue_block_editor_assets' === current_action() ); - $is_frontend = ! $is_editor; + $is_front_end = ! $is_editor; $is_post_or_page = is_single() || is_page(); - $enqueue_only_required_styles = $is_frontend && $is_post_or_page; + $enqueue_only_required_styles = $is_front_end && $is_post_or_page; $enqueue_styles_for_all_blocks = ! $enqueue_only_required_styles; $block_registry = WP_Block_Type_Registry::get_instance(); @@ -816,10 +816,8 @@ function enqueue_required_frontend_block_styles( $content ) { if ( ! isset( $block_type ) ) { // Log the error here - } else { - if ( isset( $block_type->style ) ) { - wp_enqueue_style( $block_type->style ); - } + } else if ( isset( $block_type->style ) ) { + wp_enqueue_style( $block_type->style ); } } From 3cc30e4edd8d252bf9f3f5501e788e263a808624 Mon Sep 17 00:00:00 2001 From: Kanishk Dudeja Date: Sun, 15 Apr 2018 00:35:15 +0530 Subject: [PATCH 11/24] Basic framework for enqueueing styles for required block types in section --- lib/blocks.php | 10 +---- lib/class-wp-parsed-block-types-registry.php | 9 +++- lib/client-assets.php | 43 ++++++++++++-------- 3 files changed, 34 insertions(+), 28 deletions(-) diff --git a/lib/blocks.php b/lib/blocks.php index 507526bc99ecd4..4fd7d0792c14f2 100644 --- a/lib/blocks.php +++ b/lib/blocks.php @@ -128,8 +128,8 @@ function gutenberg_normalize_block_type( $block_type ) { $index_of_slash = strpos( $block_type, '/' ); - // If namespace isn't found in the block type name, prefix it with 'core/' - if ( $index_of_slash === FALSE ) { + // If namespace isn't found in the block type name, prefix it with 'core/'. + if ( false === $index_of_slash ) { return 'core/' . $block_type; } @@ -278,10 +278,4 @@ function gutenberg_process_block_comment( $matches ) { return ''; } -/* - * If both filters have the same priority (9 in this case), they are executed - * in the order in which they were added to the action. Therefore, in this case, - * gutenberg_render_dynamic_blocks() will get executed before gutenberg_strip_block_comments() - */ add_filter( 'the_content', 'gutenberg_render_dynamic_blocks', 9 ); // BEFORE do_shortcode(). -add_filter( 'the_content', 'gutenberg_strip_block_comments', 9); // AFTER the above filter diff --git a/lib/class-wp-parsed-block-types-registry.php b/lib/class-wp-parsed-block-types-registry.php index 6e6b28f93b0d33..2df7c0a76e6b7f 100644 --- a/lib/class-wp-parsed-block-types-registry.php +++ b/lib/class-wp-parsed-block-types-registry.php @@ -1,4 +1,10 @@ is_registered( $block_type ) ) { - // translators: 1: block name + // translators: 1: block name. $message = sprintf( __( 'Block type "%s" isn\'t registered yet.', 'gutenberg' ), $block_type ); _doing_it_wrong( __METHOD__, $message, '0.1.0' ); return false; diff --git a/lib/client-assets.php b/lib/client-assets.php index 534a29d10f638c..049290846092df 100644 --- a/lib/client-assets.php +++ b/lib/client-assets.php @@ -748,12 +748,12 @@ function gutenberg_common_scripts_and_styles() { */ function gutenberg_enqueue_registered_block_scripts_and_styles() { - $is_editor = ( 'enqueue_block_editor_assets' === current_action() ); + $is_editor = ( 'enqueue_block_editor_assets' === current_action() ); $is_front_end = ! $is_editor; $is_post_or_page = is_single() || is_page(); - $enqueue_only_required_styles = $is_front_end && $is_post_or_page; + $enqueue_only_required_styles = $is_front_end && $is_post_or_page; $enqueue_styles_for_all_blocks = ! $enqueue_only_required_styles; $block_registry = WP_Block_Type_Registry::get_instance(); @@ -782,30 +782,39 @@ function gutenberg_enqueue_registered_block_scripts_and_styles() { } if ( $enqueue_only_required_styles ) { - add_action( 'the_content', 'enqueue_required_frontend_block_styles', 10 ); + enqueue_required_frontend_block_styles(); } } +/** + * Parses block types present in the current post/page while stripping block comments. + * It calls apply_filters() on 'the_content' before starting to strip block comments so that + * block types added by plugins (through filters on 'the_content') can also be parsed. + * + * The gutenberg_strip_block_comments() function contains the actual logic for + * parsing and stripping block comments + * + * @since 2.7.0 + */ +function parse_block_types_while_stripping_comments() { + global $post; + + $post->post_content = apply_filters( 'the_content', $post->post_content ); + $post->post_content = gutenberg_strip_block_comments( $post->post_content ); +} + +add_action( 'enqueue_block_assets', 'parse_block_types_while_stripping_comments' ); add_action( 'enqueue_block_assets', 'gutenberg_enqueue_registered_block_scripts_and_styles' ); add_action( 'enqueue_block_editor_assets', 'gutenberg_enqueue_registered_block_scripts_and_styles' ); /** * Enqueues frontend styles for block types present in the current page/post. - * This is hooked as a filter to 'the_content' and is executed after block comments - * are stripped from the HTML. - * - * It doesn't actually filter the post's content. It returns the post's content as is. - * It's only hooked as a filter so that it gets executed right after block comments are - * stripped from the HTML. This will make sure that we enqueue styles as early as we can. * * @since 2.7.0 - * - * @param string $content Post content - * @return string Post content returned as-is. */ -function enqueue_required_frontend_block_styles( $content ) { +function enqueue_required_frontend_block_styles() { - $all_block_types_registry = WP_Block_Type_Registry::get_instance(); + $all_block_types_registry = WP_Block_Type_Registry::get_instance(); $parsed_block_types_registry = WP_Parsed_Block_Types_Registry::get_instance(); $block_types_in_current_page = $parsed_block_types_registry->get_block_types_in_current_page(); @@ -815,13 +824,11 @@ function enqueue_required_frontend_block_styles( $content ) { $block_type = $all_block_types_registry->get_registered( $block_type_name ); if ( ! isset( $block_type ) ) { - // Log the error here - } else if ( isset( $block_type->style ) ) { + // Log the error here. + } elseif ( isset( $block_type->style ) ) { wp_enqueue_style( $block_type->style ); } } - - return $content; } /** From 53049cc682b09437fc0b0a830962422a490aaac5 Mon Sep 17 00:00:00 2001 From: Kanishk Dudeja Date: Sun, 15 Apr 2018 18:31:12 +0530 Subject: [PATCH 12/24] Added a class WP_Block_Type_Validator for validating block types --- lib/class-wp-block-type-registry.php | 19 +-- lib/class-wp-block-type-validator.php | 119 +++++++++++++++++++ lib/class-wp-parsed-block-types-registry.php | 19 +-- lib/load.php | 1 + 4 files changed, 130 insertions(+), 28 deletions(-) create mode 100644 lib/class-wp-block-type-validator.php diff --git a/lib/class-wp-block-type-registry.php b/lib/class-wp-block-type-registry.php index 2e91b53933a72b..f89508a7ecf4cb 100644 --- a/lib/class-wp-block-type-registry.php +++ b/lib/class-wp-block-type-registry.php @@ -56,22 +56,13 @@ public function register( $name, $args = array() ) { $name = $block_type->name; } - if ( ! is_string( $name ) ) { - $message = __( 'Block type names must be strings.', 'gutenberg' ); - _doing_it_wrong( __METHOD__, $message, '0.1.0' ); - return false; - } + $block_type_validator = new WP_Block_Type_Validator(); + $is_block_type_valid = $block_type_validator->validate( $name ); - if ( preg_match( '/[A-Z]+/', $name ) ) { - $message = __( 'Block type names must not contain uppercase characters.', 'gutenberg' ); - _doing_it_wrong( __METHOD__, $message, '1.5.0' ); - return false; - } + if ( ! $is_block_type_valid ) { + $error_message = $block_type_validator->get_last_error(); + _doing_it_wrong( __METHOD__, $error_message, '2.7.0' ); - $name_matcher = '/^[a-z0-9-]+\/[a-z0-9-]+$/'; - if ( ! preg_match( $name_matcher, $name ) ) { - $message = __( 'Block type names must contain a namespace prefix. Example: my-plugin/my-custom-block-type', 'gutenberg' ); - _doing_it_wrong( __METHOD__, $message, '0.1.0' ); return false; } diff --git a/lib/class-wp-block-type-validator.php b/lib/class-wp-block-type-validator.php new file mode 100644 index 00000000000000..6b6232bc9849a8 --- /dev/null +++ b/lib/class-wp-block-type-validator.php @@ -0,0 +1,119 @@ +name; + } + + if ( ! is_string( $name ) ) { + $message = __( 'Block type names must be strings.', 'gutenberg' ); + + $this->set_error( $message ); + _doing_it_wrong( __METHOD__, $message, '0.1.0' ); + return false; + } + + if ( preg_match( '/[A-Z]+/', $name ) ) { + $message = __( 'Block type names must not contain uppercase characters.', 'gutenberg' ); + $this->set_error( $message ); + + _doing_it_wrong( __METHOD__, $message, '1.5.0' ); + return false; + } + + $name_matcher = '/^[a-z0-9-]+\/[a-z0-9-]+$/'; + if ( ! preg_match( $name_matcher, $name ) ) { + $message = __( 'Block type names must contain a namespace prefix. Example: my-plugin/my-custom-block-type', 'gutenberg' ); + $this->set_error( $message ); + + _doing_it_wrong( __METHOD__, $message, '0.1.0' ); + return false; + } + + return true; + } + + /** + * Set an error in the validator + * + * This function can be used to set an error in the Validator + * Please note that this function adds the error to the existing $this->errors array + * It does not flush the existing errors object + * + * @param string $error_text A string denoting the error. + */ + public function set_error( $error_text ) { + $this->errors[] = $error_text; + } + + /** + * Checks if the Validator encountered any errors in validation + * + * This function checks $this->errors array and returns true if there are any errors in it. + * If yes, it returns true. If no, it returns false. + * + * @return bool True/False + */ + public function has_errors() { + return ! empty( $this->errors ) ? true : false; + } + + /** + * Get errors stored in the Validator + * + * This function returns the $this->errors array + * + * @return array An array is returned containing the errors stored in the Validator. + * The returned array is an array of strings (each error is denoted as a string) + * If there are no errors stored, an empty array is returned + */ + public function get_errors() { + return $this->errors; + } + + /** + * Get the last error encountered by the Validator + * + * @return string|bool A string denoting the last error is returned. If there are no errors stored in the validator, FALSE (boolean) is returned + */ + public function get_last_error() { + $error_count = count( $this->errors ); + + if ( 0 === $error_count ) { + return false; + } else { + return $errors[ $error_count - 1 ]; + } + } +} diff --git a/lib/class-wp-parsed-block-types-registry.php b/lib/class-wp-parsed-block-types-registry.php index 2df7c0a76e6b7f..53f06c407b43d0 100644 --- a/lib/class-wp-parsed-block-types-registry.php +++ b/lib/class-wp-parsed-block-types-registry.php @@ -51,22 +51,13 @@ public function add( $block_type ) { $block_type = $block_type->name; } - if ( ! is_string( $block_type ) ) { - $message = __( 'Block type names must be strings.', 'gutenberg' ); - _doing_it_wrong( __METHOD__, $message, '2.6.0' ); - return false; - } + $block_type_validator = new WP_Block_Type_Validator(); + $is_block_type_valid = $block_type_validator->validate( $block_type ); - if ( preg_match( '/[A-Z]+/', $block_type ) ) { - $message = __( 'Block type names must not contain uppercase characters.', 'gutenberg' ); - _doing_it_wrong( __METHOD__, $message, '2.6.0' ); - return false; - } + if ( ! $is_block_type_valid ) { + $error_message = $block_type_validator->get_last_error(); + _doing_it_wrong( __METHOD__, $error_message, '2.7.0' ); - $name_matcher = '/^[a-z0-9-]+\/[a-z0-9-]+$/'; - if ( ! preg_match( $name_matcher, $block_type ) ) { - $message = __( 'Block type names must contain a namespace prefix. Example: my-plugin/my-custom-block-type', 'gutenberg' ); - _doing_it_wrong( __METHOD__, $message, '2.6.0' ); return false; } diff --git a/lib/load.php b/lib/load.php index a693ea85d4f496..bff46a8c432fa8 100644 --- a/lib/load.php +++ b/lib/load.php @@ -11,6 +11,7 @@ require dirname( __FILE__ ) . '/meta-box-partial-page.php'; require dirname( __FILE__ ) . '/class-wp-block-type.php'; +require dirname( __FILE__ ) . '/class-wp-block-type-validator.php'; require dirname( __FILE__ ) . '/class-wp-block-type-registry.php'; require dirname( __FILE__ ) . '/class-wp-parsed-block-types-registry.php'; require dirname( __FILE__ ) . '/class-wp-rest-blocks-controller.php'; From e96e8611914aec7cf71518eaa67df86013b9318d Mon Sep 17 00:00:00 2001 From: Kanishk Dudeja Date: Sun, 15 Apr 2018 22:56:18 +0530 Subject: [PATCH 13/24] Disabled stripping of dynamic block comments while rendering dynamic blocks Our rendering algorithm for dynamic blocks was stripping block comments after rendering those dynamic blocks. Disabled that functionality since block comments are needed to find out block types present in the page later. --- lib/blocks.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/blocks.php b/lib/blocks.php index 4fd7d0792c14f2..e7659dcb8c5bf6 100644 --- a/lib/blocks.php +++ b/lib/blocks.php @@ -176,8 +176,8 @@ function gutenberg_render_dynamic_blocks( $content ) { } // Since content is a working copy since the last match, append to - // rendered content up to the matched offset... - $rendered_content .= substr( $content, 0, $offset ); + // rendered content up to the matched offset (including the opening tag)... + $rendered_content .= substr( $content, 0, $offset + strlen( $opening_tag ) ); // ...then update the working copy of content. $content = substr( $content, $offset + strlen( $opening_tag ) ); @@ -210,11 +210,11 @@ function gutenberg_render_dynamic_blocks( $content ) { break; } - // Update content to omit text up to and including closing tag. + // Update content to omit text up to the closing tag. $end_tag = $block_match_end[0][0]; $end_offset = $block_match_end[0][1]; - $content = substr( $content, $end_offset + strlen( $end_tag ) ); + $content = substr( $content, $end_offset ); } } From 59b1cd1af7c2bcd49fbb2b027490f63e5f323baa Mon Sep 17 00:00:00 2001 From: Kanishk Dudeja Date: Mon, 16 Apr 2018 00:59:43 +0530 Subject: [PATCH 14/24] Added a check to only add unique block types to parsed block types registry --- lib/class-wp-parsed-block-types-registry.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/class-wp-parsed-block-types-registry.php b/lib/class-wp-parsed-block-types-registry.php index 53f06c407b43d0..a58e6d384d0a4a 100644 --- a/lib/class-wp-parsed-block-types-registry.php +++ b/lib/class-wp-parsed-block-types-registry.php @@ -68,7 +68,9 @@ public function add( $block_type ) { return false; } - $this->block_types_in_current_page[] = $block_type; + if ( ! in_array( $block_type, $this->block_types_in_current_page ) ) { + array_push( $this->block_types_in_current_page, $block_type ); + } return true; } From f00369bf302aee2f11dd2c4520625b1c7c98012a Mon Sep 17 00:00:00 2001 From: Kanishk Dudeja Date: Mon, 16 Apr 2018 02:32:27 +0530 Subject: [PATCH 15/24] Fixed an error in get_last_error() function in Block Type Validator get_last_error() function in Block Type Validator was referencing $errors as a local variable, whereas it should have referenced it as a class variable. Have fixed it in this commit. --- lib/class-wp-block-type-validator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/class-wp-block-type-validator.php b/lib/class-wp-block-type-validator.php index 6b6232bc9849a8..b43726eb8b9165 100644 --- a/lib/class-wp-block-type-validator.php +++ b/lib/class-wp-block-type-validator.php @@ -113,7 +113,7 @@ public function get_last_error() { if ( 0 === $error_count ) { return false; } else { - return $errors[ $error_count - 1 ]; + return $this->errors[ $error_count - 1 ]; } } } From 65bcbd67377bf773b7a6000b9758ab8f91c71103 Mon Sep 17 00:00:00 2001 From: Kanishk Dudeja Date: Mon, 16 Apr 2018 02:35:47 +0530 Subject: [PATCH 16/24] Fixed unit tests for dynamic block rendering and stripping block types --- phpunit/class-do-blocks-test.php | 25 -------------------- phpunit/class-dynamic-blocks-render-test.php | 12 +++++----- phpunit/class-strip-block-comments-test.php | 25 ++++++++++++++++++++ 3 files changed, 31 insertions(+), 31 deletions(-) delete mode 100644 phpunit/class-do-blocks-test.php create mode 100644 phpunit/class-strip-block-comments-test.php diff --git a/phpunit/class-do-blocks-test.php b/phpunit/class-do-blocks-test.php deleted file mode 100644 index ec7cd97c39bbdf..00000000000000 --- a/phpunit/class-do-blocks-test.php +++ /dev/null @@ -1,25 +0,0 @@ -assertEquals( $expected_html, $actual_html ); - } -} diff --git a/phpunit/class-dynamic-blocks-render-test.php b/phpunit/class-dynamic-blocks-render-test.php index a642184fc99a5b..81801696028712 100644 --- a/phpunit/class-dynamic-blocks-render-test.php +++ b/phpunit/class-dynamic-blocks-render-test.php @@ -53,7 +53,7 @@ function tearDown() { /** * Test dynamic blocks that lack content, including void blocks. * - * @covers ::do_blocks + * @covers ::gutenberg_render_dynamic_blocks */ function test_dynamic_block_rendering() { $settings = array( @@ -74,14 +74,14 @@ function test_dynamic_block_rendering() { '' . 'after'; - $updated_post_content = do_blocks( $post_content ); + $updated_post_content = gutenberg_render_dynamic_blocks( $post_content ); $this->assertEquals( $updated_post_content, 'before' . - '1:b1' . - '2:b1' . + '' . '1:b1' . '' . + '' . '2:b1' . '' . 'between' . - '3:b2' . - '4:b2' . + '' . '3:b2' . + '' . '4:b2' . 'after' ); } diff --git a/phpunit/class-strip-block-comments-test.php b/phpunit/class-strip-block-comments-test.php new file mode 100644 index 00000000000000..520129bf7786fb --- /dev/null +++ b/phpunit/class-strip-block-comments-test.php @@ -0,0 +1,25 @@ +assertEquals( $expected_html, $actual_html ); + } +} From 3669dc61bfb04213fc8da5f92c0a84707705fb17 Mon Sep 17 00:00:00 2001 From: Kanishk Dudeja Date: Mon, 16 Apr 2018 02:52:05 +0530 Subject: [PATCH 17/24] Removed _doing_it_wrong() notices from Block Type validator class Removed _doing_it_wrong() notices from Block Type validator class since the same are being called in the caller class. --- lib/class-wp-block-type-validator.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/class-wp-block-type-validator.php b/lib/class-wp-block-type-validator.php index b43726eb8b9165..229e80059a4990 100644 --- a/lib/class-wp-block-type-validator.php +++ b/lib/class-wp-block-type-validator.php @@ -38,9 +38,8 @@ public function validate( $name ) { if ( ! is_string( $name ) ) { $message = __( 'Block type names must be strings.', 'gutenberg' ); - $this->set_error( $message ); - _doing_it_wrong( __METHOD__, $message, '0.1.0' ); + return false; } @@ -48,7 +47,6 @@ public function validate( $name ) { $message = __( 'Block type names must not contain uppercase characters.', 'gutenberg' ); $this->set_error( $message ); - _doing_it_wrong( __METHOD__, $message, '1.5.0' ); return false; } @@ -57,7 +55,6 @@ public function validate( $name ) { $message = __( 'Block type names must contain a namespace prefix. Example: my-plugin/my-custom-block-type', 'gutenberg' ); $this->set_error( $message ); - _doing_it_wrong( __METHOD__, $message, '0.1.0' ); return false; } From 7960d223265e940d5234a611ae879b79c5ac6e5c Mon Sep 17 00:00:00 2001 From: Kanishk Dudeja Date: Mon, 16 Apr 2018 14:35:17 +0530 Subject: [PATCH 18/24] Renamed gutenberg_strip_block_comments to gutenberg_process_block_comments and other changes Renamed gutenberg_strip_block_comments to gutenberg_process_block_comments Used gutenberg_normalize_block_type() in function which renders dynamic blocks Improved documentation blocks for some functions --- lib/blocks.php | 10 ++++------ lib/client-assets.php | 16 +++++++--------- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/lib/blocks.php b/lib/blocks.php index e7659dcb8c5bf6..018342717ad2ec 100644 --- a/lib/blocks.php +++ b/lib/blocks.php @@ -183,8 +183,7 @@ function gutenberg_render_dynamic_blocks( $content ) { $content = substr( $content, $offset + strlen( $opening_tag ) ); // Make implicit core namespace explicit. - $is_implicit_core_namespace = ( false === strpos( $block_name, '/' ) ); - $normalized_block_name = $is_implicit_core_namespace ? 'core/' . $block_name : $block_name; + $normalized_block_name = gutenberg_normalize_block_type( $block_name ); // Find registered block type. We can assume it exists since we use the // `get_dynamic_block_names` function as a source for pattern matching. @@ -225,9 +224,8 @@ function gutenberg_render_dynamic_blocks( $content ) { } /** - * Strips block comments from the post's HTML. This is hooked as a filter - * to 'the_content' and gets executed after dynamic blocks are rendered in - * the post's HTML. + * Parses block types while stripping block comments from the post's HTML and + * calls WP_Parsed_Block_Types_Registry to save those parsed block types. * * It registers gutenberg_process_block_comment() as a callback function for * preg_replace_callback(). For each block comment (matched by the Regex) in @@ -238,7 +236,7 @@ function gutenberg_render_dynamic_blocks( $content ) { * @param string $content Post content. * @return string Updated post content (without block comments) */ -function gutenberg_strip_block_comments( $content ) { +function gutenberg_process_block_comments( $content ) { $content = preg_replace_callback( '/\r?\n?/m', 'gutenberg_process_block_comment', $content ); diff --git a/lib/client-assets.php b/lib/client-assets.php index 049290846092df..5310d148fdf2f0 100644 --- a/lib/client-assets.php +++ b/lib/client-assets.php @@ -787,23 +787,21 @@ function gutenberg_enqueue_registered_block_scripts_and_styles() { } /** - * Parses block types present in the current post/page while stripping block comments. - * It calls apply_filters() on 'the_content' before starting to strip block comments so that - * block types added by plugins (through filters on 'the_content') can also be parsed. - * - * The gutenberg_strip_block_comments() function contains the actual logic for - * parsing and stripping block comments + * Calls gutenberg_process_block_comments() to parse block types present in the + * current post/page while stripping block comments. It calls apply_filters() on + * 'the_content' before starting to strip block comments so that block types added by + * plugins (through filters added uptil now on 'the_content') can also be parsed. * * @since 2.7.0 */ -function parse_block_types_while_stripping_comments() { +function gutenberg_trigger_block_comments_processing() { global $post; $post->post_content = apply_filters( 'the_content', $post->post_content ); - $post->post_content = gutenberg_strip_block_comments( $post->post_content ); + $post->post_content = gutenberg_process_block_comments( $post->post_content ); } -add_action( 'enqueue_block_assets', 'parse_block_types_while_stripping_comments' ); +add_action( 'enqueue_block_assets', 'gutenberg_trigger_block_comments_processing' ); add_action( 'enqueue_block_assets', 'gutenberg_enqueue_registered_block_scripts_and_styles' ); add_action( 'enqueue_block_editor_assets', 'gutenberg_enqueue_registered_block_scripts_and_styles' ); From ec63723cfc0305a175b6e578101ae8fbc58f7a53 Mon Sep 17 00:00:00 2001 From: Kanishk Dudeja Date: Mon, 16 Apr 2018 15:58:38 +0530 Subject: [PATCH 19/24] Called gutenberg_trigger_block_comments_processing() from the function which enqueues scripts and style handles --- lib/client-assets.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/client-assets.php b/lib/client-assets.php index 5310d148fdf2f0..05e0676d44b5e4 100644 --- a/lib/client-assets.php +++ b/lib/client-assets.php @@ -753,6 +753,11 @@ function gutenberg_enqueue_registered_block_scripts_and_styles() { $is_post_or_page = is_single() || is_page(); + // Triggers parsing of post's content so as to save block types present in the post and to strip block comments from the HTML. + if ( $is_post_or_page ) { + gutenberg_trigger_block_comments_processing(); + } + $enqueue_only_required_styles = $is_front_end && $is_post_or_page; $enqueue_styles_for_all_blocks = ! $enqueue_only_required_styles; @@ -801,7 +806,6 @@ function gutenberg_trigger_block_comments_processing() { $post->post_content = gutenberg_process_block_comments( $post->post_content ); } -add_action( 'enqueue_block_assets', 'gutenberg_trigger_block_comments_processing' ); add_action( 'enqueue_block_assets', 'gutenberg_enqueue_registered_block_scripts_and_styles' ); add_action( 'enqueue_block_editor_assets', 'gutenberg_enqueue_registered_block_scripts_and_styles' ); From 93b61ee944d362d994adc6733abd921167b2b101 Mon Sep 17 00:00:00 2001 From: Kanishk Dudeja Date: Mon, 16 Apr 2018 17:21:51 +0530 Subject: [PATCH 20/24] Used is_singular() to check if current requested page is a page or a post --- lib/client-assets.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/client-assets.php b/lib/client-assets.php index 05e0676d44b5e4..7df6ffa7f7e84b 100644 --- a/lib/client-assets.php +++ b/lib/client-assets.php @@ -751,7 +751,7 @@ function gutenberg_enqueue_registered_block_scripts_and_styles() { $is_editor = ( 'enqueue_block_editor_assets' === current_action() ); $is_front_end = ! $is_editor; - $is_post_or_page = is_single() || is_page(); + $is_post_or_page = is_singular( array( 'post', 'page') ); // Triggers parsing of post's content so as to save block types present in the post and to strip block comments from the HTML. if ( $is_post_or_page ) { From 5d43d139e2c7d0bf92e03965c3d8c7d0b760dbd0 Mon Sep 17 00:00:00 2001 From: Kanishk Dudeja Date: Mon, 16 Apr 2018 17:27:31 +0530 Subject: [PATCH 21/24] Coding standard: Added space before closing the array bracket --- lib/client-assets.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/client-assets.php b/lib/client-assets.php index 7df6ffa7f7e84b..039db7e243ecef 100644 --- a/lib/client-assets.php +++ b/lib/client-assets.php @@ -751,7 +751,7 @@ function gutenberg_enqueue_registered_block_scripts_and_styles() { $is_editor = ( 'enqueue_block_editor_assets' === current_action() ); $is_front_end = ! $is_editor; - $is_post_or_page = is_singular( array( 'post', 'page') ); + $is_post_or_page = is_singular( array( 'post', 'page' ) ); // Triggers parsing of post's content so as to save block types present in the post and to strip block comments from the HTML. if ( $is_post_or_page ) { From 647a095beea6af7c64fafad64a31aa810ec607bb Mon Sep 17 00:00:00 2001 From: Kanishk Dudeja Date: Mon, 16 Apr 2018 22:32:50 +0530 Subject: [PATCH 22/24] Added $added_from_version field to Errors in Block type validator --- lib/class-wp-block-type-registry.php | 4 +-- lib/class-wp-block-type-validator.php | 28 +++++++++++++------- lib/class-wp-parsed-block-types-registry.php | 4 +-- 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/lib/class-wp-block-type-registry.php b/lib/class-wp-block-type-registry.php index f89508a7ecf4cb..79d855be7cde60 100644 --- a/lib/class-wp-block-type-registry.php +++ b/lib/class-wp-block-type-registry.php @@ -60,8 +60,8 @@ public function register( $name, $args = array() ) { $is_block_type_valid = $block_type_validator->validate( $name ); if ( ! $is_block_type_valid ) { - $error_message = $block_type_validator->get_last_error(); - _doing_it_wrong( __METHOD__, $error_message, '2.7.0' ); + $error = $block_type_validator->get_last_error(); ++ _doing_it_wrong( __METHOD__, $error['error_text'], $error['added_from_version'] ); return false; } diff --git a/lib/class-wp-block-type-validator.php b/lib/class-wp-block-type-validator.php index 229e80059a4990..ed51bf6e7d5977 100644 --- a/lib/class-wp-block-type-validator.php +++ b/lib/class-wp-block-type-validator.php @@ -14,7 +14,9 @@ class WP_Block_Type_Validator { /** - * Errors array + * A container (array) for storing errors encountered by the validator. + * + * Each error is itself an array with `error_text` and `added_from_version` keys. * * @var array $errors Array for storing errors encountered by the Validator */ @@ -38,14 +40,14 @@ public function validate( $name ) { if ( ! is_string( $name ) ) { $message = __( 'Block type names must be strings.', 'gutenberg' ); - $this->set_error( $message ); + $this->set_error( $message, '0.1.0' ); return false; } if ( preg_match( '/[A-Z]+/', $name ) ) { $message = __( 'Block type names must not contain uppercase characters.', 'gutenberg' ); - $this->set_error( $message ); + $this->set_error( $message, '1.5.0' ); return false; } @@ -53,7 +55,7 @@ public function validate( $name ) { $name_matcher = '/^[a-z0-9-]+\/[a-z0-9-]+$/'; if ( ! preg_match( $name_matcher, $name ) ) { $message = __( 'Block type names must contain a namespace prefix. Example: my-plugin/my-custom-block-type', 'gutenberg' ); - $this->set_error( $message ); + $this->set_error( $message, '0.1.0' ); return false; } @@ -68,10 +70,14 @@ public function validate( $name ) { * Please note that this function adds the error to the existing $this->errors array * It does not flush the existing errors object * - * @param string $error_text A string denoting the error. + * @param string $error_text A string denoting the error message. + * @param string $added_from_version A string denoting the version of WordPress where the error message was added. */ - public function set_error( $error_text ) { - $this->errors[] = $error_text; + public function set_error( $error_text, $added_from_version = '' ) { + $this->errors[] = array( + 'error_text' => $error_text, + 'added_from_version' => $added_from_version, + ); } /** @@ -92,8 +98,9 @@ public function has_errors() { * This function returns the $this->errors array * * @return array An array is returned containing the errors stored in the Validator. - * The returned array is an array of strings (each error is denoted as a string) - * If there are no errors stored, an empty array is returned + * The returned array is an array of errors. + * Each individual error is an array with `error_text` and `added_from_version` keys. + * If there are no errors stored, an empty array is returned */ public function get_errors() { return $this->errors; @@ -102,7 +109,8 @@ public function get_errors() { /** * Get the last error encountered by the Validator * - * @return string|bool A string denoting the last error is returned. If there are no errors stored in the validator, FALSE (boolean) is returned + * @return array|bool An array containing `error_text` and `added_from_version` keys + * If there are no errors stored in the validator, FALSE (boolean) is returned */ public function get_last_error() { $error_count = count( $this->errors ); diff --git a/lib/class-wp-parsed-block-types-registry.php b/lib/class-wp-parsed-block-types-registry.php index a58e6d384d0a4a..8657d6ca758f56 100644 --- a/lib/class-wp-parsed-block-types-registry.php +++ b/lib/class-wp-parsed-block-types-registry.php @@ -55,8 +55,8 @@ public function add( $block_type ) { $is_block_type_valid = $block_type_validator->validate( $block_type ); if ( ! $is_block_type_valid ) { - $error_message = $block_type_validator->get_last_error(); - _doing_it_wrong( __METHOD__, $error_message, '2.7.0' ); + $error = $block_type_validator->get_last_error(); ++ _doing_it_wrong( __METHOD__, $error['error_text'], $error['added_from_version'] ); return false; } From 9b29c107da8ba7c7f9b3e0d4b4ac2a237ec7ba98 Mon Sep 17 00:00:00 2001 From: Kanishk Dudeja Date: Wed, 18 Apr 2018 02:29:45 +0530 Subject: [PATCH 23/24] Disabled block type parsing from HTML for pages listing multiple posts Earlier, we were parsing block types from the HTML and saving them to the registry even for pages like category / archive pages (which contain multiple posts). Have disabled this functionality for such pages now since the registry is only needed for intelligent enqueueing of assets (which is needed for single post / page types as of now). --- lib/blocks.php | 30 ++++++++++++++++++++++++------ lib/client-assets.php | 35 ++++++++++++++++++++++++----------- 2 files changed, 48 insertions(+), 17 deletions(-) diff --git a/lib/blocks.php b/lib/blocks.php index 018342717ad2ec..305079ee17cc96 100644 --- a/lib/blocks.php +++ b/lib/blocks.php @@ -224,12 +224,18 @@ function gutenberg_render_dynamic_blocks( $content ) { } /** - * Parses block types while stripping block comments from the post's HTML and - * calls WP_Parsed_Block_Types_Registry to save those parsed block types. + * For a single post / page, this function parses `block types` while stripping + * `block comments` from the post's HTML and calls WP_Parsed_Block_Types_Registry + * to save those parsed block types. It registers gutenberg_process_block_comment() + * as a callback function for preg_replace_callback(). For each block comment + * (matched by the Regex) in the post's HTML, gutenberg_process_block_comment() + * will get called. * - * It registers gutenberg_process_block_comment() as a callback function for - * preg_replace_callback(). For each block comment (matched by the Regex) in - * the post's HTML, gutenberg_process_block_comment() will get called. + * For pages containing multiple posts like category / archive / home page, this only strips + * block comments (doesn't save the parsed block types in WP_Parsed_Block_Types_Registry). + * We don't need to save `parsed block types` for such pages since intelligent enqueuing of + * front-end styles (only enqueue styles for `block types` present in the post/page) + * only needs to happen for single posts / pages as of now. * * @since 2.7.0 * @@ -238,7 +244,19 @@ function gutenberg_render_dynamic_blocks( $content ) { */ function gutenberg_process_block_comments( $content ) { - $content = preg_replace_callback( '/\r?\n?/m', 'gutenberg_process_block_comment', $content ); + // Checks if a single page/post is requested. + $is_post_or_page = is_singular( array( 'post', 'page' ) ); + + /* + * If a single page/post is requested, we need to parse and save `parsed block types` while stripping block comments. + * Otherwise, for category / archive / home page etc, we can simply strip block comments and return the HTML. + */ + + if ( $is_post_or_page ) { + $content = preg_replace_callback( '/\r?\n?/m', 'gutenberg_process_block_comment', $content ); + } else { + $content = preg_replace( '/\r?\n?/m', '', $content ); + } return $content; } diff --git a/lib/client-assets.php b/lib/client-assets.php index fd50ab169665d0..5ca364e8286457 100644 --- a/lib/client-assets.php +++ b/lib/client-assets.php @@ -753,10 +753,8 @@ function gutenberg_enqueue_registered_block_scripts_and_styles() { $is_post_or_page = is_singular( array( 'post', 'page' ) ); - // Triggers parsing of post's content so as to save block types present in the post and to strip block comments from the HTML. - if ( $is_post_or_page ) { - gutenberg_trigger_block_comments_processing(); - } + // Triggers processing of block comments in the HTML. + gutenberg_trigger_block_comments_processing(); $enqueue_only_required_styles = $is_front_end && $is_post_or_page; $enqueue_styles_for_all_blocks = ! $enqueue_only_required_styles; @@ -792,18 +790,33 @@ function gutenberg_enqueue_registered_block_scripts_and_styles() { } /** - * Calls gutenberg_process_block_comments() to parse block types present in the - * current post/page while stripping block comments. It calls apply_filters() on - * 'the_content' before starting to strip block comments so that block types added by - * plugins (through filters added uptil now on 'the_content') can also be parsed. + * For a single post / page, it calls gutenberg_process_block_comments() which parses + * `block types` present in the current post/page while stripping `block comments`. + * It calls apply_filters() on 'the_content' before starting to strip block comments + * so that block types added by plugins (through filters added uptil now on 'the_content') + * can also be parsed. + * + * For pages with multiple posts like category / archive / home page, it just adds + * gutenberg_process_block_comments() as a filter on `the_content`'. In that case, + * gutenberg_process_block_comments() only strips block comments from the HTML. + * We don't need to save `parsed block types` in that case since that's only needed + * for intelligent enqueueing of styles right now and because intelligent enqueueing of + * styles only needs to be enabled for single posts / pages right now. * * @since 2.7.0 */ function gutenberg_trigger_block_comments_processing() { - global $post; - $post->post_content = apply_filters( 'the_content', $post->post_content ); - $post->post_content = gutenberg_process_block_comments( $post->post_content ); + $is_post_or_page = is_singular( array( 'post', 'page' ) ); + + if ( $is_post_or_page ) { + global $post; + + $post->post_content = apply_filters( 'the_content', $post->post_content ); + $post->post_content = gutenberg_process_block_comments( $post->post_content ); + } else { + add_filter( 'the_content', 'gutenberg_process_block_comments', 10 ); + } } add_action( 'enqueue_block_assets', 'gutenberg_enqueue_registered_block_scripts_and_styles' ); From c25addc678a8f217f4081e82707e87287a8711c3 Mon Sep 17 00:00:00 2001 From: Kanishk Dudeja Date: Wed, 18 Apr 2018 14:36:12 +0530 Subject: [PATCH 24/24] Improved documentation for class WP_Parsed_Block_Types_Registry --- lib/class-wp-parsed-block-types-registry.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/class-wp-parsed-block-types-registry.php b/lib/class-wp-parsed-block-types-registry.php index 8657d6ca758f56..1d73726cf8eb3d 100644 --- a/lib/class-wp-parsed-block-types-registry.php +++ b/lib/class-wp-parsed-block-types-registry.php @@ -7,8 +7,15 @@ */ /** - * Core class used for storing a list of block types present in the page being currently served - * This list of blocks is populated while stripping block comments from the HTML. + * Core class used for storing a list of `block types` present in the currently requested post / page. + * This list of `block types` is populated while stripping block comments from the post's HTML. + * + * This registry is later used for intelligent enqueueing of front-end styles (enqueue front-end styles + * for only `block types` present in the currently requested post / page) + * + * This registry is only populated if the current queried object is a a single post / page. + * For pages containing multiple posts like `category / archive / home page`, this registry isn't populated + * since we don't need to enqueue front-end styles on these pages intelligently. * * @package gutenberg * @since 2.6.0