-
Notifications
You must be signed in to change notification settings - Fork 46
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix template texts localizing/escaping (#641)
Co-authored-by: Jason Crist <jcrist@pbking.com>
- Loading branch information
1 parent
d3ffe65
commit ca08514
Showing
9 changed files
with
485 additions
and
97 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
<?php | ||
/* | ||
* Locale related functionality | ||
*/ | ||
class CBT_Theme_Locale { | ||
|
||
/** | ||
* Escape a string for localization. | ||
* | ||
* @param string $string The string to escape. | ||
* @return string The escaped string. | ||
*/ | ||
public static function escape_string( $string ) { | ||
// Avoid escaping if the text is not a string. | ||
if ( ! is_string( $string ) ) { | ||
return $string; | ||
} | ||
|
||
// Check if the text is already escaped. | ||
if ( str_starts_with( $string, '<?php echo' ) ) { | ||
return $string; | ||
} | ||
|
||
$string = addcslashes( $string, "'" ); | ||
return "<?php echo __('" . $string . "', '" . wp_get_theme()->get( 'TextDomain' ) . "');?>"; | ||
} | ||
|
||
/** | ||
* Get a replacement pattern for escaping the text from the html content of a block. | ||
* | ||
* @param string $block_name The block name. | ||
* @return array|null The regex patterns to match the content that needs to be escaped. | ||
* Returns null if the block is not supported. | ||
* Returns an array of regex patterns if the block has html elements that need to be escaped. | ||
*/ | ||
private static function get_text_replacement_patterns_for_html( $block_name ) { | ||
switch ( $block_name ) { | ||
case 'core/paragraph': | ||
return array( '/(<p[^>]*>)(.*?)(<\/p>)/' ); | ||
case 'core/heading': | ||
return array( '/(<h[^>]*>)(.*?)(<\/h[^>]*>)/' ); | ||
case 'core/list-item': | ||
return array( '/(<li[^>]*>)(.*?)(<\/li>)/' ); | ||
case 'core/verse': | ||
return array( '/(<pre[^>]*>)(.*?)(<\/pre>)/' ); | ||
case 'core/button': | ||
return array( '/(<a[^>]*>)(.*?)(<\/a>)/' ); | ||
case 'core/image': | ||
case 'core/cover': | ||
case 'core/media-text': | ||
return array( '/alt="(.*?)"/' ); | ||
case 'core/quote': | ||
case 'core/pullquote': | ||
return array( | ||
'/(<p[^>]*>)(.*?)(<\/p>)/', | ||
'/(<cite[^>]*>)(.*?)(<\/cite>)/', | ||
); | ||
case 'core/table': | ||
return array( | ||
'/(<td[^>]*>)(.*?)(<\/td>)/', | ||
'/(<th[^>]*>)(.*?)(<\/th>)/', | ||
'/(<figcaption[^>]*>)(.*?)(<\/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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
<?php | ||
/** | ||
* Base test case for Theme Locale tests. | ||
* | ||
* @package Create_Block_Theme | ||
*/ | ||
abstract class CBT_Theme_Locale_UnitTestCase extends WP_UnitTestCase { | ||
|
||
/** | ||
* Stores the original active theme slug in order to restore it in tear down. | ||
* | ||
* @var string|null | ||
*/ | ||
private $orig_active_theme_slug; | ||
|
||
/** | ||
* Stores the custom test theme directory. | ||
* | ||
* @var string|null; | ||
*/ | ||
private $test_theme_dir; | ||
|
||
/** | ||
* Sets up tests. | ||
*/ | ||
public function set_up() { | ||
parent::set_up(); | ||
|
||
// Store the original active theme. | ||
$this->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 ); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
<?php | ||
|
||
require_once __DIR__ . '/base.php'; | ||
|
||
/** | ||
* Tests for the CBT_Theme_Locale::escape_string method. | ||
* | ||
* @package Create_Block_Theme | ||
* @covers CBT_Theme_Locale::escape_string | ||
* @group locale | ||
*/ | ||
class CBT_Theme_Locale_EscapeString extends CBT_Theme_Locale_UnitTestCase { | ||
public function test_escape_string() { | ||
$string = 'This is a test text.'; | ||
$escaped_string = CBT_Theme_Locale::escape_string( $string ); | ||
$this->assertEquals( "<?php echo __('This is a test text.', 'test-locale-theme');?>", $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( "<?php echo __('This is a test text with a single quote \\'', 'test-locale-theme');?>", $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( "<?php echo __('This is a test text with a double quote \"', 'test-locale-theme');?>", $escaped_string ); | ||
} | ||
|
||
public function test_escape_string_with_html() { | ||
$string = '<p>This is a test text with HTML.</p>'; | ||
$escaped_string = CBT_Theme_Locale::escape_string( $string ); | ||
$this->assertEquals( "<?php echo __('<p>This is a test text with HTML.</p>', 'test-locale-theme');?>", $escaped_string ); | ||
} | ||
|
||
public function test_escape_string_with_already_escaped_string() { | ||
$string = "<?php echo __('This is a test text.', 'test-locale-theme');?>"; | ||
$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 ); | ||
} | ||
} |
Oops, something went wrong.