PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 7.1
Jetpack – WP Security, Backup, Speed, & Growth v7.1
16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 14.2.2 14.3.1 All 501 releases
jetpack / class.jetpack-gutenberg.php

class.jetpack-gutenberg.php in Jetpack – WP Security, Backup, Speed, & Growth 7.1, at class.jetpack-gutenberg.php

597 lines 17.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php //phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2 /**
3 * Handles server-side registration and use of all blocks and plugins available in Jetpack for the block editor, aka Gutenberg.
4 * Works in tandem with client-side block registration via `index.json`
5 *
6 * @package Jetpack
7 */
8
9 /**
10 * Wrapper function to safely register a gutenberg block type
11 *
12 * @param string $slug Slug of the block.
13 * @param array $args Arguments that are passed into register_block_type.
14 *
15 * @see register_block_type
16 *
17 * @since 6.7.0
18 *
19 * @return WP_Block_Type|false The registered block type on success, or false on failure.
20 */
21 function jetpack_register_block( $slug, $args = array() ) {
22 if ( ! function_exists( 'register_block_type' ) ) {
23 return false;
24 }
25 if ( 0 !== strpos( $slug, 'jetpack/' ) && ! strpos( $slug, '/' ) ) {
26 _doing_it_wrong( 'jetpack_register_block', 'Prefix the block with jetpack/ ', '7.1.0' );
27 $slug = 'jetpack/' . $slug;
28 }
29 return register_block_type( $slug, $args );
30 }
31
32 /**
33 * Helper function to register a Jetpack Gutenberg plugin
34 *
35 * @deprecated 7.1.0 Use Jetpack_Gutenberg::set_extension_available() instead
36 *
37 * @param string $slug Slug of the plugin.
38 *
39 * @since 6.9.0
40 *
41 * @return void
42 */
43 function jetpack_register_plugin( $slug ) {
44 _deprecated_function( __FUNCTION__, '7.1', 'Jetpack_Gutenberg::set_extension_available' );
45
46 Jetpack_Gutenberg::register_plugin( $slug );
47 }
48
49 /**
50 * Set the reason why an extension (block or plugin) is unavailable
51 *
52 * @deprecated 7.1.0 Use Jetpack_Gutenberg::set_extension_unavailable() instead
53 *
54 * @param string $slug Slug of the block.
55 * @param string $reason A string representation of why the extension is unavailable.
56 *
57 * @since 7.0.0
58 *
59 * @return void
60 */
61 function jetpack_set_extension_unavailability_reason( $slug, $reason ) {
62 _deprecated_function( __FUNCTION__, '7.1', 'Jetpack_Gutenberg::set_extension_unavailable' );
63
64 Jetpack_Gutenberg::set_extension_unavailability_reason( $slug, $reason );
65 }
66
67 /**
68 * General Gutenberg editor specific functionality
69 */
70 class Jetpack_Gutenberg {
71
72 /**
73 * Only these extensions can be registered. Used to control availability of beta blocks.
74 *
75 * @var array Extensions whitelist
76 */
77 private static $extensions = array();
78
79 /**
80 * Keeps track of the reasons why a given extension is unavailable.
81 *
82 * @var array Extensions availability information
83 */
84 private static $availability = array();
85
86 /**
87 * Prepend the 'jetpack/' prefix to a block name
88 *
89 * @param string $block_name The block name.
90 *
91 * @return string The prefixed block name.
92 */
93 private static function prepend_block_prefix( $block_name ) {
94 return 'jetpack/' . $block_name;
95 }
96
97 /**
98 * Remove the 'jetpack/' or jetpack-' prefix from an extension name
99 *
100 * @param string $extension_name The extension name.
101 *
102 * @return string The unprefixed extension name.
103 */
104 private static function remove_extension_prefix( $extension_name ) {
105 if ( wp_startswith( $extension_name, 'jetpack/' ) || wp_startswith( $extension_name, 'jetpack-' ) ) {
106 return substr( $extension_name, strlen( 'jetpack/' ) );
107 }
108 return $extension_name;
109 }
110
111 /**
112 * Whether two arrays share at least one item
113 *
114 * @param array $a An array.
115 * @param array $b Another array.
116 *
117 * @return boolean True if $a and $b share at least one item
118 */
119 protected static function share_items( $a, $b ) {
120 return count( array_intersect( $a, $b ) ) > 0;
121 }
122
123 /**
124 * Register a block
125 *
126 * @deprecated 7.1.0 Use jetpack_register_block() instead
127 *
128 * @param string $slug Slug of the block.
129 * @param array $args Arguments that are passed into register_block_type().
130 */
131 public static function register_block( $slug, $args ) {
132 _deprecated_function( __METHOD__, '7.1', 'jetpack_register_block' );
133
134 jetpack_register_block( 'jetpack/' . $slug, $args );
135 }
136
137 /**
138 * Register a plugin
139 *
140 * @deprecated 7.1.0 Use Jetpack_Gutenberg::set_extension_available() instead
141 *
142 * @param string $slug Slug of the plugin.
143 */
144 public static function register_plugin( $slug ) {
145 _deprecated_function( __METHOD__, '7.1', 'Jetpack_Gutenberg::set_extension_available' );
146
147 self::set_extension_available( $slug );
148 }
149
150 /**
151 * Register a block
152 *
153 * @deprecated 7.0.0 Use jetpack_register_block() instead
154 *
155 * @param string $slug Slug of the block.
156 * @param array $args Arguments that are passed into the register_block_type.
157 * @param array $availability array containing if a block is available and the reason when it is not.
158 */
159 public static function register( $slug, $args, $availability ) {
160 _deprecated_function( __METHOD__, '7.0', 'jetpack_register_block' );
161
162 if ( isset( $availability['available'] ) && ! $availability['available'] ) {
163 self::set_extension_unavailability_reason( $slug, $availability['unavailable_reason'] );
164 } else {
165 self::register_block( $slug, $args );
166 }
167 }
168
169 /**
170 * Set a (non-block) extension as available
171 *
172 * @param string $slug Slug of the extension.
173 */
174 public static function set_extension_available( $slug ) {
175 self::$availability[ self::remove_extension_prefix( $slug ) ] = true;
176 }
177
178 /**
179 * Set the reason why an extension (block or plugin) is unavailable
180 *
181 * @param string $slug Slug of the extension.
182 * @param string $reason A string representation of why the extension is unavailable.
183 */
184 public static function set_extension_unavailable( $slug, $reason ) {
185 self::$availability[ self::remove_extension_prefix( $slug ) ] = $reason;
186 }
187
188 /**
189 * Set the reason why an extension (block or plugin) is unavailable
190 *
191 * @deprecated 7.1.0 Use set_extension_unavailable() instead
192 *
193 * @param string $slug Slug of the extension.
194 * @param string $reason A string representation of why the extension is unavailable.
195 */
196 public static function set_extension_unavailability_reason( $slug, $reason ) {
197 _deprecated_function( __METHOD__, '7.1', 'Jetpack_Gutenberg::set_extension_unavailable' );
198
199 self::set_extension_unavailable( $slug, $reason );
200 }
201
202 /**
203 * Set up a whitelist of allowed block editor extensions
204 *
205 * @return void
206 */
207 public static function init() {
208 if ( ! self::is_gutenberg_available() ) {
209 return;
210 }
211
212 if ( ! self::should_load() ) {
213 return;
214 }
215
216 /**
217 * Alternative to `JETPACK_BETA_BLOCKS`, set to `true` to load Beta Blocks.
218 *
219 * @since 6.9.0
220 *
221 * @param boolean
222 */
223 if ( apply_filters( 'jetpack_load_beta_blocks', false ) ) {
224 Jetpack_Constants::set_constant( 'JETPACK_BETA_BLOCKS', true );
225 }
226
227 /**
228 * Filter the whitelist of block editor extensions that are available through Jetpack.
229 *
230 * @since 7.0.0
231 *
232 * @param array
233 */
234 self::$extensions = apply_filters( 'jetpack_set_available_extensions', self::get_jetpack_gutenberg_extensions_whitelist() );
235
236 /**
237 * Filter the whitelist of block editor plugins that are available through Jetpack.
238 *
239 * @deprecated 7.0.0 Use jetpack_set_available_extensions instead
240 *
241 * @since 6.8.0
242 *
243 * @param array
244 */
245 self::$extensions = apply_filters( 'jetpack_set_available_blocks', self::$extensions );
246
247 /**
248 * Filter the whitelist of block editor plugins that are available through Jetpack.
249 *
250 * @deprecated 7.0.0 Use jetpack_set_available_extensions instead
251 *
252 * @since 6.9.0
253 *
254 * @param array
255 */
256 self::$extensions = apply_filters( 'jetpack_set_available_plugins', self::$extensions );
257 }
258
259 /**
260 * Resets the class to its original state
261 *
262 * Used in unit tests
263 *
264 * @return void
265 */
266 public static function reset() {
267 self::$extensions = array();
268 self::$availability = array();
269 }
270
271 /**
272 * Return the Gutenberg extensions (blocks and plugins) directory
273 *
274 * @return string The Gutenberg extensions directory
275 */
276 public static function get_blocks_directory() {
277 /**
278 * Filter to select Gutenberg blocks directory
279 *
280 * @since 6.9.0
281 *
282 * @param string default: '_inc/blocks/'
283 */
284 return apply_filters( 'jetpack_blocks_directory', '_inc/blocks/' );
285 }
286
287 /**
288 * Checks for a given .json file in the blocks folder.
289 *
290 * @param string $preset The name of the .json file to look for.
291 *
292 * @return bool True if the file is found.
293 */
294 public static function preset_exists( $preset ) {
295 return file_exists( JETPACK__PLUGIN_DIR . self::get_blocks_directory() . $preset . '.json' );
296 }
297
298 /**
299 * Decodes JSON loaded from a preset file in the blocks folder
300 *
301 * @param string $preset The name of the .json file to load.
302 *
303 * @return mixed Returns an object if the file is present, or false if a valid .json file is not present.
304 */
305 public static function get_preset( $preset ) {
306 return json_decode(
307 // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
308 file_get_contents( JETPACK__PLUGIN_DIR . self::get_blocks_directory() . $preset . '.json' )
309 );
310 }
311
312 /**
313 * Returns a whitelist of Jetpack Gutenberg extensions (blocks and plugins), based on index.json
314 *
315 * @return array A list of blocks: eg [ 'publicize', 'markdown' ]
316 */
317 public static function get_jetpack_gutenberg_extensions_whitelist() {
318 $preset_extensions_manifest = self::preset_exists( 'index' ) ? self::get_preset( 'index' ) : (object) array();
319
320 $preset_extensions = isset( $preset_extensions_manifest->production ) ? (array) $preset_extensions_manifest->production : array();
321
322 if ( Jetpack_Constants::is_true( 'JETPACK_BETA_BLOCKS' ) ) {
323 $beta_extensions = isset( $preset_extensions_manifest->beta ) ? (array) $preset_extensions_manifest->beta : array();
324 return array_unique( array_merge( $preset_extensions, $beta_extensions ) );
325 }
326
327 return $preset_extensions;
328 }
329
330 /**
331 * Get availability of each block / plugin.
332 *
333 * @return array A list of block and plugins and their availablity status
334 */
335 public static function get_availability() {
336 if ( ! self::is_gutenberg_available() ) {
337 return array();
338 }
339
340 /**
341 * Fires before Gutenberg extensions availability is computed.
342 *
343 * In the function call you supply, use `jetpack_register_block()` to set a block as available.
344 * Alternatively, use `Jetpack_Gutenberg::set_extension_available()` (for a non-block plugin), and
345 * `Jetpack_Gutenberg::set_extension_unavailable()` (if the block or plugin should not be registered
346 * but marked as unavailable).
347 *
348 * @since 7.0.0
349 */
350 do_action( 'jetpack_register_gutenberg_extensions' );
351
352 $available_extensions = array();
353
354 foreach ( self::$extensions as $extension ) {
355 $is_available = WP_Block_Type_Registry::get_instance()->is_registered( 'jetpack/' . $extension ) ||
356 ( isset( self::$availability[ $extension ] ) && true === self::$availability[ $extension ] );
357
358 $available_extensions[ $extension ] = array(
359 'available' => $is_available,
360 );
361
362 if ( ! $is_available ) {
363 $reason = isset( self::$availability[ $extension ] ) ? self::$availability[ $extension ] : 'missing_module';
364 $available_extensions[ $extension ]['unavailable_reason'] = $reason;
365 }
366 }
367
368 $unwhitelisted_blocks = array();
369 $all_registered_blocks = WP_Block_Type_Registry::get_instance()->get_all_registered();
370 foreach ( $all_registered_blocks as $block_name => $block_type ) {
371 if ( ! wp_startswith( $block_name, 'jetpack/' ) || isset( $block_type->parent ) ) {
372 continue;
373 }
374
375 $unprefixed_block_name = self::remove_extension_prefix( $block_name );
376
377 if ( in_array( $unprefixed_block_name, self::$extensions, true ) ) {
378 continue;
379 }
380
381 $unwhitelisted_blocks[ $unprefixed_block_name ] = array(
382 'available' => false,
383 'unavailable_reason' => 'not_whitelisted',
384 );
385 }
386
387 // Finally: Unwhitelisted non-block extensions. These are in $availability.
388 $unwhitelisted_extensions = array_fill_keys(
389 array_diff( array_keys( self::$availability ), self::$extensions ),
390 array(
391 'available' => false,
392 'unavailable_reason' => 'not_whitelisted',
393 )
394 );
395 return array_merge( $available_extensions, $unwhitelisted_blocks, $unwhitelisted_extensions );
396 }
397
398 /**
399 * Check if Gutenberg editor is available
400 *
401 * @since 6.7.0
402 *
403 * @return bool
404 */
405 public static function is_gutenberg_available() {
406 return function_exists( 'register_block_type' );
407 }
408
409 /**
410 * Check whether conditions indicate Gutenberg Extensions (blocks and plugins) should be loaded
411 *
412 * Loading blocks and plugins is enabled by default and may be disabled via filter:
413 * add_filter( 'jetpack_gutenberg', '__return_false' );
414 *
415 * @since 6.9.0
416 *
417 * @return bool
418 */
419 public static function should_load() {
420 if ( ! Jetpack::is_active() && ! Jetpack::is_development_mode() ) {
421 return false;
422 }
423
424 /**
425 * Filter to disable Gutenberg blocks
426 *
427 * @since 6.5.0
428 *
429 * @param bool true Whether to load Gutenberg blocks
430 */
431 return (bool) apply_filters( 'jetpack_gutenberg', true );
432 }
433
434 /**
435 * Only enqueue block assets when needed.
436 *
437 * @param string $type slug of the block.
438 * @param array $script_dependencies An array of view-side Javascript dependencies to be enqueued.
439 *
440 * @return void
441 */
442 public static function load_assets_as_required( $type, $script_dependencies = array() ) {
443 if ( is_admin() ) {
444 // A block's view assets will not be required in wp-admin.
445 return;
446 }
447
448 $type = sanitize_title_with_dashes( $type );
449 // Enqueue styles.
450 $style_relative_path = self::get_blocks_directory() . $type . '/view' . ( is_rtl() ? '.rtl' : '' ) . '.css';
451 if ( self::block_has_asset( $style_relative_path ) ) {
452 $style_version = self::get_asset_version( $style_relative_path );
453 $view_style = plugins_url( $style_relative_path, JETPACK__PLUGIN_FILE );
454 wp_enqueue_style( 'jetpack-block-' . $type, $view_style, array(), $style_version );
455 }
456
457 // Enqueue script.
458 $script_relative_path = self::get_blocks_directory() . $type . '/view.js';
459 if ( self::block_has_asset( $script_relative_path ) ) {
460 $script_version = self::get_asset_version( $script_relative_path );
461 $view_script = plugins_url( $script_relative_path, JETPACK__PLUGIN_FILE );
462 wp_enqueue_script( 'jetpack-block-' . $type, $view_script, $script_dependencies, $script_version, false );
463 }
464
465 wp_localize_script(
466 'jetpack-block-' . $type,
467 'Jetpack_Block_Assets_Base_Url',
468 plugins_url( self::get_blocks_directory(), JETPACK__PLUGIN_FILE )
469 );
470 }
471
472 /**
473 * Check if an asset exists for a block.
474 *
475 * @param string $file Path of the file we are looking for.
476 *
477 * @return bool $block_has_asset Does the file exist.
478 */
479 public static function block_has_asset( $file ) {
480 return file_exists( JETPACK__PLUGIN_DIR . $file );
481 }
482
483 /**
484 * Get the version number to use when loading the file. Allows us to bypass cache when developing.
485 *
486 * @param string $file Path of the file we are looking for.
487 *
488 * @return string $script_version Version number.
489 */
490 public static function get_asset_version( $file ) {
491 return Jetpack::is_development_version() && self::block_has_asset( $file )
492 ? filemtime( JETPACK__PLUGIN_DIR . $file )
493 : JETPACK__VERSION;
494 }
495
496 /**
497 * Load Gutenberg editor assets
498 *
499 * @since 6.7.0
500 *
501 * @return void
502 */
503 public static function enqueue_block_editor_assets() {
504 if ( ! self::should_load() ) {
505 return;
506 }
507
508 $rtl = is_rtl() ? '.rtl' : '';
509 $beta = Jetpack_Constants::is_true( 'JETPACK_BETA_BLOCKS' ) ? '-beta' : '';
510 $blocks_dir = self::get_blocks_directory();
511
512 $editor_script = plugins_url( "{$blocks_dir}editor{$beta}.js", JETPACK__PLUGIN_FILE );
513 $editor_style = plugins_url( "{$blocks_dir}editor{$beta}{$rtl}.css", JETPACK__PLUGIN_FILE );
514
515 $version = Jetpack::is_development_version() && file_exists( JETPACK__PLUGIN_DIR . $blocks_dir . 'editor.js' )
516 ? filemtime( JETPACK__PLUGIN_DIR . $blocks_dir . 'editor.js' )
517 : JETPACK__VERSION;
518
519 if ( method_exists( 'Jetpack', 'build_raw_urls' ) ) {
520 $site_fragment = Jetpack::build_raw_urls( home_url() );
521 } elseif ( class_exists( 'WPCOM_Masterbar' ) && method_exists( 'WPCOM_Masterbar', 'get_calypso_site_slug' ) ) {
522 $site_fragment = WPCOM_Masterbar::get_calypso_site_slug( get_current_blog_id() );
523 } else {
524 $site_fragment = '';
525 }
526
527 wp_enqueue_script(
528 'jetpack-blocks-editor',
529 $editor_script,
530 array(
531 'lodash',
532 'wp-api-fetch',
533 'wp-blob',
534 'wp-blocks',
535 'wp-components',
536 'wp-compose',
537 'wp-data',
538 'wp-date',
539 'wp-edit-post',
540 'wp-editor',
541 'wp-element',
542 'wp-hooks',
543 'wp-i18n',
544 'wp-keycodes',
545 'wp-plugins',
546 'wp-rich-text',
547 'wp-token-list',
548 'wp-url',
549 ),
550 $version,
551 false
552 );
553
554 wp_localize_script(
555 'jetpack-blocks-editor',
556 'Jetpack_Block_Assets_Base_Url',
557 plugins_url( $blocks_dir . '/', JETPACK__PLUGIN_FILE )
558 );
559
560 wp_localize_script(
561 'jetpack-blocks-editor',
562 'Jetpack_Editor_Initial_State',
563 array(
564 'available_blocks' => self::get_availability(),
565 'jetpack' => array( 'is_active' => Jetpack::is_active() ),
566 'siteFragment' => $site_fragment,
567 )
568 );
569
570 Jetpack::setup_wp_i18n_locale_data();
571
572 wp_enqueue_style( 'jetpack-blocks-editor', $editor_style, array(), $version );
573 }
574
575 /**
576 * Some blocks do not depend on a specific module,
577 * and can consequently be loaded outside of the usual modules.
578 * We will look for such modules in the extensions/ directory.
579 *
580 * @since 7.1.0
581 */
582 public static function load_independent_blocks() {
583 if ( self::should_load() && self::is_gutenberg_available() ) {
584 /**
585 * Look for files that match our list of available Jetpack Gutenberg extensions (blocks and plugins).
586 * If available, load them.
587 */
588 foreach ( self::$extensions as $extension ) {
589 $extension_file_glob = glob( JETPACK__PLUGIN_DIR . 'extensions/*/' . $extension . '/' . $extension . '.php' );
590 if ( ! empty( $extension_file_glob ) ) {
591 include_once $extension_file_glob[0];
592 }
593 }
594 }
595 }
596 }
597