]*>)(.*?)(<\/a>)/' );
+ case 'core/image':
+ case 'core/cover':
+ case 'core/media-text':
+ return array( '/alt="(.*?)"/' );
+ case 'core/quote':
+ case 'core/pullquote':
+ return array(
+ '/(]*>)(.*?)(<\/p>)/',
+ '/(]*>)(.*?)(<\/cite>)/',
+ );
+ case 'core/table':
+ return array(
+ '/(]*>)(.*?)(<\/td>)/',
+ '/( ]*>)(.*?)(<\/th>)/',
+ '/(]*>)(.*?)(<\/figcaption>)/',
+ );
+ default:
+ return null;
+ }
+ }
+
+ /*
+ * Localize text in text blocks.
+ *
+ * @param array $blocks The blocks to localize.
+ * @return array The localized blocks.
+ */
+ public static function escape_text_content_of_blocks( $blocks ) {
+ foreach ( $blocks as &$block ) {
+
+ // Recursively escape the inner blocks.
+ if ( ! empty( $block['innerBlocks'] ) ) {
+ $block['innerBlocks'] = self::escape_text_content_of_blocks( $block['innerBlocks'] );
+ }
+
+ /*
+ * Set the pattern based on the block type.
+ * The pattern is used to match the content that needs to be escaped.
+ * Patterns are defined in the get_text_replacement_patterns_for_html method.
+ */
+ $patterns = self::get_text_replacement_patterns_for_html( $block['blockName'] );
+
+ // If the block does not have any patterns leave the block as is and continue to the next block.
+ if ( ! $patterns ) {
+ continue;
+ }
+
+ // Builds the replacement callback function based on the block type.
+ switch ( $block['blockName'] ) {
+ case 'core/paragraph':
+ case 'core/heading':
+ case 'core/list-item':
+ case 'core/verse':
+ case 'core/button':
+ case 'core/quote':
+ case 'core/pullquote':
+ case 'core/table':
+ $replace_content_callback = function ( $content, $pattern ) {
+ if ( empty( $content ) ) {
+ return;
+ }
+ return preg_replace_callback(
+ $pattern,
+ function( $matches ) {
+ return $matches[1] . self::escape_string( $matches[2] ) . $matches[3];
+ },
+ $content
+ );
+ };
+ break;
+ case 'core/image':
+ case 'core/cover':
+ case 'core/media-text':
+ $replace_content_callback = function ( $content, $pattern ) {
+ if ( empty( $content ) ) {
+ return;
+ }
+ return preg_replace_callback(
+ $pattern,
+ function( $matches ) {
+ return 'alt="' . self::escape_string( $matches[1] ) . '"';
+ },
+ $content
+ );
+ };
+ break;
+ default:
+ $replace_content_callback = null;
+ break;
+ }
+
+ // Apply the replacement patterns to the block content.
+ foreach ( $patterns as $pattern ) {
+ if (
+ ! empty( $block['innerContent'] ) &&
+ is_callable( $replace_content_callback )
+ ) {
+ $block['innerContent'] = is_array( $block['innerContent'] )
+ ? array_map(
+ function( $content ) use ( $replace_content_callback, $pattern ) {
+ return $replace_content_callback( $content, $pattern );
+ },
+ $block['innerContent']
+ )
+ : $replace_content_callback( $block['innerContent'], $pattern );
+ }
+ }
+ }
+ return $blocks;
+ }
+}
diff --git a/admin/create-theme/theme-templates.php b/admin/create-theme/theme-templates.php
index 254af526..3298e7ee 100644
--- a/admin/create-theme/theme-templates.php
+++ b/admin/create-theme/theme-templates.php
@@ -268,105 +268,20 @@ public static function add_templates_to_local( $export_type, $path = null, $slug
}
}
+ /**
+ * Escape text in template content.
+ *
+ * @param object $template The template to escape text content in.
+ * @return object The template with the content escaped.
+ */
public static function escape_text_in_template( $template ) {
-
- $template_blocks = parse_blocks( $template->content );
- $text_to_localize = array();
-
- // Gather up all the strings that need to be localized
- foreach ( $template_blocks as &$block ) {
- $text_to_localize = array_merge( $text_to_localize, self::get_text_to_localize_from_block( $block ) );
- }
- $text_to_localize = array_unique( $text_to_localize );
-
- // Localize the strings
- foreach ( $text_to_localize as $text ) {
- $template->content = str_replace( $text, self::escape_text( $text ), $template->content );
- }
-
+ $template_blocks = parse_blocks( $template->content );
+ $localized_blocks = CBT_Theme_Locale::escape_text_content_of_blocks( $template_blocks );
+ $updated_template_content = serialize_blocks( $localized_blocks );
+ $template->content = $updated_template_content;
return $template;
}
- private static function get_text_to_localize_from_block( $block ) {
-
- $text_to_localize = array();
-
- // Text Blocks (paragraphs and headings)
- if ( in_array( $block['blockName'], array( 'core/paragraph', 'core/heading', 'core/list-item', 'core/verse' ), true ) ) {
- $markup = $block['innerContent'][0];
- // remove the tags from the beginning and end of the markup
- $markup = substr( $markup, strpos( $markup, '>' ) + 1 );
- $markup = substr( $markup, 0, strrpos( $markup, '<' ) );
- $text_to_localize[] = $markup;
- }
-
- // Quote Blocks
- if ( in_array( $block['blockName'], array( 'core/quote', 'core/pullquote' ), true ) ) {
- $markup = serialize_blocks( array( $block ) );
- // Grab paragraph tag content
- if ( preg_match( '/]*>(.*?)<\/p>/', $markup, $matches ) ) {
- $text_to_localize[] = $matches[1];
- }
- // Grab cite tag content
- if ( preg_match( '/]*>(.*?)<\/cite>/', $markup, $matches ) ) {
- $text_to_localize[] = $matches[1];
- }
- }
-
- // Button Blocks
- if ( in_array( $block['blockName'], array( 'core/button' ), true ) ) {
- $markup = $block['innerContent'][0];
- if ( preg_match( '/]*>(.*?)<\/a>/', $markup, $matches ) ) {
- $text_to_localize[] = $matches[1];
- }
- }
-
- // Alt text in Image and Cover Blocks
- if ( in_array( $block['blockName'], array( 'core/image', 'core/cover', 'core/media-text' ), true ) ) {
- $markup = $block['innerContent'][0];
- if ( preg_match( '/alt="(.*?)"/', $markup, $matches ) ) {
- $text_to_localize[] = $matches[1];
- }
- if ( array_key_exists( 'alt', $block['attrs'] ) ) {
- $text_to_localize[] = $block['attrs']['alt'];
- }
- }
-
- // Table Blocks
- if ( in_array( $block['blockName'], array( 'core/table' ), true ) ) {
- $markup = serialize_blocks( array( $block ) );
- // Grab table cell content
- if ( preg_match_all( '/ ]*>(.*?)<\/td>/', $markup, $matches ) ) {
- $text_to_localize = array_merge( $text_to_localize, $matches[1] );
- }
- // Grab table header content
- if ( preg_match_all( '/ ]*>(.*?)<\/th>/', $markup, $matches ) ) {
- $text_to_localize = array_merge( $text_to_localize, $matches[1] );
- }
- // Grab the caption
- if ( preg_match_all( '/]*>(.*?)<\/figcaption>/', $markup, $matches ) ) {
- $text_to_localize = array_merge( $text_to_localize, $matches[1] );
- }
- }
-
- // process inner blocks
- if ( ! empty( $block['innerBlocks'] ) ) {
- foreach ( $block['innerBlocks'] as $inner_block ) {
- $text_to_localize = array_merge( $text_to_localize, self::get_text_to_localize_from_block( $inner_block ) );
- }
- }
-
- return $text_to_localize;
- }
-
- public static function escape_text( $text ) {
- if ( ! $text ) {
- return $text;
- }
- $text = addcslashes( $text, "'" );
- return "get( 'TextDomain' ) . "');?>";
- }
-
private static function eliminate_environment_specific_content_from_block( $block, $options = null ) {
// remove theme attribute from template parts
diff --git a/tests/CbtThemeLocale/base.php b/tests/CbtThemeLocale/base.php
new file mode 100644
index 00000000..bf86d292
--- /dev/null
+++ b/tests/CbtThemeLocale/base.php
@@ -0,0 +1,51 @@
+orig_active_theme_slug = get_option( 'stylesheet' );
+
+ // Create a test theme directory.
+ $this->test_theme_dir = DIR_TESTDATA . '/themes/';
+
+ // Register test theme directory.
+ register_theme_directory( $this->test_theme_dir );
+
+ // Switch to the test theme.
+ switch_theme( 'test-theme-locale' );
+ }
+
+ /**
+ * Tears down tests.
+ */
+ public function tear_down() {
+ parent::tear_down();
+
+ // Restore the original active theme.
+ switch_theme( $this->orig_active_theme_slug );
+ }
+}
diff --git a/tests/CbtThemeLocale/escapeString.php b/tests/CbtThemeLocale/escapeString.php
new file mode 100644
index 00000000..4ffcaf9a
--- /dev/null
+++ b/tests/CbtThemeLocale/escapeString.php
@@ -0,0 +1,48 @@
+assertEquals( "", $escaped_string );
+ }
+
+ public function test_escape_string_with_single_quote() {
+ $string = "This is a test text with a single quote '";
+ $escaped_string = CBT_Theme_Locale::escape_string( $string );
+ $this->assertEquals( "", $escaped_string );
+ }
+
+ public function test_escape_string_with_double_quote() {
+ $string = 'This is a test text with a double quote "';
+ $escaped_string = CBT_Theme_Locale::escape_string( $string );
+ $this->assertEquals( "", $escaped_string );
+ }
+
+ public function test_escape_string_with_html() {
+ $string = 'This is a test text with HTML.
';
+ $escaped_string = CBT_Theme_Locale::escape_string( $string );
+ $this->assertEquals( "This is a test text with HTML.', 'test-locale-theme');?>", $escaped_string );
+ }
+
+ public function test_escape_string_with_already_escaped_string() {
+ $string = "";
+ $escaped_string = CBT_Theme_Locale::escape_string( $string );
+ $this->assertEquals( $string, $escaped_string );
+ }
+
+ public function test_escape_string_with_non_string() {
+ $string = null;
+ $escaped_string = CBT_Theme_Locale::escape_string( $string );
+ $this->assertEquals( $string, $escaped_string );
+ }
+}
diff --git a/tests/CbtThemeLocale/escapeTextContentOfBlocks.php b/tests/CbtThemeLocale/escapeTextContentOfBlocks.php
new file mode 100644
index 00000000..c138a24a
--- /dev/null
+++ b/tests/CbtThemeLocale/escapeTextContentOfBlocks.php
@@ -0,0 +1,195 @@
+assertEquals( $expected_markup, $escaped_markup, 'The markup result is not as the expected one.' );
+ }
+
+ public function data_test_escape_text_content_of_blocks() {
+ return array(
+
+ 'paragraph' => array(
+ 'block_markup' => 'This is a test text.
',
+ 'expected_markup' => '
',
+ ),
+
+ 'paragraph on nested groups' => array(
+ 'block_markup' =>
+ '
+
+ ',
+ 'expected_markup' =>
+ '
+
+ ',
+ ),
+
+ 'heading 1' => array(
+ 'block_markup' =>
+ '
+ A passion for creating spaces
+ ',
+ 'expected_markup' =>
+ '
+
+ ',
+ ),
+
+ 'heading 2' => array(
+ 'block_markup' =>
+ '
+ A passion for creating spaces
+ ',
+ 'expected_markup' =>
+ '
+
+ ',
+ ),
+
+ 'list item' => array(
+ 'block_markup' =>
+ '
+
+ Collaborate with fellow architects.
+
+
+ Showcase your projects.
+
+
+ Experience the world of architecture.
+
+ ',
+ 'expected_markup' =>
+ '
+
+ ',
+ ),
+
+ 'verse' => array(
+ 'block_markup' =>
+ '
+ Ya somos el olvido que seremos. El polvo elemental que nos ignora y que fue el rojo Adán y que es ahora todos los hombres, y que no veremos.
+ ',
+ 'expected_markup' =>
+ '
+ El polvo elemental que nos ignora y que fue el rojo Adán y que es ahora todos los hombres, y que no veremos.\', \'test-locale-theme\');?>
+ ',
+ ),
+
+ 'button' => array(
+ 'block_markup' =>
+ '
+
+ ',
+ 'expected_markup' =>
+ '
+
+ ',
+ ),
+
+ 'image' => array(
+ 'block_markup' =>
+ '
+
+ ',
+ 'expected_markup' =>
+ '
+
+ ',
+ ),
+
+ 'cover' => array(
+ 'block_markup' =>
+ '
+
+
This is a cover caption
+
+ ',
+ 'expected_markup' =>
+ '
+
+ ',
+ ),
+
+ 'media-text' => array(
+ 'block_markup' =>
+ '
+
+
Media text content test.
+
+ ',
+ 'expected_markup' =>
+ '
+
+ ',
+ ),
+
+ 'pullquote' => array(
+ 'block_markup' =>
+ '
+ Yo me equivoqué y pagué, pero la pelota no se mancha.
Diego Armando Maradona
+ ',
+ 'expected_markup' =>
+ '
+
+ ',
+ ),
+
+ 'table' => array(
+ 'block_markup' =>
+ '
+ Score table
+ ',
+ 'expected_markup' =>
+ '
+
+ ',
+ ),
+
+ );
+ }
+}
+
+
+
+
diff --git a/tests/data/themes/test-theme-locale/style.css b/tests/data/themes/test-theme-locale/style.css
new file mode 100644
index 00000000..fbc200a3
--- /dev/null
+++ b/tests/data/themes/test-theme-locale/style.css
@@ -0,0 +1,15 @@
+/*
+Theme Name: Test Locale Theme
+Theme URI: https://example.org/themes/test-locale-theme
+Author: the WordPress team
+Author URI: https://wordpress.org
+Description: Test Locale Theme is a theme for testing the text localization/escaping capabilities of the Create Block Theme plugin.
+Requires at least: 6.4
+Tested up to: 6.4
+Requires PHP: 7.0
+Version: 1.0
+License: GNU General Public License v2 or later
+License URI: http://www.gnu.org/licenses/gpl-2.0.html
+Text Domain: test-locale-theme
+Tags: test, locale, theme
+*/
diff --git a/tests/data/themes/test-theme-locale/theme.json b/tests/data/themes/test-theme-locale/theme.json
new file mode 100644
index 00000000..c4c73c82
--- /dev/null
+++ b/tests/data/themes/test-theme-locale/theme.json
@@ -0,0 +1,6 @@
+{
+ "$schema": "https://schemas.wp.org/trunk/theme.json",
+ "version": 2,
+ "styles": {},
+ "settings": {}
+}
diff --git a/tests/test-theme-templates.php b/tests/test-theme-templates.php
index 6fca386c..faabb623 100644
--- a/tests/test-theme-templates.php
+++ b/tests/test-theme-templates.php
@@ -1,6 +1,7 @@
assertStringContainsString( 'alt=""', $new_template->content );
- // Check the block attribute
- $this->assertStringContainsString( '"alt":""', $new_template->content );
}
public function test_localize_quote() {