Completed
Push — master ( 36e98a...2d3538 )
by Bernhard
07:39
created

class.jetpack-gutenberg.php (5 issues)

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
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
 * Helper function to register a Jetpack Gutenberg block
11
 *
12
 * @deprecated 7.1.0 Use (Gutenberg's) register_block_type() instead
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 void
22
 */
23
function jetpack_register_block( $slug, $args = array() ) {
24
	_deprecated_function( __FUNCTION__, '7.1', 'register_block_type' );
25
26
	Jetpack_Gutenberg::register_block( $slug, $args );
0 ignored issues
show
Deprecated Code introduced by
The method Jetpack_Gutenberg::register_block() has been deprecated with message: 7.1.0 Use (Gutenberg's) register_block_type() instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
27
}
28
29
/**
30
 * Helper function to register a Jetpack Gutenberg plugin
31
 *
32
 * @deprecated 7.1.0 Use Jetpack_Gutenberg::set_extension_available() instead
33
 *
34
 * @param string $slug Slug of the plugin.
35
 *
36
 * @since 6.9.0
37
 *
38
 * @return void
39
 */
40
function jetpack_register_plugin( $slug ) {
41
	_deprecated_function( __FUNCTION__, '7.1', 'Jetpack_Gutenberg::set_extension_available' );
42
43
	Jetpack_Gutenberg::register_plugin( $slug );
0 ignored issues
show
Deprecated Code introduced by
The method Jetpack_Gutenberg::register_plugin() has been deprecated with message: 7.1.0 Use Jetpack_Gutenberg::set_extension_available() instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
44
}
45
46
/**
47
 * Set the reason why an extension (block or plugin) is unavailable
48
 *
49
 * @deprecated 7.1.0 Use Jetpack_Gutenberg::set_extension_unavailable() instead
50
 *
51
 * @param string $slug Slug of the block.
52
 * @param string $reason A string representation of why the extension is unavailable.
53
 *
54
 * @since 7.0.0
55
 *
56
 * @return void
57
 */
58
function jetpack_set_extension_unavailability_reason( $slug, $reason ) {
59
	_deprecated_function( __FUNCTION__, '7.1', 'Jetpack_Gutenberg::set_extension_unavailable' );
60
61
	Jetpack_Gutenberg::set_extension_unavailability_reason( $slug, $reason );
0 ignored issues
show
Deprecated Code introduced by
The method Jetpack_Gutenberg::set_e...unavailability_reason() has been deprecated with message: 7.1.0 Use set_extension_unavailable() instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
62
}
63
64
/**
65
 * General Gutenberg editor specific functionality
66
 */
67
class Jetpack_Gutenberg {
68
69
	/**
70
	 * Only these extensions can be registered. Used to control availability of beta blocks.
71
	 *
72
	 * @var array Extensions whitelist
73
	 */
74
	private static $extensions = array();
75
76
	/**
77
	 * Keeps track of the reasons why a given extension is unavailable.
78
	 *
79
	 * @var array Extensions availability information
80
	 */
81
	private static $availability = array();
82
83
	/**
84
	 * Prepend the 'jetpack/' prefix to a block name
85
	 *
86
	 * @param string $block_name The block name.
87
	 *
88
	 * @return string The prefixed block name.
89
	 */
90
	private static function prepend_block_prefix( $block_name ) {
91
		return 'jetpack/' . $block_name;
92
	}
93
94
	/**
95
	 * Remove the 'jetpack/' or jetpack-' prefix from an extension name
96
	 *
97
	 * @param string $extension_name The extension name.
98
	 *
99
	 * @return string The unprefixed extension name.
100
	 */
101
	private static function remove_extension_prefix( $extension_name ) {
102
		if ( wp_startswith( $extension_name, 'jetpack/' ) || wp_startswith( $extension_name, 'jetpack-' ) ) {
103
			return substr( $extension_name, strlen( 'jetpack/' ) );
104
		}
105
		return $extension_name;
106
	}
107
108
	/**
109
	 * Whether two arrays share at least one item
110
	 *
111
	 * @param array $a An array.
112
	 * @param array $b Another array.
113
	 *
114
	 * @return boolean True if $a and $b share at least one item
115
	 */
116
	protected static function share_items( $a, $b ) {
117
		return count( array_intersect( $a, $b ) ) > 0;
118
	}
119
120
	/**
121
	 * Register a block
122
	 *
123
	 * @deprecated 7.1.0 Use (Gutenberg's) register_block_type() instead
124
	 *
125
	 * @param string $slug Slug of the block.
126
	 * @param array  $args Arguments that are passed into register_block_type().
127
	 */
128
	public static function register_block( $slug, $args ) {
129
		_deprecated_function( __METHOD__, '7.1', 'register_block_type' );
130
131
		register_block_type( 'jetpack/' . $slug, $args );
132
	}
133
134
	/**
135
	 * Register a plugin
136
	 *
137
	 * @deprecated 7.1.0 Use Jetpack_Gutenberg::set_extension_available() instead
138
	 *
139
	 * @param string $slug Slug of the plugin.
140
	 */
141
	public static function register_plugin( $slug ) {
142
		_deprecated_function( __METHOD__, '7.1', 'Jetpack_Gutenberg::set_extension_available' );
143
144
		self::set_extension_available( $slug );
145
	}
146
147
	/**
148
	 * Register a block
149
	 *
150
	 * @deprecated 7.0.0 Use register_block_type() instead
151
	 *
152
	 * @param string $slug Slug of the block.
153
	 * @param array  $args Arguments that are passed into the register_block_type.
154
	 * @param array  $availability array containing if a block is available and the reason when it is not.
155
	 */
156
	public static function register( $slug, $args, $availability ) {
157
		_deprecated_function( __METHOD__, '7.0', 'register_block_type' );
158
159
		if ( isset( $availability['available'] ) && ! $availability['available'] ) {
160
			self::set_extension_unavailability_reason( $slug, $availability['unavailable_reason'] );
0 ignored issues
show
Deprecated Code introduced by
The method Jetpack_Gutenberg::set_e...unavailability_reason() has been deprecated with message: 7.1.0 Use set_extension_unavailable() instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
161
		} else {
162
			self::register_block( $slug, $args );
0 ignored issues
show
Deprecated Code introduced by
The method Jetpack_Gutenberg::register_block() has been deprecated with message: 7.1.0 Use (Gutenberg's) register_block_type() instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
163
		}
164
	}
165
166
	/**
167
	 * Set a (non-block) extension as available
168
	 *
169
	 * @param string $slug Slug of the extension.
170
	 */
171
	public static function set_extension_available( $slug ) {
172
		self::$availability[ self::remove_extension_prefix( $slug ) ] = true;
173
	}
174
175
	/**
176
	 * Set the reason why an extension (block or plugin) is unavailable
177
	 *
178
	 * @param string $slug Slug of the extension.
179
	 * @param string $reason A string representation of why the extension is unavailable.
180
	 */
181
	public static function set_extension_unavailable( $slug, $reason ) {
182
		self::$availability[ self::remove_extension_prefix( $slug ) ] = $reason;
183
	}
184
185
	/**
186
	 * Set the reason why an extension (block or plugin) is unavailable
187
	 *
188
	 * @deprecated 7.1.0 Use set_extension_unavailable() instead
189
	 *
190
	 * @param string $slug Slug of the extension.
191
	 * @param string $reason A string representation of why the extension is unavailable.
192
	 */
193
	public static function set_extension_unavailability_reason( $slug, $reason ) {
194
		_deprecated_function( __METHOD__, '7.1', 'Jetpack_Gutenberg::set_extension_unavailable' );
195
196
		self::set_extension_unavailable( $slug, $reason );
197
	}
198
199
	/**
200
	 * Set up a whitelist of allowed block editor extensions
201
	 *
202
	 * @return void
203
	 */
204
	public static function init() {
205
		if ( ! self::is_gutenberg_available() ) {
206
			return;
207
		}
208
209
		if ( ! self::should_load() ) {
210
			return;
211
		}
212
213
		/**
214
		 * Alternative to `JETPACK_BETA_BLOCKS`, set to `true` to load Beta Blocks.
215
		 *
216
		 * @since 6.9.0
217
		 *
218
		 * @param boolean
219
		 */
220
		if ( apply_filters( 'jetpack_load_beta_blocks', false ) ) {
221
			Jetpack_Constants::set_constant( 'JETPACK_BETA_BLOCKS', true );
222
		}
223
224
		/**
225
		 * Filter the whitelist of block editor extensions that are available through Jetpack.
226
		 *
227
		 * @since 7.0.0
228
		 *
229
		 * @param array
230
		 */
231
		self::$extensions = apply_filters( 'jetpack_set_available_extensions', self::get_jetpack_gutenberg_extensions_whitelist() );
232
233
		/**
234
		 * Filter the whitelist of block editor plugins that are available through Jetpack.
235
		 *
236
		 * @deprecated 7.0.0 Use jetpack_set_available_extensions instead
237
		 *
238
		 * @since 6.8.0
239
		 *
240
		 * @param array
241
		 */
242
		self::$extensions = apply_filters( 'jetpack_set_available_blocks', self::$extensions );
243
244
		/**
245
		 * Filter the whitelist of block editor plugins that are available through Jetpack.
246
		 *
247
		 * @deprecated 7.0.0 Use jetpack_set_available_extensions instead
248
		 *
249
		 * @since 6.9.0
250
		 *
251
		 * @param array
252
		 */
253
		self::$extensions = apply_filters( 'jetpack_set_available_plugins', self::$extensions );
254
	}
255
256
	/**
257
	 * Resets the class to its original state
258
	 *
259
	 * Used in unit tests
260
	 *
261
	 * @return void
262
	 */
263
	public static function reset() {
264
		self::$extensions         = array();
265
		self::$availability       = array();
266
	}
267
268
	/**
269
	 * Return the Gutenberg extensions (blocks and plugins) directory
270
	 *
271
	 * @return string The Gutenberg extensions directory
272
	 */
273
	public static function get_blocks_directory() {
274
		/**
275
		 * Filter to select Gutenberg blocks directory
276
		 *
277
		 * @since 6.9.0
278
		 *
279
		 * @param string default: '_inc/blocks/'
280
		 */
281
		return apply_filters( 'jetpack_blocks_directory', '_inc/blocks/' );
282
	}
283
284
	/**
285
	 * Checks for a given .json file in the blocks folder.
286
	 *
287
	 * @param string $preset The name of the .json file to look for.
288
	 *
289
	 * @return bool True if the file is found.
290
	 */
291
	public static function preset_exists( $preset ) {
292
		return file_exists( JETPACK__PLUGIN_DIR . self::get_blocks_directory() . $preset . '.json' );
293
	}
294
295
	/**
296
	 * Decodes JSON loaded from a preset file in the blocks folder
297
	 *
298
	 * @param string $preset The name of the .json file to load.
299
	 *
300
	 * @return mixed Returns an object if the file is present, or false if a valid .json file is not present.
301
	 */
302
	public static function get_preset( $preset ) {
303
		return json_decode( file_get_contents( JETPACK__PLUGIN_DIR . self::get_blocks_directory() . $preset . '.json' ) );
304
	}
305
306
	/**
307
	 * Returns a whitelist of Jetpack Gutenberg extensions (blocks and plugins), based on index.json
308
	 *
309
	 * @return array A list of blocks: eg [ 'publicize', 'markdown' ]
310
	 */
311
	public static function get_jetpack_gutenberg_extensions_whitelist() {
312
		$preset_extensions_manifest = self::preset_exists( 'index' ) ? self::get_preset( 'index' ) : (object) array();
313
314
		$preset_extensions = isset( $preset_extensions_manifest->production ) ? (array) $preset_extensions_manifest->production : array();
315
316
		if ( Jetpack_Constants::is_true( 'JETPACK_BETA_BLOCKS' ) ) {
317
			$beta_extensions = isset( $preset_extensions_manifest->beta ) ? (array) $preset_extensions_manifest->beta : array();
318
			return array_unique( array_merge( $preset_extensions, $beta_extensions ) );
319
		}
320
321
		return $preset_extensions;
322
	}
323
324
	/**
325
	 * Get availability of each block / plugin.
326
	 *
327
	 * @return array A list of block and plugins and their availablity status
328
	 */
329
	public static function get_availability() {
330
		if ( ! self::is_gutenberg_available() ) {
331
			return array();
332
		}
333
334
		/**
335
		 * Fires before Gutenberg extensions availability is computed.
336
		 *
337
		 * In the function call you supply, use `register_block_type()` to set a block as available.
338
		 * Alternatively, use `Jetpack_Gutenberg::set_extension_available()` (for a non-block plugin), and
339
		 * `Jetpack_Gutenberg::set_extension_unavailable()` (if the block or plugin should not be registered
340
		 * but marked as unavailable).
341
		 *
342
		 * @since 7.0.0
343
		 */
344
		do_action( 'jetpack_register_gutenberg_extensions' );
345
346
		$available_extensions = array();
347
348
		foreach ( self::$extensions as $extension ) {
349
			$is_available = WP_Block_Type_Registry::get_instance()->is_registered( 'jetpack/' . $extension ) ||
350
			( isset( self::$availability[ $extension ] ) && self::$availability[ $extension ] === true );
351
352
			$available_extensions[ $extension ] = array(
353
				'available' => $is_available,
354
			);
355
356
			if ( ! $is_available ) {
357
				$reason = isset( self::$availability[ $extension ] ) ? self::$availability[ $extension ] : 'missing_module';
358
				$available_extensions[ $extension ]['unavailable_reason'] = $reason;
359
			}
360
		}
361
362
		$unwhitelisted_blocks = array();
363
		$all_registered_blocks = WP_Block_Type_Registry::get_instance()->get_all_registered();
364
		foreach ( $all_registered_blocks as $block_name => $block_type ) {
365
			if ( ! wp_startswith( $block_name, 'jetpack/' ) || isset( $block_type->parent ) ) {
366
				continue;
367
			}
368
369
			$unprefixed_block_name = self::remove_extension_prefix( $block_name );
370
371
			if ( in_array( $unprefixed_block_name, self::$extensions ) ) {
372
				continue;
373
			}
374
375
			$unwhitelisted_blocks[ $unprefixed_block_name ] = array(
376
				'available'          => false,
377
				'unavailable_reason' => 'not_whitelisted',
378
			);
379
		}
380
381
		// Finally: Unwhitelisted non-block extensions. These are in $availability.
382
		$unwhitelisted_extensions = array_fill_keys(
383
			array_diff( array_keys( self::$availability ), self::$extensions ),
384
			array(
385
				'available'          => false,
386
				'unavailable_reason' => 'not_whitelisted',
387
			)
388
		);
389
		return array_merge( $available_extensions, $unwhitelisted_blocks, $unwhitelisted_extensions );
390
	}
391
392
	/**
393
	 * Check if Gutenberg editor is available
394
	 *
395
	 * @since 6.7.0
396
	 *
397
	 * @return bool
398
	 */
399
	public static function is_gutenberg_available() {
400
		return function_exists( 'register_block_type' );
401
	}
402
403
	/**
404
	 * Check whether conditions indicate Gutenberg Extensions (blocks and plugins) should be loaded
405
	 *
406
	 * Loading blocks and plugins is enabled by default and may be disabled via filter:
407
	 *   add_filter( 'jetpack_gutenberg', '__return_false' );
408
	 *
409
	 * @since 6.9.0
410
	 *
411
	 * @return bool
412
	 */
413
	public static function should_load() {
414
		if ( ! Jetpack::is_active() && ! Jetpack::is_development_mode() ) {
415
			return false;
416
		}
417
418
		/**
419
		 * Filter to disable Gutenberg blocks
420
		 *
421
		 * @since 6.5.0
422
		 *
423
		 * @param bool true Whether to load Gutenberg blocks
424
		 */
425
		return (bool) apply_filters( 'jetpack_gutenberg', true );
426
	}
427
428
	/**
429
	 * Only enqueue block assets when needed.
430
	 *
431
	 * @param string $type slug of the block.
432
	 * @param array  $script_dependencies An array of view-side Javascript dependencies to be enqueued.
433
	 *
434
	 * @return void
435
	 */
436
	public static function load_assets_as_required( $type, $script_dependencies = array() ) {
437
		if ( is_admin() ) {
438
			// A block's view assets will not be required in wp-admin.
439
			return;
440
		}
441
442
		$type = sanitize_title_with_dashes( $type );
443
		// Enqueue styles.
444
		$style_relative_path = self::get_blocks_directory() . $type . '/view' . ( is_rtl() ? '.rtl' : '' ) . '.css';
445 View Code Duplication
		if ( self::block_has_asset( $style_relative_path ) ) {
446
			$style_version = self::get_asset_version( $style_relative_path );
447
			$view_style    = plugins_url( $style_relative_path, JETPACK__PLUGIN_FILE );
448
			wp_enqueue_style( 'jetpack-block-' . $type, $view_style, array(), $style_version );
449
		}
450
451
		// Enqueue script.
452
		$script_relative_path = self::get_blocks_directory() . $type . '/view.js';
453 View Code Duplication
		if ( self::block_has_asset( $script_relative_path ) ) {
454
			$script_version = self::get_asset_version( $script_relative_path );
455
			$view_script    = plugins_url( $script_relative_path, JETPACK__PLUGIN_FILE );
456
			wp_enqueue_script( 'jetpack-block-' . $type, $view_script, $script_dependencies, $script_version, false );
457
		}
458
459
		wp_localize_script(
460
			'jetpack-block-' . $type,
461
			'Jetpack_Block_Assets_Base_Url',
462
			plugins_url( self::get_blocks_directory(), JETPACK__PLUGIN_FILE )
463
		);
464
	}
465
466
	/**
467
	 * Check if an asset exists for a block.
468
	 *
469
	 * @param string $file Path of the file we are looking for.
470
	 *
471
	 * @return bool $block_has_asset Does the file exist.
472
	 */
473
	public static function block_has_asset( $file ) {
474
		return file_exists( JETPACK__PLUGIN_DIR . $file );
475
	}
476
477
	/**
478
	 * Get the version number to use when loading the file. Allows us to bypass cache when developing.
479
	 *
480
	 * @param string $file Path of the file we are looking for.
481
	 *
482
	 * @return string $script_version Version number.
483
	 */
484
	public static function get_asset_version( $file ) {
485
		return Jetpack::is_development_version() && self::block_has_asset( $file )
486
			? filemtime( JETPACK__PLUGIN_DIR . $file )
487
			: JETPACK__VERSION;
488
	}
489
490
	/**
491
	 * Load Gutenberg editor assets
492
	 *
493
	 * @since 6.7.0
494
	 *
495
	 * @return void
496
	 */
497
	public static function enqueue_block_editor_assets() {
498
		if ( ! self::should_load() ) {
499
			return;
500
		}
501
502
		$rtl        = is_rtl() ? '.rtl' : '';
503
		$beta       = Jetpack_Constants::is_true( 'JETPACK_BETA_BLOCKS' ) ? '-beta' : '';
504
		$blocks_dir = self::get_blocks_directory();
505
506
		$editor_script = plugins_url( "{$blocks_dir}editor{$beta}.js", JETPACK__PLUGIN_FILE );
507
		$editor_style  = plugins_url( "{$blocks_dir}editor{$beta}{$rtl}.css", JETPACK__PLUGIN_FILE );
508
509
		$version = Jetpack::is_development_version() && file_exists( JETPACK__PLUGIN_DIR . $blocks_dir . 'editor.js' )
510
			? filemtime( JETPACK__PLUGIN_DIR . $blocks_dir . 'editor.js' )
511
			: JETPACK__VERSION;
512
513
		wp_enqueue_script(
514
			'jetpack-blocks-editor',
515
			$editor_script,
516
			array(
517
				'lodash',
518
				'wp-api-fetch',
519
				'wp-blob',
520
				'wp-blocks',
521
				'wp-components',
522
				'wp-compose',
523
				'wp-data',
524
				'wp-date',
525
				'wp-edit-post',
526
				'wp-editor',
527
				'wp-element',
528
				'wp-hooks',
529
				'wp-i18n',
530
				'wp-keycodes',
531
				'wp-plugins',
532
				'wp-rich-text',
533
				'wp-token-list',
534
				'wp-url',
535
			),
536
			$version,
537
			false
538
		);
539
540
		wp_localize_script(
541
			'jetpack-blocks-editor',
542
			'Jetpack_Block_Assets_Base_Url',
543
			plugins_url( $blocks_dir . '/', JETPACK__PLUGIN_FILE )
544
		);
545
546
		wp_localize_script(
547
			'jetpack-blocks-editor',
548
			'Jetpack_Editor_Initial_State',
549
			array(
550
				'available_blocks' => self::get_availability(),
551
				'jetpack'          => array( 'is_active' => Jetpack::is_active() ),
552
			)
553
		);
554
555
		Jetpack::setup_wp_i18n_locale_data();
556
557
		wp_enqueue_style( 'jetpack-blocks-editor', $editor_style, array(), $version );
558
	}
559
}
560