Completed
Push — update/paid-block-handling ( 7cc31d...7cd8cd )
by Bernhard
118:06 queued 109:14
created

class.jetpack-gutenberg.php (1 issue)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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
use Automattic\Jetpack\Constants;
10
11
/**
12
 * Wrapper function to safely register a gutenberg block type
13
 *
14
 * @param string $slug Slug of the block.
15
 * @param array  $args Arguments that are passed into register_block_type.
16
 *
17
 * @see register_block_type
18
 *
19
 * @since 6.7.0
20
 *
21
 * @return WP_Block_Type|false The registered block type on success, or false on failure.
22
 */
23
function jetpack_register_block( $slug, $args = array() ) {
24
	if ( 0 !== strpos( $slug, 'jetpack/' ) && ! strpos( $slug, '/' ) ) {
25
		_doing_it_wrong( 'jetpack_register_block', 'Prefix the block with jetpack/ ', '7.1.0' );
26
		$slug = 'jetpack/' . $slug;
27
	}
28
29
	// Checking whether block is registered to ensure it isn't registered twice.
30
	if ( Jetpack_Gutenberg::is_registered( $slug ) ) {
31
		return false;
32
	}
33
34
	return register_block_type( $slug, $args );
35
}
36
37
/**
38
 * Helper function to register a Jetpack Gutenberg plugin
39
 *
40
 * @deprecated 7.1.0 Use Jetpack_Gutenberg::set_extension_available() instead
41
 *
42
 * @param string $slug Slug of the plugin.
43
 *
44
 * @since 6.9.0
45
 *
46
 * @return void
47
 */
48
function jetpack_register_plugin( $slug ) {
49
	_deprecated_function( __FUNCTION__, '7.1', 'Jetpack_Gutenberg::set_extension_available' );
50
51
	Jetpack_Gutenberg::register_plugin( $slug );
52
}
53
54
/**
55
 * Set the reason why an extension (block or plugin) is unavailable
56
 *
57
 * @deprecated 7.1.0 Use Jetpack_Gutenberg::set_extension_unavailable() instead
58
 *
59
 * @param string $slug Slug of the block.
60
 * @param string $reason A string representation of why the extension is unavailable.
61
 *
62
 * @since 7.0.0
63
 *
64
 * @return void
65
 */
66
function jetpack_set_extension_unavailability_reason( $slug, $reason ) {
67
	_deprecated_function( __FUNCTION__, '7.1', 'Jetpack_Gutenberg::set_extension_unavailable' );
68
69
	Jetpack_Gutenberg::set_extension_unavailability_reason( $slug, $reason );
70
}
71
72
/**
73
 * General Gutenberg editor specific functionality
74
 */
75
class Jetpack_Gutenberg {
76
77
	/**
78
	 * Only these extensions can be registered. Used to control availability of beta blocks.
79
	 *
80
	 * @var array Extensions whitelist
81
	 */
82
	private static $extensions = array();
83
84
	/**
85
	 * Keeps track of the reasons why a given extension is unavailable.
86
	 *
87
	 * @var array Extensions availability information
88
	 */
89
	private static $availability = array();
90
91
	/**
92
	 * Prepend the 'jetpack/' prefix to a block name
93
	 *
94
	 * @param string $block_name The block name.
95
	 *
96
	 * @return string The prefixed block name.
97
	 */
98
	private static function prepend_block_prefix( $block_name ) {
99
		return 'jetpack/' . $block_name;
100
	}
101
102
	/**
103
	 * Remove the 'jetpack/' or jetpack-' prefix from an extension name
104
	 *
105
	 * @param string $extension_name The extension name.
106
	 *
107
	 * @return string The unprefixed extension name.
108
	 */
109
	private static function remove_extension_prefix( $extension_name ) {
110
		if ( wp_startswith( $extension_name, 'jetpack/' ) || wp_startswith( $extension_name, 'jetpack-' ) ) {
111
			return substr( $extension_name, strlen( 'jetpack/' ) );
112
		}
113
		return $extension_name;
114
	}
115
116
	/**
117
	 * Whether two arrays share at least one item
118
	 *
119
	 * @param array $a An array.
120
	 * @param array $b Another array.
121
	 *
122
	 * @return boolean True if $a and $b share at least one item
123
	 */
124
	protected static function share_items( $a, $b ) {
125
		return count( array_intersect( $a, $b ) ) > 0;
126
	}
127
128
	/**
129
	 * Register a block
130
	 *
131
	 * @deprecated 7.1.0 Use jetpack_register_block() instead
132
	 *
133
	 * @param string $slug Slug of the block.
134
	 * @param array  $args Arguments that are passed into register_block_type().
135
	 */
136
	public static function register_block( $slug, $args ) {
137
		_deprecated_function( __METHOD__, '7.1', 'jetpack_register_block' );
138
139
		jetpack_register_block( 'jetpack/' . $slug, $args );
140
	}
141
142
	/**
143
	 * Register a plugin
144
	 *
145
	 * @deprecated 7.1.0 Use Jetpack_Gutenberg::set_extension_available() instead
146
	 *
147
	 * @param string $slug Slug of the plugin.
148
	 */
149
	public static function register_plugin( $slug ) {
150
		_deprecated_function( __METHOD__, '7.1', 'Jetpack_Gutenberg::set_extension_available' );
151
152
		self::set_extension_available( $slug );
153
	}
154
155
	/**
156
	 * Register a block
157
	 *
158
	 * @deprecated 7.0.0 Use jetpack_register_block() instead
159
	 *
160
	 * @param string $slug Slug of the block.
161
	 * @param array  $args Arguments that are passed into the register_block_type.
162
	 * @param array  $availability array containing if a block is available and the reason when it is not.
163
	 */
164
	public static function register( $slug, $args, $availability ) {
165
		_deprecated_function( __METHOD__, '7.0', 'jetpack_register_block' );
166
167
		if ( isset( $availability['available'] ) && ! $availability['available'] ) {
168
			self::set_extension_unavailability_reason( $slug, $availability['unavailable_reason'] );
169
		} else {
170
			self::register_block( $slug, $args );
171
		}
172
	}
173
174
	/**
175
	 * Set a (non-block) extension as available
176
	 *
177
	 * @param string $slug Slug of the extension.
178
	 */
179
	public static function set_extension_available( $slug ) {
180
		self::$availability[ self::remove_extension_prefix( $slug ) ] = true;
181
	}
182
183
	/**
184
	 * Set the reason why an extension (block or plugin) is unavailable
185
	 *
186
	 * @param string $slug Slug of the extension.
187
	 * @param string $reason A string representation of why the extension is unavailable.
188
	 * @param array  $details A free-form array containing more information on why the extension is unavailable.
189
	 */
190
	public static function set_extension_unavailable( $slug, $reason, $details ) {
191
		self::$availability[ self::remove_extension_prefix( $slug ) ] = array(
192
			'reason'  => $reason,
193
			'details' => $details,
194
		);
195
	}
196
197
	/**
198
	 * Set the reason why an extension (block or plugin) is unavailable
199
	 *
200
	 * @deprecated 7.1.0 Use set_extension_unavailable() instead
201
	 *
202
	 * @param string $slug Slug of the extension.
203
	 * @param string $reason A string representation of why the extension is unavailable.
204
	 */
205
	public static function set_extension_unavailability_reason( $slug, $reason ) {
206
		_deprecated_function( __METHOD__, '7.1', 'Jetpack_Gutenberg::set_extension_unavailable' );
207
208
		self::set_extension_unavailable( $slug, $reason );
0 ignored issues
show
The call to set_extension_unavailable() misses a required argument $details.

This check looks for function calls that miss required arguments.

Loading history...
209
	}
210
211
	/**
212
	 * Set up a whitelist of allowed block editor extensions
213
	 *
214
	 * @return void
215
	 */
216
	public static function init() {
217
		if ( ! self::should_load() ) {
218
			return;
219
		}
220
221
		/**
222
		 * Alternative to `JETPACK_BETA_BLOCKS`, set to `true` to load Beta Blocks.
223
		 *
224
		 * @since 6.9.0
225
		 *
226
		 * @param boolean
227
		 */
228
		if ( apply_filters( 'jetpack_load_beta_blocks', false ) ) {
229
			Constants::set_constant( 'JETPACK_BETA_BLOCKS', true );
230
		}
231
232
		/**
233
		 * Filter the whitelist of block editor extensions that are available through Jetpack.
234
		 *
235
		 * @since 7.0.0
236
		 *
237
		 * @param array
238
		 */
239
		self::$extensions = apply_filters( 'jetpack_set_available_extensions', self::get_available_extensions() );
240
241
		/**
242
		 * Filter the whitelist of block editor plugins that are available through Jetpack.
243
		 *
244
		 * @deprecated 7.0.0 Use jetpack_set_available_extensions instead
245
		 *
246
		 * @since 6.8.0
247
		 *
248
		 * @param array
249
		 */
250
		self::$extensions = apply_filters( 'jetpack_set_available_blocks', self::$extensions );
251
252
		/**
253
		 * Filter the whitelist of block editor plugins that are available through Jetpack.
254
		 *
255
		 * @deprecated 7.0.0 Use jetpack_set_available_extensions instead
256
		 *
257
		 * @since 6.9.0
258
		 *
259
		 * @param array
260
		 */
261
		self::$extensions = apply_filters( 'jetpack_set_available_plugins', self::$extensions );
262
	}
263
264
	/**
265
	 * Resets the class to its original state
266
	 *
267
	 * Used in unit tests
268
	 *
269
	 * @return void
270
	 */
271
	public static function reset() {
272
		self::$extensions   = array();
273
		self::$availability = array();
274
	}
275
276
	/**
277
	 * Return the Gutenberg extensions (blocks and plugins) directory
278
	 *
279
	 * @return string The Gutenberg extensions directory
280
	 */
281
	public static function get_blocks_directory() {
282
		/**
283
		 * Filter to select Gutenberg blocks directory
284
		 *
285
		 * @since 6.9.0
286
		 *
287
		 * @param string default: '_inc/blocks/'
288
		 */
289
		return apply_filters( 'jetpack_blocks_directory', '_inc/blocks/' );
290
	}
291
292
	/**
293
	 * Checks for a given .json file in the blocks folder.
294
	 *
295
	 * @param string $preset The name of the .json file to look for.
296
	 *
297
	 * @return bool True if the file is found.
298
	 */
299
	public static function preset_exists( $preset ) {
300
		return file_exists( JETPACK__PLUGIN_DIR . self::get_blocks_directory() . $preset . '.json' );
301
	}
302
303
	/**
304
	 * Decodes JSON loaded from a preset file in the blocks folder
305
	 *
306
	 * @param string $preset The name of the .json file to load.
307
	 *
308
	 * @return mixed Returns an object if the file is present, or false if a valid .json file is not present.
309
	 */
310
	public static function get_preset( $preset ) {
311
		return json_decode(
312
			// phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
313
			file_get_contents( JETPACK__PLUGIN_DIR . self::get_blocks_directory() . $preset . '.json' )
314
		);
315
	}
316
317
	/**
318
	 * Returns a whitelist of Jetpack Gutenberg extensions (blocks and plugins), based on index.json
319
	 *
320
	 * @return array A list of blocks: eg [ 'publicize', 'markdown' ]
321
	 */
322
	public static function get_jetpack_gutenberg_extensions_whitelist() {
323
		$preset_extensions_manifest = self::preset_exists( 'index' ) ? self::get_preset( 'index' ) : (object) array();
324
325
		$preset_extensions = isset( $preset_extensions_manifest->production ) ? (array) $preset_extensions_manifest->production : array();
326
327
		if ( Constants::is_true( 'JETPACK_BETA_BLOCKS' ) ) {
328
			$beta_extensions = isset( $preset_extensions_manifest->beta ) ? (array) $preset_extensions_manifest->beta : array();
329
			return array_unique( array_merge( $preset_extensions, $beta_extensions ) );
330
		}
331
332
		return $preset_extensions;
333
	}
334
335
	/**
336
	 * Returns a diff from a combined list of whitelisted extensions and extensions determined to be excluded
337
	 *
338
	 * @param  array $whitelisted_extensions An array of whitelisted extensions.
339
	 *
340
	 * @return array A list of blocks: eg array( 'publicize', 'markdown' )
341
	 */
342
	public static function get_available_extensions( $whitelisted_extensions = null ) {
343
		$exclusions             = get_option( 'jetpack_excluded_extensions', array() );
344
		$whitelisted_extensions = is_null( $whitelisted_extensions ) ? self::get_jetpack_gutenberg_extensions_whitelist() : $whitelisted_extensions;
345
346
		return array_diff( $whitelisted_extensions, $exclusions );
347
	}
348
349
	/**
350
	 * Get availability of each block / plugin.
351
	 *
352
	 * @return array A list of block and plugins and their availablity status
353
	 */
354
	public static function get_availability() {
355
		/**
356
		 * Fires before Gutenberg extensions availability is computed.
357
		 *
358
		 * In the function call you supply, use `jetpack_register_block()` to set a block as available.
359
		 * Alternatively, use `Jetpack_Gutenberg::set_extension_available()` (for a non-block plugin), and
360
		 * `Jetpack_Gutenberg::set_extension_unavailable()` (if the block or plugin should not be registered
361
		 * but marked as unavailable).
362
		 *
363
		 * @since 7.0.0
364
		 */
365
		do_action( 'jetpack_register_gutenberg_extensions' );
366
367
		$available_extensions = array();
368
369
		foreach ( self::$extensions as $extension ) {
370
			$is_available = self::is_registered( 'jetpack/' . $extension ) ||
371
			( isset( self::$availability[ $extension ] ) && true === self::$availability[ $extension ] );
372
373
			$available_extensions[ $extension ] = array(
374
				'available' => $is_available,
375
			);
376
377
			if ( ! $is_available ) {
378
				$reason  = isset( self::$availability[ $extension ] ) ? self::$availability[ $extension ]['reason'] : 'missing_module';
379
				$details = isset( self::$availability[ $extension ] ) ? self::$availability[ $extension ]['details'] : array();
380
				$available_extensions[ $extension ]['unavailable_reason'] = $reason;
381
				$available_extensions[ $extension ]['details']            = $details;
382
			}
383
		}
384
385
		return $available_extensions;
386
	}
387
388
	/**
389
	 * Check if an extension/block is already registered
390
	 *
391
	 * @since 7.2
392
	 *
393
	 * @param string $slug Name of extension/block to check.
394
	 *
395
	 * @return bool
396
	 */
397
	public static function is_registered( $slug ) {
398
		return WP_Block_Type_Registry::get_instance()->is_registered( $slug );
399
	}
400
401
	/**
402
	 * Check if Gutenberg editor is available
403
	 *
404
	 * @since 6.7.0
405
	 *
406
	 * @return bool
407
	 */
408
	public static function is_gutenberg_available() {
409
		return true;
410
	}
411
412
	/**
413
	 * Check whether conditions indicate Gutenberg Extensions (blocks and plugins) should be loaded
414
	 *
415
	 * Loading blocks and plugins is enabled by default and may be disabled via filter:
416
	 *   add_filter( 'jetpack_gutenberg', '__return_false' );
417
	 *
418
	 * @since 6.9.0
419
	 *
420
	 * @return bool
421
	 */
422
	public static function should_load() {
423
		if ( ! Jetpack::is_active() && ! Jetpack::is_development_mode() ) {
424
			return false;
425
		}
426
427
		/**
428
		 * Filter to disable Gutenberg blocks
429
		 *
430
		 * @since 6.5.0
431
		 *
432
		 * @param bool true Whether to load Gutenberg blocks
433
		 */
434
		return (bool) apply_filters( 'jetpack_gutenberg', true );
435
	}
436
437
	/**
438
	 * Only enqueue block assets when needed.
439
	 *
440
	 * @param string $type Slug of the block.
441
	 * @param array  $script_dependencies Script dependencies. Will be merged with automatically
442
	 *                                    detected script dependencies from the webpack build.
443
	 *
444
	 * @return void
445
	 */
446
	public static function load_assets_as_required( $type, $script_dependencies = array() ) {
447
		if ( is_admin() ) {
448
			// A block's view assets will not be required in wp-admin.
449
			return;
450
		}
451
452
		$type = sanitize_title_with_dashes( $type );
453
		self::load_styles_as_required( $type );
454
		self::load_scripts_as_required( $type, $script_dependencies );
455
	}
456
457
	/**
458
	 * Only enqueue block sytles when needed.
459
	 *
460
	 * @param string $type Slug of the block.
461
	 *
462
	 * @since 7.2.0
463
	 *
464
	 * @return void
465
	 */
466
	public static function load_styles_as_required( $type ) {
467
		if ( is_admin() ) {
468
			// A block's view assets will not be required in wp-admin.
469
			return;
470
		}
471
472
		// Enqueue styles.
473
		$style_relative_path = self::get_blocks_directory() . $type . '/view' . ( is_rtl() ? '.rtl' : '' ) . '.css';
474
		if ( self::block_has_asset( $style_relative_path ) ) {
475
			$style_version = self::get_asset_version( $style_relative_path );
476
			$view_style    = plugins_url( $style_relative_path, JETPACK__PLUGIN_FILE );
477
			wp_enqueue_style( 'jetpack-block-' . $type, $view_style, array(), $style_version );
478
		}
479
480
	}
481
482
	/**
483
	 * Only enqueue block scripts when needed.
484
	 *
485
	 * @param string $type Slug of the block.
486
	 * @param array  $dependencies Script dependencies. Will be merged with automatically
487
	 *                             detected script dependencies from the webpack build.
488
	 *
489
	 * @since 7.2.0
490
	 *
491
	 * @return void
492
	 */
493
	public static function load_scripts_as_required( $type, $dependencies = array() ) {
494
		if ( is_admin() ) {
495
			// A block's view assets will not be required in wp-admin.
496
			return;
497
		}
498
499
		// Enqueue script.
500
		$script_relative_path = self::get_blocks_directory() . $type . '/view.js';
501
		$script_deps_path     = JETPACK__PLUGIN_DIR . self::get_blocks_directory() . $type . '/view.deps.json';
502
503
		$script_dependencies = file_exists( $script_deps_path )
504
			// phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
505
			? json_decode( file_get_contents( $script_deps_path ) )
506
			: array();
507
		$script_dependencies = array_merge( $script_dependencies, $dependencies, array( 'wp-polyfill' ) );
508
509
		if ( ( ! class_exists( 'Jetpack_AMP_Support' ) || ! Jetpack_AMP_Support::is_amp_request() ) && self::block_has_asset( $script_relative_path ) ) {
510
			$script_version = self::get_asset_version( $script_relative_path );
511
			$view_script    = plugins_url( $script_relative_path, JETPACK__PLUGIN_FILE );
512
			wp_enqueue_script( 'jetpack-block-' . $type, $view_script, $script_dependencies, $script_version, false );
513
		}
514
515
		wp_localize_script(
516
			'jetpack-block-' . $type,
517
			'Jetpack_Block_Assets_Base_Url',
518
			plugins_url( self::get_blocks_directory(), JETPACK__PLUGIN_FILE )
519
		);
520
	}
521
522
	/**
523
	 * Check if an asset exists for a block.
524
	 *
525
	 * @param string $file Path of the file we are looking for.
526
	 *
527
	 * @return bool $block_has_asset Does the file exist.
528
	 */
529
	public static function block_has_asset( $file ) {
530
		return file_exists( JETPACK__PLUGIN_DIR . $file );
531
	}
532
533
	/**
534
	 * Get the version number to use when loading the file. Allows us to bypass cache when developing.
535
	 *
536
	 * @param string $file Path of the file we are looking for.
537
	 *
538
	 * @return string $script_version Version number.
539
	 */
540
	public static function get_asset_version( $file ) {
541
		return Jetpack::is_development_version() && self::block_has_asset( $file )
542
			? filemtime( JETPACK__PLUGIN_DIR . $file )
543
			: JETPACK__VERSION;
544
	}
545
546
	/**
547
	 * Load Gutenberg editor assets
548
	 *
549
	 * @since 6.7.0
550
	 *
551
	 * @return void
552
	 */
553
	public static function enqueue_block_editor_assets() {
554
		if ( ! self::should_load() ) {
555
			return;
556
		}
557
558
		$rtl        = is_rtl() ? '.rtl' : '';
559
		$beta       = Constants::is_true( 'JETPACK_BETA_BLOCKS' ) ? '-beta' : '';
560
		$blocks_dir = self::get_blocks_directory();
561
562
		$editor_script = plugins_url( "{$blocks_dir}editor{$beta}.js", JETPACK__PLUGIN_FILE );
563
		$editor_style  = plugins_url( "{$blocks_dir}editor{$beta}{$rtl}.css", JETPACK__PLUGIN_FILE );
564
565
		$editor_deps_path = JETPACK__PLUGIN_DIR . $blocks_dir . "editor{$beta}.deps.json";
566
		$editor_deps      = file_exists( $editor_deps_path )
567
			// phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
568
			? json_decode( file_get_contents( $editor_deps_path ) )
569
			: array();
570
		$editor_deps[] = 'wp-polyfill';
571
572
		$version = Jetpack::is_development_version() && file_exists( JETPACK__PLUGIN_DIR . $blocks_dir . 'editor.js' )
573
			? filemtime( JETPACK__PLUGIN_DIR . $blocks_dir . 'editor.js' )
574
			: JETPACK__VERSION;
575
576
		if ( method_exists( 'Jetpack', 'build_raw_urls' ) ) {
577
			$site_fragment = Jetpack::build_raw_urls( home_url() );
578
		} elseif ( class_exists( 'WPCOM_Masterbar' ) && method_exists( 'WPCOM_Masterbar', 'get_calypso_site_slug' ) ) {
579
			$site_fragment = WPCOM_Masterbar::get_calypso_site_slug( get_current_blog_id() );
580
		} else {
581
			$site_fragment = '';
582
		}
583
584
		wp_enqueue_script(
585
			'jetpack-blocks-editor',
586
			$editor_script,
587
			$editor_deps,
588
			$version,
589
			false
590
		);
591
592
		wp_localize_script(
593
			'jetpack-blocks-editor',
594
			'Jetpack_Block_Assets_Base_Url',
595
			plugins_url( $blocks_dir . '/', JETPACK__PLUGIN_FILE )
596
		);
597
598
		wp_localize_script(
599
			'jetpack-blocks-editor',
600
			'Jetpack_Editor_Initial_State',
601
			array(
602
				'available_blocks' => self::get_availability(),
603
				'jetpack'          => array( 'is_active' => Jetpack::is_active() ),
604
				'siteFragment'     => $site_fragment,
605
			)
606
		);
607
608
		wp_set_script_translations( 'jetpack-blocks-editor', 'jetpack', plugins_url( 'languages/json', JETPACK__PLUGIN_FILE ) );
609
610
		// Adding a filter late to allow every other filter to process the path, including the CDN.
611
		add_filter( 'pre_load_script_translations', array( __CLASS__, 'filter_pre_load_script_translations' ), 1000, 3 );
612
613
		wp_enqueue_style( 'jetpack-blocks-editor', $editor_style, array(), $version );
614
	}
615
616
	/**
617
	 * A workaround for setting i18n data for WordPress client-side i18n mechanism.
618
	 * We are not yet using dotorg language packs for the editor file, so this short-circuits
619
	 * the translation loading and feeds our JSON data directly into the translation getter.
620
	 *
621
	 * @param NULL   $null     not used.
622
	 * @param String $file     the file path that is being loaded, ignored.
623
	 * @param String $handle   the script handle.
624
	 * @return NULL|String the translation data only if we're working with our handle.
625
	 */
626
	public static function filter_pre_load_script_translations( $null, $file, $handle ) {
627
		if ( 'jetpack-blocks-editor' !== $handle ) {
628
			return null;
629
		}
630
631
		return Jetpack::get_i18n_data_json();
632
	}
633
634
	/**
635
	 * Some blocks do not depend on a specific module,
636
	 * and can consequently be loaded outside of the usual modules.
637
	 * We will look for such modules in the extensions/ directory.
638
	 *
639
	 * @since 7.1.0
640
	 */
641
	public static function load_independent_blocks() {
642
		if ( self::should_load() ) {
643
			/**
644
			 * Look for files that match our list of available Jetpack Gutenberg extensions (blocks and plugins).
645
			 * If available, load them.
646
			 */
647
			foreach ( self::$extensions as $extension ) {
648
				$extension_file_glob = glob( JETPACK__PLUGIN_DIR . 'extensions/*/' . $extension . '/' . $extension . '.php' );
649
				if ( ! empty( $extension_file_glob ) ) {
650
					include_once $extension_file_glob[0];
651
				}
652
			}
653
		}
654
	}
655
}
656