PluginProbe
Gutenberg / 22.4.2
Gutenberg v22.4.2
23.9.1 23.9.0 23.8.0 23.7.2 23.7.1 23.7.0 23.6.1 23.6.2 23.6.0 23.5.3 23.5.2 23.5.1 23.5.0 23.4.0 23.3.2 23.3.1 23.3.0 23.2.0 23.2.1 23.2.2 23.1.1 23.1.0 23.0.1 12.6.0 7.4.0 All 402 releases
gutenberg / lib / blocks.php

blocks.php in Gutenberg 22.4.2, at lib/blocks.php

399 lines 12.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Block functions specific for the Gutenberg editor plugin.
4 *
5 * @package gutenberg
6 */
7
8 /**
9 * Substitutes the implementation of a core-registered block type, if exists,
10 * with the built result from the plugin.
11 */
12 function gutenberg_reregister_core_block_types() {
13 // Blocks directory may not exist if working from a fresh clone.
14 $blocks_dirs = array(
15 __DIR__ . '/../build/scripts/block-library/',
16 __DIR__ . '/../build/scripts/edit-widgets/blocks/',
17 __DIR__ . '/../build/scripts/widgets/blocks/',
18 );
19
20 foreach ( $blocks_dirs as $blocks_dir ) {
21 if ( ! is_dir( $blocks_dir ) ) {
22 continue;
23 }
24
25 // Scan for block directories (those with block.json).
26 $block_json_files = glob( $blocks_dir . '*/block.json' );
27
28 foreach ( $block_json_files as $block_json_file ) {
29 $block_folder = dirname( $block_json_file );
30 $block_name_folder = basename( $block_folder );
31
32 // Read block.json to get block name.
33 $metadata = json_decode( file_get_contents( $block_json_file ), true );
34 if ( ! is_array( $metadata ) || ! isset( $metadata['name'] ) ) {
35 continue;
36 }
37
38 // Deregister core version.
39 gutenberg_deregister_core_block_and_assets( $metadata['name'] );
40 gutenberg_register_core_block_assets( $block_name_folder );
41 $php_file = dirname( $block_folder ) . '/' . $block_name_folder . '.php';
42 if ( file_exists( $php_file ) ) {
43 require_once $php_file;
44 } else {
45 register_block_type_from_metadata( $block_json_file );
46 }
47 }
48 }
49 }
50
51 add_action( 'init', 'gutenberg_reregister_core_block_types' );
52
53 /**
54 * Adds the defer loading strategy to all registered blocks.
55 *
56 * This function would not be part of core merge. Instead, the register_block_script_handle() function would be patched
57 * as follows.
58 *
59 * ```
60 * --- a/wp-includes/blocks.php
61 * +++ b/wp-includes/blocks.php
62 * @ @ -153,7 +153,8 @ @ function register_block_script_handle( $metadata, $field_name, $index = 0 ) {
63 * $script_handle,
64 * $script_uri,
65 * $script_dependencies,
66 * - $script_asset['version'] ?? false
67 * + $script_asset['version'] ?? false,
68 * + array( 'strategy' => 'defer' )
69 * );
70 * if ( ! $result ) {
71 * return false;
72 * ```
73 *
74 * @see register_block_script_handle()
75 */
76 function gutenberg_defer_block_view_scripts() {
77 $block_types = WP_Block_Type_Registry::get_instance()->get_all_registered();
78 foreach ( $block_types as $block_type ) {
79 foreach ( $block_type->view_script_handles as $view_script_handle ) {
80 wp_script_add_data( $view_script_handle, 'strategy', 'defer' );
81 }
82 }
83 }
84
85 add_action( 'init', 'gutenberg_defer_block_view_scripts', 100 );
86
87 /**
88 * Deregisters the existing core block type and its assets.
89 *
90 * @param string $block_name The name of the block.
91 *
92 * @return void
93 */
94 function gutenberg_deregister_core_block_and_assets( $block_name ) {
95 $registry = WP_Block_Type_Registry::get_instance();
96 if ( $registry->is_registered( $block_name ) ) {
97 $block_type = $registry->get_registered( $block_name );
98 if ( ! empty( $block_type->view_script_handles ) ) {
99 foreach ( $block_type->view_script_handles as $view_script_handle ) {
100 if ( str_starts_with( $view_script_handle, 'wp-block-' ) ) {
101 wp_deregister_script( $view_script_handle );
102 }
103 }
104 }
105 $registry->unregister( $block_name );
106 }
107 }
108
109 /**
110 * Registers block styles for a core block.
111 *
112 * @param string $block_name The block-name.
113 *
114 * @return void
115 */
116 function gutenberg_register_core_block_assets( $block_name ) {
117 static $gutenberg_url_root = null;
118 // Running `gutenberg_url` inside of a loop can be expensive in systems with
119 // many callbacks attached to the `plugins_url` hook.
120 // Since all of the paths have the same root, we can instead retrieve the
121 // corresponding URL root once, and manually concatenate the URL below.
122 if ( is_null( $gutenberg_url_root ) ) {
123 $gutenberg_url_root = gutenberg_url( '/' );
124 }
125
126 if ( ! wp_should_load_separate_core_block_assets() ) {
127 return;
128 }
129
130 $block_name = str_replace( 'core/', '', $block_name );
131
132 // When in production, use the plugin's version as the default asset version;
133 // else (for development or test) default to use the current time.
134 $default_version = defined( 'GUTENBERG_VERSION' ) && ! SCRIPT_DEBUG ? GUTENBERG_VERSION : time();
135 $suffix = SCRIPT_DEBUG ? '' : '.min';
136
137 $style_path = "build/styles/block-library/$block_name/";
138 $stylesheet_url = $gutenberg_url_root . $style_path . 'style' . $suffix . '.css';
139 $stylesheet_path = gutenberg_dir_path() . $style_path . ( is_rtl() ? 'style-rtl' . $suffix . '.css' : 'style' . $suffix . '.css' );
140
141 if ( file_exists( $stylesheet_path ) ) {
142
143 wp_deregister_style( "wp-block-{$block_name}" );
144 wp_register_style(
145 "wp-block-{$block_name}",
146 $stylesheet_url,
147 array(),
148 $default_version
149 );
150 wp_style_add_data( "wp-block-{$block_name}", 'rtl', 'replace' );
151 wp_style_add_data( "wp-block-{$block_name}", 'suffix', $suffix );
152 // Add a reference to the stylesheet's path to allow calculations for inlining styles in `wp_head`.
153 wp_style_add_data( "wp-block-{$block_name}", 'path', $stylesheet_path );
154 } else {
155 wp_register_style( "wp-block-{$block_name}", false, array() );
156 }
157
158 /*
159 * If the current theme supports wp-block-styles, dequeue the core styles
160 * and enqueue the plugin ones instead.
161 */
162 if ( current_theme_supports( 'wp-block-styles' ) ) {
163
164 // Get the path to the block's stylesheet.
165 $theme_style_path = is_rtl()
166 ? "build/styles/block-library/$block_name/theme-rtl{$suffix}.css"
167 : "build/styles/block-library/$block_name/theme{$suffix}.css";
168
169 // If the file exists, enqueue it.
170 if ( file_exists( gutenberg_dir_path() . $theme_style_path ) ) {
171 wp_deregister_style( "wp-block-{$block_name}-theme" );
172 wp_register_style(
173 "wp-block-{$block_name}-theme",
174 $gutenberg_url_root . $theme_style_path,
175 array(),
176 $default_version
177 );
178 wp_style_add_data( "wp-block-{$block_name}-theme", 'path', gutenberg_dir_path() . $theme_style_path );
179 wp_style_add_data( "wp-block-{$block_name}-theme", 'suffix', $suffix );
180 }
181 }
182
183 $editor_style_path = "build/styles/block-library/$block_name/style-editor{$suffix}.css";
184 if ( file_exists( gutenberg_dir_path() . $editor_style_path ) ) {
185 wp_deregister_style( "wp-block-{$block_name}-editor" );
186 wp_register_style(
187 "wp-block-{$block_name}-editor",
188 $gutenberg_url_root . $editor_style_path,
189 array(),
190 $default_version
191 );
192 wp_style_add_data( "wp-block-{$block_name}-editor", 'rtl', 'replace' );
193 wp_style_add_data( "wp-block-{$block_name}-editor", 'suffix', $suffix );
194 } else {
195 wp_register_style( "wp-block-{$block_name}-editor", false );
196 }
197 }
198
199 /**
200 * Complements the implementation of block type `core/social-icon`, whether it
201 * be provided by core or the plugin, with derived block types for each
202 * "service" (WordPress, Twitter, etc.) supported by Social Links.
203 *
204 * This ensures backwards compatibility for any users running the Gutenberg
205 * plugin who have used Social Links prior to their conversion to block
206 * variations.
207 *
208 * This shim is INTENTIONALLY left out of core, as Social Links have never
209 * landed there.
210 *
211 * @see https://github.com/WordPress/gutenberg/pull/19887
212 */
213 function gutenberg_register_legacy_social_link_blocks() {
214 $services = array(
215 'amazon',
216 'bandcamp',
217 'behance',
218 'chain',
219 'codepen',
220 'deviantart',
221 'dribbble',
222 'dropbox',
223 'etsy',
224 'facebook',
225 'feed',
226 'fivehundredpx',
227 'flickr',
228 'foursquare',
229 'goodreads',
230 'google',
231 'github',
232 'instagram',
233 'lastfm',
234 'linkedin',
235 'mail',
236 'mastodon',
237 'meetup',
238 'medium',
239 'pinterest',
240 'pocket',
241 'reddit',
242 'skype',
243 'snapchat',
244 'soundcloud',
245 'spotify',
246 'tumblr',
247 'twitch',
248 'twitter',
249 'vimeo',
250 'vk',
251 'wordpress',
252 'yelp',
253 'youtube',
254 );
255
256 foreach ( $services as $service ) {
257 register_block_type(
258 'core/social-link-' . $service,
259 array(
260 'category' => 'widgets',
261 'attributes' => array(
262 'url' => array(
263 'type' => 'string',
264 ),
265 'service' => array(
266 'type' => 'string',
267 'default' => $service,
268 ),
269 'label' => array(
270 'type' => 'string',
271 ),
272 ),
273 'render_callback' => 'gutenberg_render_block_core_social_link',
274 )
275 );
276 }
277 }
278
279 add_action( 'init', 'gutenberg_register_legacy_social_link_blocks' );
280
281 /**
282 * Migrate the legacy `sync_status` meta key (added 16.1) to the new `wp_pattern_sync_status` meta key (16.1.1).
283 *
284 * This filter is INTENTIONALLY left out of core as the meta key was fist introduced to core in 6.3 as `wp_pattern_sync_status`.
285 * see https://github.com/WordPress/gutenberg/pull/52232
286 *
287 * @param mixed $value The value to return, either a single metadata value or an array of values depending on the value of $single.
288 * @param int $object_id ID of the object metadata is for.
289 * @param string $meta_key Metadata key.
290 * @param bool $single Whether to return only the first value of the specified $meta_key.
291 */
292 function gutenberg_legacy_wp_block_post_meta( $value, $object_id, $meta_key, $single ) {
293 if ( 'wp_pattern_sync_status' !== $meta_key ) {
294 return $value;
295 }
296
297 $sync_status = get_post_meta( $object_id, 'sync_status', $single );
298
299 if ( $single && 'unsynced' === $sync_status ) {
300 return $sync_status;
301 } elseif ( isset( $sync_status[0] ) && 'unsynced' === $sync_status[0] ) {
302 return $sync_status;
303 }
304
305 return $value;
306 }
307
308 add_filter( 'default_post_metadata', 'gutenberg_legacy_wp_block_post_meta', 10, 4 );
309
310
311
312 /**
313 * Strips all HTML from the content of footnotes, and sanitizes the ID.
314 *
315 * This function expects slashed data on the footnotes content.
316 *
317 * @access private
318 *
319 * @param string $footnotes JSON encoded string of an array containing the content and ID of each footnote.
320 * @return string Filtered content without any HTML on the footnote content and with the sanitized id.
321 */
322 function _gutenberg_filter_post_meta_footnotes( $footnotes ) {
323 $footnotes_decoded = json_decode( $footnotes, true );
324 if ( ! is_array( $footnotes_decoded ) ) {
325 return '';
326 }
327 $footnotes_sanitized = array();
328 foreach ( $footnotes_decoded as $footnote ) {
329 if ( ! empty( $footnote['content'] ) && ! empty( $footnote['id'] ) ) {
330 $footnotes_sanitized[] = array(
331 'id' => sanitize_key( $footnote['id'] ),
332 'content' => wp_unslash( wp_filter_post_kses( wp_slash( $footnote['content'] ) ) ),
333 );
334 }
335 }
336 return wp_json_encode( $footnotes_sanitized );
337 }
338
339 /**
340 * Adds the filters to filter footnotes meta field.
341 *
342 * @access private
343 */
344 function _gutenberg_footnotes_kses_init_filters() {
345 add_filter( 'sanitize_post_meta_footnotes', '_gutenberg_filter_post_meta_footnotes' );
346 }
347
348 /**
349 * Removes the filters that filter footnotes meta field.
350 *
351 * @access private
352 */
353 function _gutenberg_footnotes_remove_filters() {
354 remove_filter( 'sanitize_post_meta_footnotes', '_gutenberg_filter_post_meta_footnotes' );
355 }
356
357 /**
358 * Registers the filter of footnotes meta field if the user does not have unfiltered_html capability.
359 *
360 * @access private
361 */
362 function _gutenberg_footnotes_kses_init() {
363 if ( function_exists( '_wp_filter_post_meta_footnotes' ) ) {
364 return;
365 }
366 _gutenberg_footnotes_remove_filters();
367 if ( ! current_user_can( 'unfiltered_html' ) ) {
368 _gutenberg_footnotes_kses_init_filters();
369 }
370 }
371
372 /**
373 * Initializes footnotes meta field filters when imported data should be filtered.
374 *
375 * This filter is the last being executed on force_filtered_html_on_import.
376 * If the input of the filter is true it means we are in an import situation and should
377 * enable kses, independently of the user capabilities.
378 * So in that case we call _gutenberg_footnotes_kses_init_filters;
379 *
380 * @access private
381 *
382 * @param string $arg Input argument of the filter.
383 * @return string Input argument of the filter.
384 */
385 function _gutenberg_footnotes_force_filtered_html_on_import_filter( $arg ) {
386 if ( function_exists( '_wp_filter_post_meta_footnotes' ) ) {
387 return;
388 }
389 // force_filtered_html_on_import is true we need to init the global styles kses filters.
390 if ( $arg ) {
391 _gutenberg_footnotes_kses_init_filters();
392 }
393 return $arg;
394 }
395
396 add_action( 'init', '_gutenberg_footnotes_kses_init' );
397 add_action( 'set_current_user', '_gutenberg_footnotes_kses_init' );
398 add_filter( 'force_filtered_html_on_import', '_gutenberg_footnotes_force_filtered_html_on_import_filter', 999 );
399