Completed
Push — add/google-calendar-block ( ab5496...778175 )
by
unknown
06:55
created

Jetpack_Gutenberg::is_block_version_gated()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
nc 6
nop 1
dl 0
loc 22
rs 8.9457
c 0
b 0
f 0
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
use Automattic\Jetpack\Status;
11
12
/**
13
 * Wrapper function to safely register a gutenberg block type
14
 *
15
 * @param string $slug Slug of the block.
16
 * @param array  $args Arguments that are passed into register_block_type.
17
 *
18
 * @see register_block_type
19
 *
20
 * @since 6.7.0
21
 *
22
 * @return WP_Block_Type|false The registered block type on success, or false on failure.
23
 */
24
function jetpack_register_block( $slug, $args = array() ) {
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
30
	if ( isset( $args['version_requirements'] ) && Jetpack_Gutenberg::is_block_version_gated( $args['version_requirements'] ) ) {
31
		return false;
32
	}
33
34
	// Checking whether block is registered to ensure it isn't registered twice.
35
	if ( Jetpack_Gutenberg::is_registered( $slug ) ) {
36
		return false;
37
	}
38
39
	return register_block_type( $slug, $args );
40
}
41
42
/**
43
 * Helper function to register a Jetpack Gutenberg plugin
44
 *
45
 * @deprecated 7.1.0 Use Jetpack_Gutenberg::set_extension_available() instead
46
 *
47
 * @param string $slug Slug of the plugin.
48
 *
49
 * @since 6.9.0
50
 *
51
 * @return void
52
 */
53
function jetpack_register_plugin( $slug ) {
54
	_deprecated_function( __FUNCTION__, '7.1', 'Jetpack_Gutenberg::set_extension_available' );
55
56
	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...
57
}
58
59
/**
60
 * Set the reason why an extension (block or plugin) is unavailable
61
 *
62
 * @deprecated 7.1.0 Use Jetpack_Gutenberg::set_extension_unavailable() instead
63
 *
64
 * @param string $slug Slug of the block.
65
 * @param string $reason A string representation of why the extension is unavailable.
66
 *
67
 * @since 7.0.0
68
 *
69
 * @return void
70
 */
71
function jetpack_set_extension_unavailability_reason( $slug, $reason ) {
72
	_deprecated_function( __FUNCTION__, '7.1', 'Jetpack_Gutenberg::set_extension_unavailable' );
73
74
	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...
75
}
76
77
/**
78
 * General Gutenberg editor specific functionality
79
 */
80
class Jetpack_Gutenberg {
81
82
	/**
83
	 * Only these extensions can be registered. Used to control availability of beta blocks.
84
	 *
85
	 * @var array Extensions whitelist
86
	 */
87
	private static $extensions = array();
88
89
	/**
90
	 * Keeps track of the reasons why a given extension is unavailable.
91
	 *
92
	 * @var array Extensions availability information
93
	 */
94
	private static $availability = array();
95
96
	/**
97
	 * Check to see if a block is able to run with current wp or gutenburg plugin versions
98
	 *
99
	 * @param array $version_requirements An arry containing the minimum 'wp' and 'plugin' versions for the block.
100
	 *
101
	 * @return boolean True if the block is gated due to current wp or plugin versions.
102
	 */
103
	public static function is_block_version_gated( $version_requirements ) {
104
		global $wp_version;
105
106
		if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
107
			return false;
108
		}
109
110
		if ( is_file( WP_PLUGIN_DIR . '/gutenberg/gutenberg.php' ) ) {
111
112
			if ( ! function_exists( 'get_plugin_data' ) ) {
113
				require_once ABSPATH . 'wp-admin/includes/plugin.php';
114
			}
115
116
			$plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/gutenberg/gutenberg.php' );
117
118
			if ( isset( $plugin_data['Version'] ) ) {
119
				return $plugin_data['Version'] < $version_requirements['plugin'];
120
			}
121
		}
122
123
		return version_compare( $wp_version, $version_requirements['wp'], '<' );
124
	}
125
126
	/**
127
	 * Prepend the 'jetpack/' prefix to a block name
128
	 *
129
	 * @param string $block_name The block name.
130
	 *
131
	 * @return string The prefixed block name.
132
	 */
133
	private static function prepend_block_prefix( $block_name ) {
134
		return 'jetpack/' . $block_name;
135
	}
136
137
	/**
138
	 * Remove the 'jetpack/' or jetpack-' prefix from an extension name
139
	 *
140
	 * @param string $extension_name The extension name.
141
	 *
142
	 * @return string The unprefixed extension name.
143
	 */
144
	private static function remove_extension_prefix( $extension_name ) {
145
		if ( wp_startswith( $extension_name, 'jetpack/' ) || wp_startswith( $extension_name, 'jetpack-' ) ) {
146
			return substr( $extension_name, strlen( 'jetpack/' ) );
147
		}
148
		return $extension_name;
149
	}
150
151
	/**
152
	 * Whether two arrays share at least one item
153
	 *
154
	 * @param array $a An array.
155
	 * @param array $b Another array.
156
	 *
157
	 * @return boolean True if $a and $b share at least one item
158
	 */
159
	protected static function share_items( $a, $b ) {
160
		return count( array_intersect( $a, $b ) ) > 0;
161
	}
162
163
	/**
164
	 * Register a block
165
	 *
166
	 * @deprecated 7.1.0 Use jetpack_register_block() instead
167
	 *
168
	 * @param string $slug Slug of the block.
169
	 * @param array  $args Arguments that are passed into register_block_type().
170
	 */
171
	public static function register_block( $slug, $args ) {
172
		_deprecated_function( __METHOD__, '7.1', 'jetpack_register_block' );
173
174
		jetpack_register_block( 'jetpack/' . $slug, $args );
175
	}
176
177
	/**
178
	 * Register a plugin
179
	 *
180
	 * @deprecated 7.1.0 Use Jetpack_Gutenberg::set_extension_available() instead
181
	 *
182
	 * @param string $slug Slug of the plugin.
183
	 */
184
	public static function register_plugin( $slug ) {
185
		_deprecated_function( __METHOD__, '7.1', 'Jetpack_Gutenberg::set_extension_available' );
186
187
		self::set_extension_available( $slug );
188
	}
189
190
	/**
191
	 * Register a block
192
	 *
193
	 * @deprecated 7.0.0 Use jetpack_register_block() instead
194
	 *
195
	 * @param string $slug Slug of the block.
196
	 * @param array  $args Arguments that are passed into the register_block_type.
197
	 * @param array  $availability array containing if a block is available and the reason when it is not.
198
	 */
199
	public static function register( $slug, $args, $availability ) {
200
		_deprecated_function( __METHOD__, '7.0', 'jetpack_register_block' );
201
202
		if ( isset( $availability['available'] ) && ! $availability['available'] ) {
203
			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...
204
		} else {
205
			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 jetpack_register_block() 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...
206
		}
207
	}
208
209
	/**
210
	 * Set a (non-block) extension as available
211
	 *
212
	 * @param string $slug Slug of the extension.
213
	 */
214
	public static function set_extension_available( $slug ) {
215
		self::$availability[ self::remove_extension_prefix( $slug ) ] = true;
216
	}
217
218
	/**
219
	 * Set the reason why an extension (block or plugin) is unavailable
220
	 *
221
	 * @param string $slug Slug of the extension.
222
	 * @param string $reason A string representation of why the extension is unavailable.
223
	 * @param array  $details A free-form array containing more information on why the extension is unavailable.
224
	 */
225
	public static function set_extension_unavailable( $slug, $reason, $details = array() ) {
226
		if (
227
			// Extensions that require a plan may be eligible for upgrades.
228
			'missing_plan' === $reason
229
			&& (
230
				/**
231
				 * Filter 'jetpack_block_editor_enable_upgrade_nudge' with `true` to enable or `false`
232
				 * to disable paid feature upgrade nudges in the block editor.
233
				 *
234
				 * When this is changed to default to `true`, you should also update `modules/memberships/class-jetpack-memberships.php`
235
				 * See https://github.com/Automattic/jetpack/pull/13394#pullrequestreview-293063378
236
				 *
237
				 * @since 7.7.0
238
				 *
239
				 * @param boolean
240
				 */
241
				! apply_filters( 'jetpack_block_editor_enable_upgrade_nudge', false )
242
				/** This filter is documented in _inc/lib/admin-pages/class.jetpack-react-page.php */
243
				|| ! apply_filters( 'jetpack_show_promotions', true )
244
			)
245
		) {
246
			// The block editor may apply an upgrade nudge if `missing_plan` is the reason.
247
			// Add a descriptive suffix to disable behavior but provide informative reason.
248
			$reason .= '__nudge_disabled';
249
		}
250
251
		self::$availability[ self::remove_extension_prefix( $slug ) ] = array(
252
			'reason'  => $reason,
253
			'details' => $details,
254
		);
255
	}
256
257
	/**
258
	 * Set the reason why an extension (block or plugin) is unavailable
259
	 *
260
	 * @deprecated 7.1.0 Use set_extension_unavailable() instead
261
	 *
262
	 * @param string $slug Slug of the extension.
263
	 * @param string $reason A string representation of why the extension is unavailable.
264
	 */
265
	public static function set_extension_unavailability_reason( $slug, $reason ) {
266
		_deprecated_function( __METHOD__, '7.1', 'Jetpack_Gutenberg::set_extension_unavailable' );
267
268
		self::set_extension_unavailable( $slug, $reason );
269
	}
270
271
	/**
272
	 * Set up a whitelist of allowed block editor extensions
273
	 *
274
	 * @return void
275
	 */
276
	public static function init() {
277
		if ( ! self::should_load() ) {
278
			return;
279
		}
280
281
		/**
282
		 * Alternative to `JETPACK_BETA_BLOCKS`, set to `true` to load Beta Blocks.
283
		 *
284
		 * @since 6.9.0
285
		 *
286
		 * @param boolean
287
		 */
288
		if ( apply_filters( 'jetpack_load_beta_blocks', false ) ) {
289
			Constants::set_constant( 'JETPACK_BETA_BLOCKS', true );
0 ignored issues
show
Documentation introduced by
true is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
290
		}
291
292
		/**
293
		 * Filter the whitelist of block editor extensions that are available through Jetpack.
294
		 *
295
		 * @since 7.0.0
296
		 *
297
		 * @param array
298
		 */
299
		self::$extensions = apply_filters( 'jetpack_set_available_extensions', self::get_available_extensions() );
300
301
		/**
302
		 * Filter the whitelist of block editor plugins that are available through Jetpack.
303
		 *
304
		 * @deprecated 7.0.0 Use jetpack_set_available_extensions instead
305
		 *
306
		 * @since 6.8.0
307
		 *
308
		 * @param array
309
		 */
310
		self::$extensions = apply_filters( 'jetpack_set_available_blocks', self::$extensions );
311
312
		/**
313
		 * Filter the whitelist of block editor plugins that are available through Jetpack.
314
		 *
315
		 * @deprecated 7.0.0 Use jetpack_set_available_extensions instead
316
		 *
317
		 * @since 6.9.0
318
		 *
319
		 * @param array
320
		 */
321
		self::$extensions = apply_filters( 'jetpack_set_available_plugins', self::$extensions );
322
	}
323
324
	/**
325
	 * Resets the class to its original state
326
	 *
327
	 * Used in unit tests
328
	 *
329
	 * @return void
330
	 */
331
	public static function reset() {
332
		self::$extensions   = array();
333
		self::$availability = array();
334
	}
335
336
	/**
337
	 * Return the Gutenberg extensions (blocks and plugins) directory
338
	 *
339
	 * @return string The Gutenberg extensions directory
340
	 */
341
	public static function get_blocks_directory() {
342
		/**
343
		 * Filter to select Gutenberg blocks directory
344
		 *
345
		 * @since 6.9.0
346
		 *
347
		 * @param string default: '_inc/blocks/'
348
		 */
349
		return apply_filters( 'jetpack_blocks_directory', '_inc/blocks/' );
350
	}
351
352
	/**
353
	 * Checks for a given .json file in the blocks folder.
354
	 *
355
	 * @param string $preset The name of the .json file to look for.
356
	 *
357
	 * @return bool True if the file is found.
358
	 */
359
	public static function preset_exists( $preset ) {
360
		return file_exists( JETPACK__PLUGIN_DIR . self::get_blocks_directory() . $preset . '.json' );
361
	}
362
363
	/**
364
	 * Decodes JSON loaded from a preset file in the blocks folder
365
	 *
366
	 * @param string $preset The name of the .json file to load.
367
	 *
368
	 * @return mixed Returns an object if the file is present, or false if a valid .json file is not present.
369
	 */
370
	public static function get_preset( $preset ) {
371
		return json_decode(
372
			// phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
373
			file_get_contents( JETPACK__PLUGIN_DIR . self::get_blocks_directory() . $preset . '.json' )
374
		);
375
	}
376
377
	/**
378
	 * Returns a whitelist of Jetpack Gutenberg extensions (blocks and plugins), based on index.json
379
	 *
380
	 * @return array A list of blocks: eg [ 'publicize', 'markdown' ]
381
	 */
382
	public static function get_jetpack_gutenberg_extensions_whitelist() {
383
		$preset_extensions_manifest = self::preset_exists( 'index' )
384
			? self::get_preset( 'index' )
385
			: (object) array();
386
		$blocks_variation           = self::blocks_variation();
387
388
		return self::get_extensions_preset_for_variation( $preset_extensions_manifest, $blocks_variation );
389
	}
390
391
	/**
392
	 * Returns a diff from a combined list of whitelisted extensions and extensions determined to be excluded
393
	 *
394
	 * @param  array $whitelisted_extensions An array of whitelisted extensions.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $whitelisted_extensions not be array|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
395
	 *
396
	 * @return array A list of blocks: eg array( 'publicize', 'markdown' )
397
	 */
398
	public static function get_available_extensions( $whitelisted_extensions = null ) {
399
		$exclusions             = get_option( 'jetpack_excluded_extensions', array() );
400
		$whitelisted_extensions = is_null( $whitelisted_extensions ) ? self::get_jetpack_gutenberg_extensions_whitelist() : $whitelisted_extensions;
401
402
		return array_diff( $whitelisted_extensions, $exclusions );
403
	}
404
405
	/**
406
	 * Get availability of each block / plugin.
407
	 *
408
	 * @return array A list of block and plugins and their availablity status
409
	 */
410
	public static function get_availability() {
411
		/**
412
		 * Fires before Gutenberg extensions availability is computed.
413
		 *
414
		 * In the function call you supply, use `jetpack_register_block()` to set a block as available.
415
		 * Alternatively, use `Jetpack_Gutenberg::set_extension_available()` (for a non-block plugin), and
416
		 * `Jetpack_Gutenberg::set_extension_unavailable()` (if the block or plugin should not be registered
417
		 * but marked as unavailable).
418
		 *
419
		 * @since 7.0.0
420
		 */
421
		do_action( 'jetpack_register_gutenberg_extensions' );
422
423
		$available_extensions = array();
424
425
		foreach ( self::$extensions as $extension ) {
426
			$is_available = self::is_registered( 'jetpack/' . $extension ) ||
427
			( isset( self::$availability[ $extension ] ) && true === self::$availability[ $extension ] );
428
429
			$available_extensions[ $extension ] = array(
430
				'available' => $is_available,
431
			);
432
433
			if ( ! $is_available ) {
434
				$reason  = isset( self::$availability[ $extension ] ) ? self::$availability[ $extension ]['reason'] : 'missing_module';
435
				$details = isset( self::$availability[ $extension ] ) ? self::$availability[ $extension ]['details'] : array();
436
				$available_extensions[ $extension ]['unavailable_reason'] = $reason;
437
				$available_extensions[ $extension ]['details']            = $details;
438
			}
439
		}
440
441
		return $available_extensions;
442
	}
443
444
	/**
445
	 * Check if an extension/block is already registered
446
	 *
447
	 * @since 7.2
448
	 *
449
	 * @param string $slug Name of extension/block to check.
450
	 *
451
	 * @return bool
452
	 */
453
	public static function is_registered( $slug ) {
454
		return WP_Block_Type_Registry::get_instance()->is_registered( $slug );
455
	}
456
457
	/**
458
	 * Check if Gutenberg editor is available
459
	 *
460
	 * @since 6.7.0
461
	 *
462
	 * @return bool
463
	 */
464
	public static function is_gutenberg_available() {
465
		return true;
466
	}
467
468
	/**
469
	 * Check whether conditions indicate Gutenberg Extensions (blocks and plugins) should be loaded
470
	 *
471
	 * Loading blocks and plugins is enabled by default and may be disabled via filter:
472
	 *   add_filter( 'jetpack_gutenberg', '__return_false' );
473
	 *
474
	 * @since 6.9.0
475
	 *
476
	 * @return bool
477
	 */
478
	public static function should_load() {
479
		if ( ! Jetpack::is_active() && ! ( new Status() )->is_development_mode() ) {
480
			return false;
481
		}
482
483
		/**
484
		 * Filter to disable Gutenberg blocks
485
		 *
486
		 * @since 6.5.0
487
		 *
488
		 * @param bool true Whether to load Gutenberg blocks
489
		 */
490
		return (bool) apply_filters( 'jetpack_gutenberg', true );
491
	}
492
493
	/**
494
	 * Only enqueue block assets when needed.
495
	 *
496
	 * @param string $type Slug of the block.
497
	 * @param array  $script_dependencies Script dependencies. Will be merged with automatically
498
	 *                                    detected script dependencies from the webpack build.
499
	 *
500
	 * @return void
501
	 */
502
	public static function load_assets_as_required( $type, $script_dependencies = array() ) {
503
		if ( is_admin() ) {
504
			// A block's view assets will not be required in wp-admin.
505
			return;
506
		}
507
508
		$type = sanitize_title_with_dashes( $type );
509
		self::load_styles_as_required( $type );
510
		self::load_scripts_as_required( $type, $script_dependencies );
511
	}
512
513
	/**
514
	 * Only enqueue block sytles when needed.
515
	 *
516
	 * @param string $type Slug of the block.
517
	 *
518
	 * @since 7.2.0
519
	 *
520
	 * @return void
521
	 */
522
	public static function load_styles_as_required( $type ) {
523
		if ( is_admin() ) {
524
			// A block's view assets will not be required in wp-admin.
525
			return;
526
		}
527
528
		// Enqueue styles.
529
		$style_relative_path = self::get_blocks_directory() . $type . '/view' . ( is_rtl() ? '.rtl' : '' ) . '.css';
530 View Code Duplication
		if ( self::block_has_asset( $style_relative_path ) ) {
531
			$style_version = self::get_asset_version( $style_relative_path );
532
			$view_style    = plugins_url( $style_relative_path, JETPACK__PLUGIN_FILE );
533
			wp_enqueue_style( 'jetpack-block-' . $type, $view_style, array(), $style_version );
534
		}
535
536
	}
537
538
	/**
539
	 * Only enqueue block scripts when needed.
540
	 *
541
	 * @param string $type Slug of the block.
542
	 * @param array  $dependencies Script dependencies. Will be merged with automatically
543
	 *                             detected script dependencies from the webpack build.
544
	 *
545
	 * @since 7.2.0
546
	 *
547
	 * @return void
548
	 */
549
	public static function load_scripts_as_required( $type, $dependencies = array() ) {
550
		if ( is_admin() ) {
551
			// A block's view assets will not be required in wp-admin.
552
			return;
553
		}
554
555
		// Enqueue script.
556
		$script_relative_path = self::get_blocks_directory() . $type . '/view.js';
557
		$script_deps_path     = JETPACK__PLUGIN_DIR . self::get_blocks_directory() . $type . '/view.asset.php';
558
		$script_dependencies  = array( 'wp-polyfill' );
559
		if ( file_exists( $script_deps_path ) ) {
560
			$asset_manifest      = include $script_deps_path;
561
			$script_dependencies = $asset_manifest['dependencies'];
562
		}
563
564
		if ( ( ! class_exists( 'Jetpack_AMP_Support' ) || ! Jetpack_AMP_Support::is_amp_request() ) && self::block_has_asset( $script_relative_path ) ) {
565
			$script_version = self::get_asset_version( $script_relative_path );
566
			$view_script    = plugins_url( $script_relative_path, JETPACK__PLUGIN_FILE );
567
			wp_enqueue_script( 'jetpack-block-' . $type, $view_script, $script_dependencies, $script_version, false );
568
		}
569
570
		wp_localize_script(
571
			'jetpack-block-' . $type,
572
			'Jetpack_Block_Assets_Base_Url',
573
			plugins_url( self::get_blocks_directory(), JETPACK__PLUGIN_FILE )
574
		);
575
	}
576
577
	/**
578
	 * Check if an asset exists for a block.
579
	 *
580
	 * @param string $file Path of the file we are looking for.
581
	 *
582
	 * @return bool $block_has_asset Does the file exist.
583
	 */
584
	public static function block_has_asset( $file ) {
585
		return file_exists( JETPACK__PLUGIN_DIR . $file );
586
	}
587
588
	/**
589
	 * Get the version number to use when loading the file. Allows us to bypass cache when developing.
590
	 *
591
	 * @param string $file Path of the file we are looking for.
592
	 *
593
	 * @return string $script_version Version number.
594
	 */
595
	public static function get_asset_version( $file ) {
596
		return Jetpack::is_development_version() && self::block_has_asset( $file )
597
			? filemtime( JETPACK__PLUGIN_DIR . $file )
598
			: JETPACK__VERSION;
599
	}
600
601
	/**
602
	 * Load Gutenberg editor assets
603
	 *
604
	 * @since 6.7.0
605
	 *
606
	 * @return void
607
	 */
608
	public static function enqueue_block_editor_assets() {
609
		if ( ! self::should_load() ) {
610
			return;
611
		}
612
613
		// Required for Analytics. See _inc/lib/admin-pages/class.jetpack-admin-page.php.
614
		if ( ! ( new Status() )->is_development_mode() && Jetpack::is_active() ) {
615
			wp_enqueue_script( 'jp-tracks', '//stats.wp.com/w.js', array(), gmdate( 'YW' ), true );
616
		}
617
618
		$rtl              = is_rtl() ? '.rtl' : '';
619
		$blocks_dir       = self::get_blocks_directory();
620
		$blocks_variation = self::blocks_variation();
621
622
		if ( 'production' !== $blocks_variation ) {
623
			$blocks_env = '-' . esc_attr( $blocks_variation );
624
		} else {
625
			$blocks_env = '';
626
		}
627
628
		$editor_script = plugins_url( "{$blocks_dir}editor{$blocks_env}.js", JETPACK__PLUGIN_FILE );
629
		$editor_style  = plugins_url( "{$blocks_dir}editor{$blocks_env}{$rtl}.css", JETPACK__PLUGIN_FILE );
630
631
		$editor_deps_path = JETPACK__PLUGIN_DIR . $blocks_dir . "editor{$blocks_env}.asset.php";
632
		$editor_deps      = array( 'wp-polyfill' );
633
		if ( file_exists( $editor_deps_path ) ) {
634
			$asset_manifest = include $editor_deps_path;
635
			$editor_deps    = $asset_manifest['dependencies'];
636
		}
637
638
		$version = Jetpack::is_development_version() && file_exists( JETPACK__PLUGIN_DIR . $blocks_dir . 'editor.js' )
639
			? filemtime( JETPACK__PLUGIN_DIR . $blocks_dir . 'editor.js' )
640
			: JETPACK__VERSION;
641
642
		if ( method_exists( 'Jetpack', 'build_raw_urls' ) ) {
643
			$site_fragment = Jetpack::build_raw_urls( home_url() );
644
		} elseif ( class_exists( 'WPCOM_Masterbar' ) && method_exists( 'WPCOM_Masterbar', 'get_calypso_site_slug' ) ) {
645
			$site_fragment = WPCOM_Masterbar::get_calypso_site_slug( get_current_blog_id() );
646
		} else {
647
			$site_fragment = '';
648
		}
649
650
		wp_enqueue_script(
651
			'jetpack-blocks-editor',
652
			$editor_script,
653
			$editor_deps,
654
			$version,
655
			false
656
		);
657
658
		wp_localize_script(
659
			'jetpack-blocks-editor',
660
			'Jetpack_Block_Assets_Base_Url',
661
			plugins_url( $blocks_dir . '/', JETPACK__PLUGIN_FILE )
662
		);
663
664
		if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
665
			$user      = wp_get_current_user();
666
			$user_data = array(
667
				'userid'   => $user->ID,
668
				'username' => $user->user_login,
669
			);
670
			$blog_id   = get_current_blog_id();
671
		} else {
672
			$user_data = Jetpack_Tracks_Client::get_connected_user_tracks_identity();
673
			$blog_id   = Jetpack_Options::get_option( 'id', 0 );
674
		}
675
676
		wp_localize_script(
677
			'jetpack-blocks-editor',
678
			'Jetpack_Editor_Initial_State',
679
			array(
680
				'available_blocks' => self::get_availability(),
681
				'jetpack'          => array( 'is_active' => Jetpack::is_active() ),
682
				'siteFragment'     => $site_fragment,
683
				'tracksUserData'   => $user_data,
684
				'wpcomBlogId'      => $blog_id,
685
			)
686
		);
687
688
		wp_set_script_translations( 'jetpack-blocks-editor', 'jetpack' );
689
690
		wp_enqueue_style( 'jetpack-blocks-editor', $editor_style, array(), $version );
691
	}
692
693
	/**
694
	 * Some blocks do not depend on a specific module,
695
	 * and can consequently be loaded outside of the usual modules.
696
	 * We will look for such modules in the extensions/ directory.
697
	 *
698
	 * @since 7.1.0
699
	 */
700
	public static function load_independent_blocks() {
701
		if ( self::should_load() ) {
702
			/**
703
			 * Look for files that match our list of available Jetpack Gutenberg extensions (blocks and plugins).
704
			 * If available, load them.
705
			 */
706
			foreach ( self::$extensions as $extension ) {
707
				$extension_file_glob = glob( JETPACK__PLUGIN_DIR . 'extensions/*/' . $extension . '/' . $extension . '.php' );
708
				if ( ! empty( $extension_file_glob ) ) {
709
					include_once $extension_file_glob[0];
710
				}
711
			}
712
		}
713
	}
714
715
	/**
716
	 * Get CSS classes for a block.
717
	 *
718
	 * @since 7.7.0
719
	 *
720
	 * @param string $slug  Block slug.
721
	 * @param array  $attr  Block attributes.
722
	 * @param array  $extra Potential extra classes you may want to provide.
723
	 *
724
	 * @return string $classes List of CSS classes for a block.
725
	 */
726
	public static function block_classes( $slug = '', $attr, $extra = array() ) {
727
		if ( empty( $slug ) ) {
728
			return '';
729
		}
730
731
		// Basic block name class.
732
		$classes = array(
733
			'wp-block-jetpack-' . $slug,
734
		);
735
736
		// Add alignment if provided.
737
		if (
738
			! empty( $attr['align'] )
739
			&& in_array( $attr['align'], array( 'left', 'center', 'right', 'wide', 'full' ), true )
740
		) {
741
			array_push( $classes, 'align' . $attr['align'] );
742
		}
743
744
		// Add custom classes if provided in the block editor.
745
		if ( ! empty( $attr['className'] ) ) {
746
			array_push( $classes, $attr['className'] );
747
		}
748
749
		// Add any extra classes.
750
		if ( is_array( $extra ) && ! empty( $extra ) ) {
751
			$classes = array_merge( $classes, $extra );
752
		}
753
754
		return implode( ' ', $classes );
755
	}
756
757
	/**
758
	 * Determine whether a site should use the default set of blocks, or a custom set.
759
	 * Possible variations are currently beta, experimental, and production.
760
	 *
761
	 * @since 8.1.0
762
	 *
763
	 * @return string $block_varation production|beta|experimental
764
	 */
765
	public static function blocks_variation() {
766
		// Default to production blocks.
767
		$block_varation = 'production';
768
769
		if ( Constants::is_true( 'JETPACK_BETA_BLOCKS' ) ) {
770
			$block_varation = 'beta';
771
		}
772
773
		/*
774
		 * Switch to experimental blocks if you use the JETPACK_EXPERIMENTAL_BLOCKS constant.
775
		 */
776
		if ( Constants::is_true( 'JETPACK_EXPERIMENTAL_BLOCKS' ) ) {
777
			$block_varation = 'experimental';
778
		}
779
780
		/**
781
		 * Allow customizing the variation of blocks in use on a site.
782
		 *
783
		 * @since 8.1.0
784
		 *
785
		 * @param string $block_variation Can be beta, experimental, and production. Defaults to production.
786
		 */
787
		return apply_filters( 'jetpack_blocks_variation', $block_varation );
788
	}
789
790
	/**
791
	 * Get a list of extensions available for the variation you chose.
792
	 *
793
	 * @since 8.1.0
794
	 *
795
	 * @param obj    $preset_extensions_manifest List of extensions available in Jetpack.
796
	 * @param string $blocks_variation           Subset of blocks. production|beta|experimental.
797
	 *
798
	 * @return array $preset_extensions Array of extensions for that variation
799
	 */
800
	public static function get_extensions_preset_for_variation( $preset_extensions_manifest, $blocks_variation ) {
801
		$preset_extensions = isset( $preset_extensions_manifest->{ $blocks_variation } )
802
				? (array) $preset_extensions_manifest->{ $blocks_variation }
803
				: array();
804
805
		/*
806
		 * Experimental and Beta blocks need the production blocks as well.
807
		 */
808 View Code Duplication
		if (
809
			'experimental' === $blocks_variation
810
			|| 'beta' === $blocks_variation
811
		) {
812
			$production_extensions = isset( $preset_extensions_manifest->production )
813
				? (array) $preset_extensions_manifest->production
814
				: array();
815
816
			$preset_extensions = array_unique( array_merge( $preset_extensions, $production_extensions ) );
817
		}
818
819
		/*
820
		 * Beta blocks need the experimental blocks as well.
821
		 *
822
		 * If you've chosen to see Beta blocks,
823
		 * we want to make all blocks available to you:
824
		 * - Production
825
		 * - Experimental
826
		 * - Beta
827
		 */
828 View Code Duplication
		if ( 'beta' === $blocks_variation ) {
829
			$production_extensions = isset( $preset_extensions_manifest->experimental )
830
				? (array) $preset_extensions_manifest->experimental
831
				: array();
832
833
			$preset_extensions = array_unique( array_merge( $preset_extensions, $production_extensions ) );
834
		}
835
836
		return $preset_extensions;
837
	}
838
}
839