Skip to content

Commit dc0bc0a

Browse files
committed
Add metadata support for single-root patterns in resolve_pattern_blocks function
Enhances the `resolve_pattern_blocks` function to include additional metadata attributes such as `description` and `categories` for single-root patterns. Updates unit tests to validate the new functionality, including sanitization of potentially harmful characters in pattern attributes.
1 parent 09572a0 commit dc0bc0a

File tree

2 files changed

+68
-9
lines changed

2 files changed

+68
-9
lines changed

src/wp-includes/blocks.php

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1844,6 +1844,7 @@ function traverse_and_serialize_block( $block, $pre_callback = null, $post_callb
18441844
* Replaces patterns in a block tree with their content.
18451845
*
18461846
* @since 6.6.0
1847+
* @since n.e.x.t Adds metadata to attributes of single-pattern container blocks.
18471848
*
18481849
* @param array $blocks An array blocks.
18491850
*
@@ -1884,10 +1885,23 @@ function resolve_pattern_blocks( $blocks ) {
18841885

18851886
// For single-root patterns, add the pattern name to make this a pattern instance in the editor.
18861887
if ( count( $blocks_to_insert ) === 1 ) {
1887-
$blocks_to_insert[0]['attrs']['metadata'] = array(
1888+
$metadata = array(
18881889
'patternName' => $slug,
1889-
'name' => $pattern['title'],
18901890
);
1891+
1892+
if ( ! empty( $pattern['title'] ) ) {
1893+
$metadata['name'] = sanitize_text_field( $pattern['title'] );
1894+
}
1895+
1896+
if ( ! empty( $pattern['description'] ) ) {
1897+
$metadata['description'] = sanitize_text_field( $pattern['description'] );
1898+
}
1899+
1900+
if ( ! empty( $pattern['categories'] ) && is_array( $pattern['categories'] ) ) {
1901+
$metadata['categories'] = array_map( 'sanitize_text_field', $pattern['categories'] );
1902+
}
1903+
1904+
$blocks_to_insert[0]['attrs']['metadata'] = $metadata;
18911905
}
18921906

18931907
$seen_refs[ $slug ] = true;

tests/phpunit/tests/blocks/resolvePatternBlocks.php

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,22 @@ public function set_up() {
3636
'title' => 'Single Root Pattern',
3737
'content' => '<!-- wp:paragraph -->Single root content<!-- /wp:paragraph -->',
3838
'description' => 'A single root pattern.',
39+
'categories' => array( 'text' ),
40+
)
41+
);
42+
register_block_pattern(
43+
'core/single-root-with-forbidden-chars-in-attrs',
44+
array(
45+
'title' => 'Single Root Pattern<script>alert("XSS")</script>',
46+
'content' => '<!-- wp:paragraph -->Single root content<!-- /wp:paragraph -->',
47+
'description' => 'A single root pattern.<script>alert("XSS")</script><img src=x onerror=alert(1)>',
48+
'categories' => array(
49+
'text<script>alert("XSS")</script>',
50+
'bad\'); DROP TABLE wp_posts;--',
51+
'<img src=x onerror=alert(1)>',
52+
"evil\x00null\nbyte",
53+
'category with <strong>html</strong> tags',
54+
),
3955
)
4056
);
4157
register_block_pattern(
@@ -52,6 +68,7 @@ public function set_up() {
5268
'title' => 'Nested Pattern',
5369
'content' => '<!-- wp:group --><!-- wp:paragraph -->Nested content<!-- /wp:paragraph --><!-- wp:pattern {"slug":"core/single-root"} /--><!-- /wp:group -->',
5470
'description' => 'A nested single root pattern.',
71+
'categories' => array( 'featured' ),
5572
)
5673
);
5774
}
@@ -60,7 +77,9 @@ public function tear_down() {
6077
unregister_block_pattern( 'core/test' );
6178
unregister_block_pattern( 'core/recursive' );
6279
unregister_block_pattern( 'core/single-root' );
80+
unregister_block_pattern( 'core/single-root-with-forbidden-chars-in-attrs' );
6381
unregister_block_pattern( 'core/with-attrs' );
82+
unregister_block_pattern( 'core/nested-single' );
6483
parent::tear_down();
6584
}
6685

@@ -85,19 +104,45 @@ public function test_should_resolve_pattern_blocks_as_expected( $blocks, $expect
85104
public function data_should_resolve_pattern_blocks_as_expected() {
86105
return array(
87106
// Works without attributes, leaves the block as is.
88-
'pattern with no slug attribute' => array( '<!-- wp:pattern /-->', '<!-- wp:pattern /-->' ),
107+
'pattern with no slug attribute' => array(
108+
'<!-- wp:pattern /-->',
109+
'<!-- wp:pattern /-->',
110+
),
89111
// Resolves the pattern.
90-
'test pattern' => array( '<!-- wp:pattern {"slug":"core/test"} /-->', '<!-- wp:paragraph -->Hello<!-- /wp:paragraph --><!-- wp:paragraph -->World<!-- /wp:paragraph -->' ),
112+
'test pattern' => array(
113+
'<!-- wp:pattern {"slug":"core/test"} /-->',
114+
'<!-- wp:paragraph -->Hello<!-- /wp:paragraph --><!-- wp:paragraph -->World<!-- /wp:paragraph -->',
115+
),
91116
// Skips recursive patterns.
92-
'recursive pattern' => array( '<!-- wp:pattern {"slug":"core/recursive"} /-->', '<!-- wp:paragraph -->Recursive<!-- /wp:paragraph -->' ),
117+
'recursive pattern' => array(
118+
'<!-- wp:pattern {"slug":"core/recursive"} /-->',
119+
'<!-- wp:paragraph -->Recursive<!-- /wp:paragraph -->',
120+
),
93121
// Resolves the pattern within a block.
94-
'pattern within a block' => array( '<!-- wp:group --><!-- wp:paragraph -->Before<!-- /wp:paragraph --><!-- wp:pattern {"slug":"core/test"} /--><!-- wp:paragraph -->After<!-- /wp:paragraph --><!-- /wp:group -->', '<!-- wp:group --><!-- wp:paragraph -->Before<!-- /wp:paragraph --><!-- wp:paragraph -->Hello<!-- /wp:paragraph --><!-- wp:paragraph -->World<!-- /wp:paragraph --><!-- wp:paragraph -->After<!-- /wp:paragraph --><!-- /wp:group -->' ),
122+
'pattern within a block' => array(
123+
'<!-- wp:group --><!-- wp:paragraph -->Before<!-- /wp:paragraph --><!-- wp:pattern {"slug":"core/test"} /--><!-- wp:paragraph -->After<!-- /wp:paragraph --><!-- /wp:group -->',
124+
'<!-- wp:group --><!-- wp:paragraph -->Before<!-- /wp:paragraph --><!-- wp:paragraph -->Hello<!-- /wp:paragraph --><!-- wp:paragraph -->World<!-- /wp:paragraph --><!-- wp:paragraph -->After<!-- /wp:paragraph --><!-- /wp:group -->',
125+
),
95126
// Resolves the single-root pattern and adds metadata.
96-
'single-root pattern' => array( '<!-- wp:pattern {"slug":"core/single-root"} /-->', '<!-- wp:paragraph {"metadata":{"patternName":"core/single-root","name":"Single Root Pattern"}} -->Single root content<!-- /wp:paragraph -->' ),
127+
'single-root pattern' => array(
128+
'<!-- wp:pattern {"slug":"core/single-root"} /-->',
129+
'<!-- wp:paragraph {"metadata":{"patternName":"core/single-root","name":"Single Root Pattern","description":"A single root pattern.","categories":["text"]}} -->Single root content<!-- /wp:paragraph -->',
130+
),
97131
// Existing attributes are preserved when adding metadata.
98-
'existing attributes preserved' => array( '<!-- wp:pattern {"slug":"core/with-attrs"} /-->', '<!-- wp:paragraph {"className":"custom-class","metadata":{"patternName":"core/with-attrs","name":"Pattern With Attrs"}} -->Content<!-- /wp:paragraph -->' ),
132+
'existing attributes preserved' => array(
133+
'<!-- wp:pattern {"slug":"core/with-attrs"} /-->',
134+
'<!-- wp:paragraph {"className":"custom-class","metadata":{"patternName":"core/with-attrs","name":"Pattern With Attrs","description":"A pattern with existing attributes."}} -->Content<!-- /wp:paragraph -->',
135+
),
99136
// Resolves the nested single-root pattern and adds metadata.
100-
'nested single-root pattern' => array( '<!-- wp:pattern {"slug":"core/nested-single"} /-->', '<!-- wp:group {"metadata":{"patternName":"core/nested-single","name":"Nested Pattern"}} --><!-- wp:paragraph -->Nested content<!-- /wp:paragraph --><!-- wp:paragraph {"metadata":{"patternName":"core/single-root","name":"Single Root Pattern"}} -->Single root content<!-- /wp:paragraph --><!-- /wp:group -->' ),
137+
'nested single-root pattern' => array(
138+
'<!-- wp:pattern {"slug":"core/nested-single"} /-->',
139+
'<!-- wp:group {"metadata":{"patternName":"core/nested-single","name":"Nested Pattern","description":"A nested single root pattern.","categories":["featured"]}} --><!-- wp:paragraph -->Nested content<!-- /wp:paragraph --><!-- wp:paragraph {"metadata":{"patternName":"core/single-root","name":"Single Root Pattern","description":"A single root pattern.","categories":["text"]}} -->Single root content<!-- /wp:paragraph --><!-- /wp:group -->',
140+
),
141+
// Sanitizes fields.
142+
'sanitized pattern attrs' => array(
143+
'<!-- wp:pattern {"slug":"core/single-root-with-forbidden-chars-in-attrs"} /-->',
144+
'<!-- wp:paragraph {"metadata":{"patternName":"core/single-root-with-forbidden-chars-in-attrs","name":"Single Root Pattern","description":"A single root pattern.","categories":["text","bad\'); DROP TABLE wp_posts;\u002d\u002d","","evil\u0000null byte","category with html tags"]}} -->Single root content<!-- /wp:paragraph -->',
145+
),
101146
);
102147
}
103148
}

0 commit comments

Comments
 (0)