Completed
Pull Request — master (#11091)
by Bernhard
19:04 queued 12:38
created

class.jetpack-gutenberg.php (4 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 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
 * @param string $slug Slug of the block
13
 * @param array  $args Arguments that are passed into the register_block_type.
14
 * @param array  $avalibility Arguments that tells us what kind of avalibility the block has
0 ignored issues
show
There is no parameter named $avalibility. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
15
 *
16
 * @see register_block_type
17
 *
18
 * @since 6.7.0
19
 *
20
 * @return void
21
 */
22
function jetpack_register_block( $slug, $args = array(), $availability = array( 'available' => true ) ) {
23
	Jetpack_Gutenberg::register( $slug, $args, $availability );
24
}
25
26
/**
27
 * Helper function to register a Jetpack Gutenberg plugin
28
 *
29
 * @param string $slug Slug of the plugin.
30
 * @param array  $avalibility Arguments that tells us what kind of avalibility the plugin has
0 ignored issues
show
There is no parameter named $avalibility. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
31
 *
32
 * @see register_block_type
33
 *
34
 * @since 6.9.0
35
 *
36
 * @return void
37
 */
38
function jetpack_register_plugin( $slug, $availability = array( 'available' => true ) ) {
39
	Jetpack_Gutenberg::register( $slug, array(), $availability );
40
}
41
42
/**
43
 * General Gutenberg editor specific functionality
44
 */
45
class Jetpack_Gutenberg {
46
47
	/**
48
	 * @var array Array of blocks information.
49
	 *
50
	 * For each block we have information about the availability for the current user
51
	 */
52
	private static $blocks = array();
53
54
	private static $availability = array();
55
56
	/**
57
	 * @var array Array of plugins information.
58
	 *
59
	 * For each block we have information about the availability for the current user
60
	 */
61
	private static $plugins = array();
62
63
	/**
64
	 * @var array Array of extensions we will be registering.
65
	 */
66
	private static $registered = array();
67
68
	/**
69
	 * Add a block to the list of blocks to be registered.
70
	 *
71
	 * @param string $slug Slug of the block.
72
	 * @param array  $args Arguments that are passed into the register_block_type.
73
	 * @param array $availability array containing if a block is available and the reason when it is not.
74
	 */
75
	public static function register( $slug, $args, $availability ) {
76
		$sanitized_slug = sanitize_title_with_dashes( $slug );
77
		self::$registered[ $sanitized_slug ] = array( 'args' => $args, 'availability' => $availability );
78
	}
79
80
	/**
81
	 * Register all Jetpack blocks available.
82
	 *
83
	 * @return void
84
	 */
85
	public static function init() {
86
		if ( ! self::is_gutenberg_available() ) {
87
			return;
88
		}
89
90
		if ( ! self::should_load() ) {
91
			return;
92
		}
93
94
		if ( Jetpack_Constants::is_true( 'REST_API_REQUEST' ) ) {
95
			// We defer the loading of the blocks until we have a better scope in reset requests.
96
			add_filter( 'rest_request_before_callbacks', array( __CLASS__, 'load' ), 10, 3 );
97
			return;
98
		}
99
		self::load();
100
	}
101
102
	/**
103
	 * @deprecated
104
	 */
105
	static function load_blocks() {
106
		self::init();
107
	}
108
109
	/**
110
	 * Add a block to the list of blocks to be registered.
111
	 *
112
	 * @param string $slug Slug of the block.
0 ignored issues
show
There is no parameter named $slug. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
113
	 * @param array  $args Arguments that are passed into the register_block_type.
114
	 * @param array $availability array containing if a block is available and the reason when it is not.
115
	 *
116
	 * @deprecated
117
	 */
118
	static function add_block( $type, $args, $availability ) {
119
		self::register( $type, $args, $availability );
120
	}
121
122
	static function load( $response = null, $handler = null, $request = null ) {
123
		$is_availability_endpoint_beta = ! is_null( $request ) && $request->get_param( 'beta' ) && wp_endswith( $request->get_route(), 'gutenberg/available-extensions' );
124
125
		/**
126
		 * Alternative to `JETPACK_BETA_BLOCKS`, set to `true` to load Beta Blocks.
127
		 *
128
		 * @since 6.9.0
129
		 *
130
		 * @param boolean
131
		 */
132
		if ( apply_filters( 'jetpack_load_beta_blocks', $is_availability_endpoint_beta ) ) {
133
			Jetpack_Constants::set_constant( 'JETPACK_BETA_BLOCKS', true );
134
		}
135
136
		/**
137
		 * Filter the list of block editor blocks that are available through jetpack.
138
		 *
139
		 * This filter is populated by Jetpack_Gutenberg::jetpack_set_available_blocks
140
		 *
141
		 * @since 6.8.0
142
		 *
143
		 * @param array
144
		 */
145
		self::$blocks = apply_filters( 'jetpack_set_available_blocks', array() );
146
147
		/**
148
		 * Filter the list of block editor plugins that are available through jetpack.
149
		 *
150
		 * This filter is populated by Jetpack_Gutenberg::jetpack_set_available_blocks
151
		 *
152
		 * @since 6.9.0
153
		 *
154
		 * @param array
155
		 */
156
		self::$plugins = apply_filters( 'jetpack_set_available_plugins', array() );
157
		self::set_blocks_availability();
158
		self::set_plugins_availability();
159
		self::register_blocks();
160
		return $response;
161
	}
162
163
	static function is_registered( $slug ) {
164
		return isset( self::$registered[ $slug ] );
165
	}
166
167
	static function get_registered_args( $slug ) {
168
		return self::$registered[ $slug ]['args'];
169
	}
170
	static function get_registered_type( $slug ) {
171
		return self::$registered[ $slug ]['type'];
172
	}
173
174
	static function is_available( $slug ) {
175
		if ( ! isset( self::$availability[ $slug ]['available'] ) ) {
176
			return false;
177
		}
178
		return (bool) self::$availability[ $slug ]['available'];
179
	}
180
181
182
	static function get_extension_availability( $slug ) {
183
		if ( ! self::is_registered( $slug ) ) {
184
			return array( 'available' => false, 'unavailable_reason' => 'missing_module' );
185
		}
186
187
		$availability = self::$registered[ $slug ]['availability'];
188
189
		if ( isset( $availability['callback'] ) ) {
190
			$availability = call_user_func( $availability['callback'] );
191
		}
192
193
		$availability['available'] = isset( $availability['available'] )
194
			? (bool) $availability['available']
195
			: true ; // this is the default value
196
197
		if ( ! $availability['available'] ) {
198
			$availability['unavailable_reason'] = isset( $availability['unavailable_reason'] )
199
				? $availability['unavailable_reason']
200
				: 'unknown'; //
201
		}
202
203
		return $availability;
204
	}
205
206
	static function set_blocks_availability() {
207
		foreach ( self::$blocks as $slug ) {
208
			self::$availability[ $slug ] = self::get_extension_availability( $slug );
209
		}
210
	}
211
212
	static function set_plugins_availability() {
213
		foreach ( self::$plugins as $slug ) {
214
			self::$availability[ $slug ] = self::get_extension_availability( $slug );
215
		}
216
	}
217
218
	static function register_blocks() {
219
		/**
220
		 * Filter the list of block editor plugins should be passed to register_block_type.
221
		 * You can use this to manually register some blocks without Jetpack registering them for you.
222
		 *
223
		 * This filter is populated by Jetpack_Gutenberg::jetpack_set_available_blocks
224
		 *
225
		 * @since 6.9.0
226
		 *
227
		 * @param array
228
		 */
229
		$blocks = apply_filters( 'jetpack_blocks_to_register', self::$blocks );
230
		foreach ( $blocks as $slug ) {
231
			if ( self::is_available( $slug ) ) {
232
				register_block_type( 'jetpack/' . $slug, self::get_registered_args( $slug ) );
233
			}
234
		}
235
	}
236
237
	/**
238
	 * Return the Gutenberg extensions (blocks and plugins) directory
239
	 *
240
	 * @return string The Gutenberg extensions directory
241
	 */
242
	public static function get_blocks_directory() {
243
		/**
244
		 * Filter to select Gutenberg blocks directory
245
		 *
246
		 * @since 6.9
247
		 *
248
		 * @param string default: '_inc/blocks/'
249
		 */
250
		return apply_filters( 'jetpack_blocks_directory', '_inc/blocks/' );
251
	}
252
253
	/**
254
	 * Checks for a given .json file in the blocks folder.
255
	 *
256
	 * @param $preset The name of the .json file to look for.
257
	 *
258
	 * @return bool True if the file is found.
259
	 */
260
	public static function preset_exists( $preset ) {
261
		return file_exists( JETPACK__PLUGIN_DIR . self::get_blocks_directory() . $preset . '.json' );
262
	}
263
264
	/**
265
	 * Decodes JSON loaded from a preset file in the blocks folder
266
	 *
267
	 * @param $preset The name of the .json file to load.
268
	 *
269
	 * @return mixed Returns an object if the file is present, or false if a valid .json file is not present.
270
	 */
271
	public static function get_preset( $preset ) {
272
		return json_decode( file_get_contents(  JETPACK__PLUGIN_DIR .self::get_blocks_directory() . $preset . '.json' ) );
273
	}
274
275
	/**
276
	 * Filters the results of `apply_filter( 'jetpack_set_available_blocks', array() )`
277
	 * using the merged contents of `blocks-manifest.json` ( $preset_blocks )
278
	 * and self::$jetpack_blocks ( $internal_blocks )
279
	 *
280
	 * @param $blocks The default list.
281
	 *
282
	 * @return array A list of blocks: eg [ 'publicize', 'markdown' ]
283
	 */
284
	public static function jetpack_set_available_blocks( $blocks ) {
285
		$preset_blocks_manifest =  self::preset_exists( 'index' ) ? self::get_preset( 'index' ) : (object) array( 'blocks' => $blocks );
286
		// manifest shouldn't remove default blocks...
287
288
		$preset_blocks = isset( $preset_blocks_manifest->production ) ? (array) $preset_blocks_manifest->production : array() ;
289
		$preset_blocks = array_unique( array_merge( $preset_blocks, $blocks ) );
290
291
		if ( Jetpack_Constants::is_true( 'JETPACK_BETA_BLOCKS' ) ) {
292
			$beta_blocks = isset( $preset_blocks_manifest->beta ) ? (array) $preset_blocks_manifest->beta : array();
293
			return array_unique( array_merge( $preset_blocks, $beta_blocks ) );
294
		}
295
296
		return $preset_blocks;
297
	}
298
299
	/**
300
	 * @deprecated
301
	 * @return array A list of block-availability information, eg: [ "publicize" => ["available" => true ], "markdown" => [ "available" => false, "unavailable_reason" => 'missing_module' ] ]
302
	 */
303
	public static function get_block_availability() {
304
		return self::get_availability();
305
	}
306
307
	/**
308
	 * @return array A list of block and plugins and their availablity status
309
	 */
310
	public static function get_availability() {
311
		if ( ! self::should_load() ) {
312
			return array();
313
		}
314
315
		return self::$availability;
316
	}
317
318
	/**
319
	 * Check if Gutenberg editor is available
320
	 *
321
	 * @since 6.7.0
322
	 *
323
	 * @return bool
324
	 */
325
	public static function is_gutenberg_available() {
326
		return function_exists( 'register_block_type' );
327
	}
328
329
	/**
330
	 * Check whether conditions indicate Gutenberg blocks should be loaded
331
	 *
332
	 * Loading blocks is enabled by default and may be disabled via filter:
333
	 *   add_filter( 'jetpack_gutenberg', '__return_false' );
334
	 *
335
	 * @since 6.7.0
336
	 * @deprecated
337
	 * @return bool
338
	 */
339
	public static function should_load_blocks() {
340
		return self::should_load();
341
	}
342
343
	/**
344
	 * Check whether conditions indicate Gutenberg Extensions (blocks and plugins) should be loaded
345
	 *
346
	 * Loading blocks and plugins is enabled by default and may be disabled via filter:
347
	 *   add_filter( 'jetpack_gutenberg', '__return_false' );
348
	 *
349
	 * @since 6.9.0
350
	 *
351
	 * @return bool
352
	 */
353
	public static function should_load() {
354
		if ( ! Jetpack::is_active() && ! Jetpack::is_development_mode() ) {
355
			return false;
356
		}
357
358
		/**
359
		 * Filter to disable Gutenberg blocks
360
		 *
361
		 * @since 6.5.0
362
		 *
363
		 * @param bool true Whether to load Gutenberg blocks
364
		 */
365
		return (bool) apply_filters( 'jetpack_gutenberg', true );
366
	}
367
368
	/**
369
	 * Only enqueue block assets when needed.
370
	 *
371
	 * @param string $type slug of the block.
372
	 * @param array $script_dependencies An array of view-side Javascript dependencies to be enqueued.
373
	 *
374
	 * @return void
375
	 */
376
	public static function load_assets_as_required( $type, $script_dependencies = array() ) {
377
		if ( is_admin() ) {
378
			// A block's view assets will not be required in wp-admin.
379
			return;
380
		}
381
382
		$type = sanitize_title_with_dashes( $type );
383
		// Enqueue styles.
384
		$style_relative_path = self::get_blocks_directory() . $type . '/view' . ( is_rtl() ? '.rtl' : '' ) . '.css';
385 View Code Duplication
		if ( self::block_has_asset( $style_relative_path ) ) {
386
			$style_version = self::get_asset_version( $style_relative_path );
387
			$view_style    = plugins_url( $style_relative_path, JETPACK__PLUGIN_FILE );
388
			wp_enqueue_style( 'jetpack-block-' . $type, $view_style, array(), $style_version );
389
		}
390
391
		// Enqueue script.
392
		$script_relative_path = self::get_blocks_directory() . $type . '/view.js';
393 View Code Duplication
		if ( self::block_has_asset( $script_relative_path ) ) {
394
			$script_version = self::get_asset_version( $script_relative_path );
395
			$view_script    = plugins_url( $script_relative_path, JETPACK__PLUGIN_FILE );
396
			wp_enqueue_script( 'jetpack-block-' . $type, $view_script, $script_dependencies, $script_version, false );
397
		}
398
399
		wp_localize_script(
400
			'jetpack-block-' . $type,
401
			'Jetpack_Block_Assets_Base_Url',
402
			plugins_url( self::get_blocks_directory(), JETPACK__PLUGIN_FILE )
403
		);
404
	}
405
406
	/**
407
	 * Check if an asset exists for a block.
408
	 *
409
	 * @param string $file Path of the file we are looking for.
410
	 *
411
	 * @return bool $block_has_asset Does the file exist.
412
	 */
413
	public static function block_has_asset( $file ) {
414
		return file_exists( JETPACK__PLUGIN_DIR . $file );
415
	}
416
417
	/**
418
	 * Get the version number to use when loading the file. Allows us to bypass cache when developing.
419
	 *
420
	 * @param string $file Path of the file we are looking for.
421
	 *
422
	 * @return string $script_version Version number.
423
	 */
424
	public static function get_asset_version( $file ) {
425
		return Jetpack::is_development_version() && self::block_has_asset( $file )
426
			? filemtime( JETPACK__PLUGIN_DIR . $file )
427
			: JETPACK__VERSION;
428
	}
429
430
	/**
431
	 * Load Gutenberg editor assets
432
	 *
433
	 * @since 6.7.0
434
	 *
435
	 * @return void
436
	 */
437
	public static function enqueue_block_editor_assets() {
438
		if ( ! self::should_load_blocks() ) {
439
			return;
440
		}
441
442
		$rtl = is_rtl() ? '.rtl' : '';
443
		$beta = Jetpack_Constants::is_true('JETPACK_BETA_BLOCKS' ) ? '-beta' : '';
444
		$blocks_dir = self::get_blocks_directory();
445
446
		$editor_script = plugins_url( "{$blocks_dir}editor{$beta}.js", JETPACK__PLUGIN_FILE );
447
		$editor_style  = plugins_url( "{$blocks_dir}editor{$beta}{$rtl}.css", JETPACK__PLUGIN_FILE );
448
449
		$version       = Jetpack::is_development_version() && file_exists( JETPACK__PLUGIN_DIR . $blocks_dir . 'editor.js' )
450
			? filemtime( JETPACK__PLUGIN_DIR . $blocks_dir . 'editor.js' )
451
			: JETPACK__VERSION;
452
453
		wp_enqueue_script(
454
			'jetpack-blocks-editor',
455
			$editor_script,
456
			array(
457
				'lodash',
458
				'wp-api-fetch',
459
				'wp-blob',
460
				'wp-blocks',
461
				'wp-components',
462
				'wp-compose',
463
				'wp-data',
464
				'wp-date',
465
				'wp-edit-post',
466
				'wp-editor',
467
				'wp-element',
468
				'wp-hooks',
469
				'wp-i18n',
470
				'wp-keycodes',
471
				'wp-plugins',
472
				'wp-rich-text',
473
				'wp-token-list',
474
				'wp-url',
475
			),
476
			$version,
477
			false
478
		);
479
480
		wp_localize_script(
481
			'jetpack-blocks-editor',
482
			'Jetpack_Block_Assets_Base_Url',
483
			plugins_url( $blocks_dir . '/', JETPACK__PLUGIN_FILE )
484
		);
485
486
		wp_localize_script(
487
			'jetpack-blocks-editor',
488
			'Jetpack_Editor_Initial_State',
489
			array(
490
				'available_blocks' => self::get_block_availability(),
0 ignored issues
show
Deprecated Code introduced by
The method Jetpack_Gutenberg::get_block_availability() has been deprecated.

This method has been deprecated.

Loading history...
491
				'jetpack' => array( 'is_active' => Jetpack::is_active() ),
492
			)
493
		);
494
495
		Jetpack::setup_wp_i18n_locale_data();
496
497
		wp_enqueue_style( 'jetpack-blocks-editor', $editor_style, array(), $version );
498
	}
499
}
500