Completed
Push — update/avail-check-logic ( d23ca0 )
by
unknown
11:05 queued 03:46
created

Jetpack_Gutenberg::load_independent_blocks()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
nc 4
nop 0
dl 0
loc 14
rs 9.7998
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
const WPCOM_PLANS = array( 'personal-plan', 'premium-plan', 'business-plan', 'ecommerce-plan' );
14
/**
15
 * Wrapper function to safely register a gutenberg block type
16
 *
17
 * @param string $slug Slug of the block.
18
 * @param array  $args Arguments that are passed into register_block_type.
19
 *
20
 * @see register_block_type
21
 *
22
 * @since 6.7.0
23
 *
24
 * @return WP_Block_Type|false The registered block type on success, or false on failure.
25
 */
26
function jetpack_register_block( $slug, $args = array() ) {
27
	if ( 0 !== strpos( $slug, 'jetpack/' ) && ! strpos( $slug, '/' ) ) {
28
		_doing_it_wrong( 'jetpack_register_block', 'Prefix the block with jetpack/ ', '7.1.0' );
29
		$slug = 'jetpack/' . $slug;
30
	}
31
32
	// Checking whether block is registered to ensure it isn't registered twice.
33
	if ( Jetpack_Gutenberg::is_registered( $slug ) ) {
34
		return false;
35
	}
36
37
	return register_block_type( $slug, $args );
38
}
39
40
/**
41
 * Helper function to register a Jetpack Gutenberg plugin
42
 *
43
 * @deprecated 7.1.0 Use Jetpack_Gutenberg::set_extension_available() instead
44
 *
45
 * @param string $slug Slug of the plugin.
46
 *
47
 * @since 6.9.0
48
 *
49
 * @return void
50
 */
51
function jetpack_register_plugin( $slug ) {
52
	_deprecated_function( __FUNCTION__, '7.1', 'Jetpack_Gutenberg::set_extension_available' );
53
54
	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...
55
}
56
57
/**
58
 * Set the reason why an extension (block or plugin) is unavailable
59
 *
60
 * @deprecated 7.1.0 Use Jetpack_Gutenberg::set_extension_unavailable() instead
61
 *
62
 * @param string $slug Slug of the block.
63
 * @param string $reason A string representation of why the extension is unavailable.
64
 *
65
 * @since 7.0.0
66
 *
67
 * @return void
68
 */
69
function jetpack_set_extension_unavailability_reason( $slug, $reason ) {
70
	_deprecated_function( __FUNCTION__, '7.1', 'Jetpack_Gutenberg::set_extension_unavailable' );
71
72
	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...
73
}
74
75
/**
76
 * General Gutenberg editor specific functionality
77
 */
78
class Jetpack_Gutenberg {
79
80
	/**
81
	 * Only these extensions can be registered. Used to control availability of beta blocks.
82
	 *
83
	 * @var array Extensions whitelist
84
	 */
85
	private static $extensions = array();
86
87
	/**
88
	 * Keeps track of the reasons why a given extension is unavailable.
89
	 *
90
	 * @var array Extensions availability information
91
	 */
92
	private static $availability = array();
93
94
	/**
95
	 * Prepend the 'jetpack/' prefix to a block name
96
	 *
97
	 * @param string $block_name The block name.
98
	 *
99
	 * @return string The prefixed block name.
100
	 */
101
	private static function prepend_block_prefix( $block_name ) {
102
		return 'jetpack/' . $block_name;
103
	}
104
105
	/**
106
	 * Remove the 'jetpack/' or jetpack-' prefix from an extension name
107
	 *
108
	 * @param string $extension_name The extension name.
109
	 *
110
	 * @return string The unprefixed extension name.
111
	 */
112
	private static function remove_extension_prefix( $extension_name ) {
113
		if ( wp_startswith( $extension_name, 'jetpack/' ) || wp_startswith( $extension_name, 'jetpack-' ) ) {
114
			return substr( $extension_name, strlen( 'jetpack/' ) );
115
		}
116
		return $extension_name;
117
	}
118
119
	/**
120
	 * Whether two arrays share at least one item
121
	 *
122
	 * @param array $a An array.
123
	 * @param array $b Another array.
124
	 *
125
	 * @return boolean True if $a and $b share at least one item
126
	 */
127
	protected static function share_items( $a, $b ) {
128
		return count( array_intersect( $a, $b ) ) > 0;
129
	}
130
131
	/**
132
	 * Register a block
133
	 *
134
	 * @deprecated 7.1.0 Use jetpack_register_block() instead
135
	 *
136
	 * @param string $slug Slug of the block.
137
	 * @param array  $args Arguments that are passed into register_block_type().
138
	 */
139
	public static function register_block( $slug, $args ) {
140
		_deprecated_function( __METHOD__, '7.1', 'jetpack_register_block' );
141
142
		jetpack_register_block( 'jetpack/' . $slug, $args );
143
	}
144
145
	/**
146
	 * Register a plugin
147
	 *
148
	 * @deprecated 7.1.0 Use Jetpack_Gutenberg::set_extension_available() instead
149
	 *
150
	 * @param string $slug Slug of the plugin.
151
	 */
152
	public static function register_plugin( $slug ) {
153
		_deprecated_function( __METHOD__, '7.1', 'Jetpack_Gutenberg::set_extension_available' );
154
155
		self::set_extension_available( $slug );
156
	}
157
158
	/**
159
	 * Register a block
160
	 *
161
	 * @deprecated 7.0.0 Use jetpack_register_block() instead
162
	 *
163
	 * @param string $slug Slug of the block.
164
	 * @param array  $args Arguments that are passed into the register_block_type.
165
	 * @param array  $availability array containing if a block is available and the reason when it is not.
166
	 */
167
	public static function register( $slug, $args, $availability ) {
168
		_deprecated_function( __METHOD__, '7.0', 'jetpack_register_block' );
169
170
		if ( isset( $availability['available'] ) && ! $availability['available'] ) {
171
			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...
172
		} else {
173
			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...
174
		}
175
	}
176
177
	/**
178
	 * Set a (non-block) extension as available
179
	 *
180
	 * @param string $slug Slug of the extension.
181
	 */
182
	public static function set_extension_available( $slug ) {
183
		self::$availability[ self::remove_extension_prefix( $slug ) ] = true;
184
	}
185
186
	/**
187
	 * Set the reason why an extension (block or plugin) is unavailable
188
	 *
189
	 * @param string $slug Slug of the extension.
190
	 * @param string $reason A string representation of why the extension is unavailable.
191
	 * @param array  $details A free-form array containing more information on why the extension is unavailable.
192
	 */
193
	public static function set_extension_unavailable( $slug, $reason, $details = array() ) {
194
		if (
195
			// Extensions that require a plan may be eligible for upgrades.
196
			'missing_plan' === $reason
197
			&& (
198
				/**
199
				 * Filter 'jetpack_block_editor_enable_upgrade_nudge' with `true` to enable or `false`
200
				 * to disable paid feature upgrade nudges in the block editor.
201
				 *
202
				 * When this is changed to default to `true`, you should also update `modules/memberships/class-jetpack-memberships.php`
203
				 * See https://github.com/Automattic/jetpack/pull/13394#pullrequestreview-293063378
204
				 *
205
				 * @since 7.7.0
206
				 *
207
				 * @param boolean
208
				 */
209
				! apply_filters( 'jetpack_block_editor_enable_upgrade_nudge', false )
210
				/** This filter is documented in _inc/lib/admin-pages/class.jetpack-react-page.php */
211
				|| ! apply_filters( 'jetpack_show_promotions', true )
212
			)
213
		) {
214
			// The block editor may apply an upgrade nudge if `missing_plan` is the reason.
215
			// Add a descriptive suffix to disable behavior but provide informative reason.
216
			$reason .= '__nudge_disabled';
217
		}
218
219
		self::$availability[ self::remove_extension_prefix( $slug ) ] = array(
220
			'reason'  => $reason,
221
			'details' => $details,
222
		);
223
	}
224
225
	/**
226
	 * Set the reason why an extension (block or plugin) is unavailable
227
	 *
228
	 * @deprecated 7.1.0 Use set_extension_unavailable() instead
229
	 *
230
	 * @param string $slug Slug of the extension.
231
	 * @param string $reason A string representation of why the extension is unavailable.
232
	 */
233
	public static function set_extension_unavailability_reason( $slug, $reason ) {
234
		_deprecated_function( __METHOD__, '7.1', 'Jetpack_Gutenberg::set_extension_unavailable' );
235
236
		self::set_extension_unavailable( $slug, $reason );
237
	}
238
239
	/**
240
	 * Set up a whitelist of allowed block editor extensions
241
	 *
242
	 * @return void
243
	 */
244
	public static function init() {
245
		if ( ! self::should_load() ) {
246
			return;
247
		}
248
249
		/**
250
		 * Alternative to `JETPACK_BETA_BLOCKS`, set to `true` to load Beta Blocks.
251
		 *
252
		 * @since 6.9.0
253
		 *
254
		 * @param boolean
255
		 */
256
		if ( apply_filters( 'jetpack_load_beta_blocks', false ) ) {
257
			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...
258
		}
259
260
		/**
261
		 * Filter the whitelist of block editor extensions that are available through Jetpack.
262
		 *
263
		 * @since 7.0.0
264
		 *
265
		 * @param array
266
		 */
267
		self::$extensions = apply_filters( 'jetpack_set_available_extensions', self::get_available_extensions() );
268
269
		/**
270
		 * Filter the whitelist of block editor plugins that are available through Jetpack.
271
		 *
272
		 * @deprecated 7.0.0 Use jetpack_set_available_extensions instead
273
		 *
274
		 * @since 6.8.0
275
		 *
276
		 * @param array
277
		 */
278
		self::$extensions = apply_filters( 'jetpack_set_available_blocks', self::$extensions );
279
280
		/**
281
		 * Filter the whitelist of block editor plugins that are available through Jetpack.
282
		 *
283
		 * @deprecated 7.0.0 Use jetpack_set_available_extensions instead
284
		 *
285
		 * @since 6.9.0
286
		 *
287
		 * @param array
288
		 */
289
		self::$extensions = apply_filters( 'jetpack_set_available_plugins', self::$extensions );
290
	}
291
292
	/**
293
	 * Resets the class to its original state
294
	 *
295
	 * Used in unit tests
296
	 *
297
	 * @return void
298
	 */
299
	public static function reset() {
300
		self::$extensions   = array();
301
		self::$availability = array();
302
	}
303
304
	/**
305
	 * Return the Gutenberg extensions (blocks and plugins) directory
306
	 *
307
	 * @return string The Gutenberg extensions directory
308
	 */
309
	public static function get_blocks_directory() {
310
		/**
311
		 * Filter to select Gutenberg blocks directory
312
		 *
313
		 * @since 6.9.0
314
		 *
315
		 * @param string default: '_inc/blocks/'
316
		 */
317
		return apply_filters( 'jetpack_blocks_directory', '_inc/blocks/' );
318
	}
319
320
	/**
321
	 * Checks for a given .json file in the blocks folder.
322
	 *
323
	 * @param string $preset The name of the .json file to look for.
324
	 *
325
	 * @return bool True if the file is found.
326
	 */
327
	public static function preset_exists( $preset ) {
328
		return file_exists( JETPACK__PLUGIN_DIR . self::get_blocks_directory() . $preset . '.json' );
329
	}
330
331
	/**
332
	 * Decodes JSON loaded from a preset file in the blocks folder
333
	 *
334
	 * @param string $preset The name of the .json file to load.
335
	 *
336
	 * @return mixed Returns an object if the file is present, or false if a valid .json file is not present.
337
	 */
338
	public static function get_preset( $preset ) {
339
		return json_decode(
340
			// phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
341
			file_get_contents( JETPACK__PLUGIN_DIR . self::get_blocks_directory() . $preset . '.json' )
342
		);
343
	}
344
345
	/**
346
	 * Returns a whitelist of Jetpack Gutenberg extensions (blocks and plugins), based on index.json
347
	 *
348
	 * @return array A list of blocks: eg [ 'publicize', 'markdown' ]
349
	 */
350
	public static function get_jetpack_gutenberg_extensions_whitelist() {
351
		$preset_extensions_manifest = self::preset_exists( 'index' )
352
			? self::get_preset( 'index' )
353
			: (object) array();
354
		$blocks_variation           = self::blocks_variation();
355
356
		return self::get_extensions_preset_for_variation( $preset_extensions_manifest, $blocks_variation );
357
	}
358
359
	/**
360
	 * Returns a diff from a combined list of whitelisted extensions and extensions determined to be excluded
361
	 *
362
	 * @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...
363
	 *
364
	 * @return array A list of blocks: eg array( 'publicize', 'markdown' )
365
	 */
366
	public static function get_available_extensions( $whitelisted_extensions = null ) {
367
		$exclusions             = get_option( 'jetpack_excluded_extensions', array() );
368
		$whitelisted_extensions = is_null( $whitelisted_extensions ) ? self::get_jetpack_gutenberg_extensions_whitelist() : $whitelisted_extensions;
369
370
		return array_diff( $whitelisted_extensions, $exclusions );
371
	}
372
373
	/**
374
	 * Get availability of each block / plugin.
375
	 *
376
	 * @return array A list of block and plugins and their availablity status
377
	 */
378
	public static function get_availability() {
379
		/**
380
		 * Fires before Gutenberg extensions availability is computed.
381
		 *
382
		 * In the function call you supply, use `jetpack_register_block()` to set a block as available.
383
		 * Alternatively, use `Jetpack_Gutenberg::set_extension_available()` (for a non-block plugin), and
384
		 * `Jetpack_Gutenberg::set_extension_unavailable()` (if the block or plugin should not be registered
385
		 * but marked as unavailable).
386
		 *
387
		 * @since 7.0.0
388
		 */
389
		do_action( 'jetpack_register_gutenberg_extensions' );
390
391
		$available_extensions = array();
392
393
		foreach ( self::$extensions as $extension ) {
394
			$is_available = self::is_registered( 'jetpack/' . $extension ) ||
395
			( isset( self::$availability[ $extension ] ) && true === self::$availability[ $extension ] );
396
397
			$available_extensions[ $extension ] = array(
398
				'available' => $is_available,
399
			);
400
401
			if ( ! $is_available ) {
402
				$reason  = isset( self::$availability[ $extension ] ) ? self::$availability[ $extension ]['reason'] : 'missing_module';
403
				$details = isset( self::$availability[ $extension ] ) ? self::$availability[ $extension ]['details'] : array();
404
				$available_extensions[ $extension ]['unavailable_reason'] = $reason;
405
				$available_extensions[ $extension ]['details']            = $details;
406
			}
407
		}
408
409
		return $available_extensions;
410
	}
411
412
	/**
413
	 * Check if an extension/block is already registered
414
	 *
415
	 * @since 7.2
416
	 *
417
	 * @param string $slug Name of extension/block to check.
418
	 *
419
	 * @return bool
420
	 */
421
	public static function is_registered( $slug ) {
422
		return WP_Block_Type_Registry::get_instance()->is_registered( $slug );
423
	}
424
425
	/**
426
	 * Check if Gutenberg editor is available
427
	 *
428
	 * @since 6.7.0
429
	 *
430
	 * @return bool
431
	 */
432
	public static function is_gutenberg_available() {
433
		return true;
434
	}
435
436
	/**
437
	 * Check whether conditions indicate Gutenberg Extensions (blocks and plugins) should be loaded
438
	 *
439
	 * Loading blocks and plugins is enabled by default and may be disabled via filter:
440
	 *   add_filter( 'jetpack_gutenberg', '__return_false' );
441
	 *
442
	 * @since 6.9.0
443
	 *
444
	 * @return bool
445
	 */
446
	public static function should_load() {
447
		if ( ! Jetpack::is_active() && ! ( new Status() )->is_development_mode() ) {
448
			return false;
449
		}
450
451
		/**
452
		 * Filter to disable Gutenberg blocks
453
		 *
454
		 * @since 6.5.0
455
		 *
456
		 * @param bool true Whether to load Gutenberg blocks
457
		 */
458
		return (bool) apply_filters( 'jetpack_gutenberg', true );
459
	}
460
461
	/**
462
	 * Only enqueue block assets when needed.
463
	 *
464
	 * @param string $type Slug of the block.
465
	 * @param array  $script_dependencies Script dependencies. Will be merged with automatically
466
	 *                                    detected script dependencies from the webpack build.
467
	 *
468
	 * @return void
469
	 */
470
	public static function load_assets_as_required( $type, $script_dependencies = array() ) {
471
		if ( is_admin() ) {
472
			// A block's view assets will not be required in wp-admin.
473
			return;
474
		}
475
476
		$type = sanitize_title_with_dashes( $type );
477
		self::load_styles_as_required( $type );
478
		self::load_scripts_as_required( $type, $script_dependencies );
479
	}
480
481
	/**
482
	 * Only enqueue block sytles when needed.
483
	 *
484
	 * @param string $type Slug of the block.
485
	 *
486
	 * @since 7.2.0
487
	 *
488
	 * @return void
489
	 */
490
	public static function load_styles_as_required( $type ) {
491
		if ( is_admin() ) {
492
			// A block's view assets will not be required in wp-admin.
493
			return;
494
		}
495
496
		// Enqueue styles.
497
		$style_relative_path = self::get_blocks_directory() . $type . '/view' . ( is_rtl() ? '.rtl' : '' ) . '.css';
498 View Code Duplication
		if ( self::block_has_asset( $style_relative_path ) ) {
499
			$style_version = self::get_asset_version( $style_relative_path );
500
			$view_style    = plugins_url( $style_relative_path, JETPACK__PLUGIN_FILE );
501
			wp_enqueue_style( 'jetpack-block-' . $type, $view_style, array(), $style_version );
502
		}
503
504
	}
505
506
	/**
507
	 * Only enqueue block scripts when needed.
508
	 *
509
	 * @param string $type Slug of the block.
510
	 * @param array  $dependencies Script dependencies. Will be merged with automatically
511
	 *                             detected script dependencies from the webpack build.
512
	 *
513
	 * @since 7.2.0
514
	 *
515
	 * @return void
516
	 */
517
	public static function load_scripts_as_required( $type, $dependencies = array() ) {
518
		if ( is_admin() ) {
519
			// A block's view assets will not be required in wp-admin.
520
			return;
521
		}
522
523
		// Enqueue script.
524
		$script_relative_path = self::get_blocks_directory() . $type . '/view.js';
525
		$script_deps_path     = JETPACK__PLUGIN_DIR . self::get_blocks_directory() . $type . '/view.asset.php';
526
		$script_dependencies  = array( 'wp-polyfill' );
527
		if ( file_exists( $script_deps_path ) ) {
528
			$asset_manifest      = include $script_deps_path;
529
			$script_dependencies = $asset_manifest['dependencies'];
530
		}
531
532
		if ( ( ! class_exists( 'Jetpack_AMP_Support' ) || ! Jetpack_AMP_Support::is_amp_request() ) && self::block_has_asset( $script_relative_path ) ) {
533
			$script_version = self::get_asset_version( $script_relative_path );
534
			$view_script    = plugins_url( $script_relative_path, JETPACK__PLUGIN_FILE );
535
			wp_enqueue_script( 'jetpack-block-' . $type, $view_script, $script_dependencies, $script_version, false );
536
		}
537
538
		wp_localize_script(
539
			'jetpack-block-' . $type,
540
			'Jetpack_Block_Assets_Base_Url',
541
			plugins_url( self::get_blocks_directory(), JETPACK__PLUGIN_FILE )
542
		);
543
	}
544
545
	/**
546
	 * Check if an asset exists for a block.
547
	 *
548
	 * @param string $file Path of the file we are looking for.
549
	 *
550
	 * @return bool $block_has_asset Does the file exist.
551
	 */
552
	public static function block_has_asset( $file ) {
553
		return file_exists( JETPACK__PLUGIN_DIR . $file );
554
	}
555
556
	/**
557
	 * Get the version number to use when loading the file. Allows us to bypass cache when developing.
558
	 *
559
	 * @param string $file Path of the file we are looking for.
560
	 *
561
	 * @return string $script_version Version number.
562
	 */
563
	public static function get_asset_version( $file ) {
564
		return Jetpack::is_development_version() && self::block_has_asset( $file )
565
			? filemtime( JETPACK__PLUGIN_DIR . $file )
566
			: JETPACK__VERSION;
567
	}
568
569
	/**
570
	 * Load Gutenberg editor assets
571
	 *
572
	 * @since 6.7.0
573
	 *
574
	 * @return void
575
	 */
576
	public static function enqueue_block_editor_assets() {
577
		if ( ! self::should_load() ) {
578
			return;
579
		}
580
581
		// Required for Analytics. See _inc/lib/admin-pages/class.jetpack-admin-page.php.
582
		if ( ! ( new Status() )->is_development_mode() && Jetpack::is_active() ) {
583
			wp_enqueue_script( 'jp-tracks', '//stats.wp.com/w.js', array(), gmdate( 'YW' ), true );
584
		}
585
586
		$rtl              = is_rtl() ? '.rtl' : '';
587
		$blocks_dir       = self::get_blocks_directory();
588
		$blocks_variation = self::blocks_variation();
589
590
		if ( 'production' !== $blocks_variation ) {
591
			$blocks_env = '-' . esc_attr( $blocks_variation );
592
		} else {
593
			$blocks_env = '';
594
		}
595
596
		$editor_script = plugins_url( "{$blocks_dir}editor{$blocks_env}.js", JETPACK__PLUGIN_FILE );
597
		$editor_style  = plugins_url( "{$blocks_dir}editor{$blocks_env}{$rtl}.css", JETPACK__PLUGIN_FILE );
598
599
		$editor_deps_path = JETPACK__PLUGIN_DIR . $blocks_dir . "editor{$blocks_env}.asset.php";
600
		$editor_deps      = array( 'wp-polyfill' );
601
		if ( file_exists( $editor_deps_path ) ) {
602
			$asset_manifest = include $editor_deps_path;
603
			$editor_deps    = $asset_manifest['dependencies'];
604
		}
605
606
		$version = Jetpack::is_development_version() && file_exists( JETPACK__PLUGIN_DIR . $blocks_dir . 'editor.js' )
607
			? filemtime( JETPACK__PLUGIN_DIR . $blocks_dir . 'editor.js' )
608
			: JETPACK__VERSION;
609
610
		if ( method_exists( 'Jetpack', 'build_raw_urls' ) ) {
611
			$site_fragment = Jetpack::build_raw_urls( home_url() );
612
		} elseif ( class_exists( 'WPCOM_Masterbar' ) && method_exists( 'WPCOM_Masterbar', 'get_calypso_site_slug' ) ) {
613
			$site_fragment = WPCOM_Masterbar::get_calypso_site_slug( get_current_blog_id() );
614
		} else {
615
			$site_fragment = '';
616
		}
617
618
		wp_enqueue_script(
619
			'jetpack-blocks-editor',
620
			$editor_script,
621
			$editor_deps,
622
			$version,
623
			false
624
		);
625
626
		wp_localize_script(
627
			'jetpack-blocks-editor',
628
			'Jetpack_Block_Assets_Base_Url',
629
			plugins_url( $blocks_dir . '/', JETPACK__PLUGIN_FILE )
630
		);
631
632
		if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
633
			$user      = wp_get_current_user();
634
			$user_data = array(
635
				'userid'   => $user->ID,
636
				'username' => $user->user_login,
637
			);
638
			$blog_id   = get_current_blog_id();
639
		} else {
640
			$user_data = Jetpack_Tracks_Client::get_connected_user_tracks_identity();
641
			$blog_id   = Jetpack_Options::get_option( 'id', 0 );
642
		}
643
644
		wp_localize_script(
645
			'jetpack-blocks-editor',
646
			'Jetpack_Editor_Initial_State',
647
			array(
648
				'available_blocks' => self::get_availability(),
649
				'jetpack'          => array( 'is_active' => Jetpack::is_active() ),
650
				'siteFragment'     => $site_fragment,
651
				'tracksUserData'   => $user_data,
652
				'wpcomBlogId'      => $blog_id,
653
			)
654
		);
655
656
		wp_set_script_translations( 'jetpack-blocks-editor', 'jetpack' );
657
658
		wp_enqueue_style( 'jetpack-blocks-editor', $editor_style, array(), $version );
659
	}
660
661
	/**
662
	 * Some blocks do not depend on a specific module,
663
	 * and can consequently be loaded outside of the usual modules.
664
	 * We will look for such modules in the extensions/ directory.
665
	 *
666
	 * @since 7.1.0
667
	 */
668
	public static function load_independent_blocks() {
669
		if ( self::should_load() ) {
670
			/**
671
			 * Look for files that match our list of available Jetpack Gutenberg extensions (blocks and plugins).
672
			 * If available, load them.
673
			 */
674
			foreach ( self::$extensions as $extension ) {
675
				$extension_file_glob = glob( JETPACK__PLUGIN_DIR . 'extensions/*/' . $extension . '/' . $extension . '.php' );
676
				if ( ! empty( $extension_file_glob ) ) {
677
					include_once $extension_file_glob[0];
678
				}
679
			}
680
		}
681
	}
682
683
	/**
684
	 * Get CSS classes for a block.
685
	 *
686
	 * @since 7.7.0
687
	 *
688
	 * @param string $slug  Block slug.
689
	 * @param array  $attr  Block attributes.
690
	 * @param array  $extra Potential extra classes you may want to provide.
691
	 *
692
	 * @return string $classes List of CSS classes for a block.
693
	 */
694
	public static function block_classes( $slug = '', $attr, $extra = array() ) {
695
		if ( empty( $slug ) ) {
696
			return '';
697
		}
698
699
		// Basic block name class.
700
		$classes = array(
701
			'wp-block-jetpack-' . $slug,
702
		);
703
704
		// Add alignment if provided.
705
		if (
706
			! empty( $attr['align'] )
707
			&& in_array( $attr['align'], array( 'left', 'center', 'right', 'wide', 'full' ), true )
708
		) {
709
			array_push( $classes, 'align' . $attr['align'] );
710
		}
711
712
		// Add custom classes if provided in the block editor.
713
		if ( ! empty( $attr['className'] ) ) {
714
			array_push( $classes, $attr['className'] );
715
		}
716
717
		// Add any extra classes.
718
		if ( is_array( $extra ) && ! empty( $extra ) ) {
719
			$classes = array_merge( $classes, $extra );
720
		}
721
722
		return implode( ' ', $classes );
723
	}
724
725
	/**
726
	 * Determine whether a site should use the default set of blocks, or a custom set.
727
	 * Possible variations are currently beta, experimental, and production.
728
	 *
729
	 * @since 8.1.0
730
	 *
731
	 * @return string $block_varation production|beta|experimental
732
	 */
733
	public static function blocks_variation() {
734
		// Default to production blocks.
735
		$block_varation = 'production';
736
737
		if ( Constants::is_true( 'JETPACK_BETA_BLOCKS' ) ) {
738
			$block_varation = 'beta';
739
		}
740
741
		/*
742
		 * Switch to experimental blocks if you use the JETPACK_EXPERIMENTAL_BLOCKS constant.
743
		 */
744
		if ( Constants::is_true( 'JETPACK_EXPERIMENTAL_BLOCKS' ) ) {
745
			$block_varation = 'experimental';
746
		}
747
748
		/**
749
		 * Allow customizing the variation of blocks in use on a site.
750
		 *
751
		 * @since 8.1.0
752
		 *
753
		 * @param string $block_variation Can be beta, experimental, and production. Defaults to production.
754
		 */
755
		return apply_filters( 'jetpack_blocks_variation', $block_varation );
756
	}
757
758
	/**
759
	 * Get a list of extensions available for the variation you chose.
760
	 *
761
	 * @since 8.1.0
762
	 *
763
	 * @param obj    $preset_extensions_manifest List of extensions available in Jetpack.
764
	 * @param string $blocks_variation           Subset of blocks. production|beta|experimental.
765
	 *
766
	 * @return array $preset_extensions Array of extensions for that variation
767
	 */
768
	public static function get_extensions_preset_for_variation( $preset_extensions_manifest, $blocks_variation ) {
769
		$preset_extensions = isset( $preset_extensions_manifest->{ $blocks_variation } )
770
				? (array) $preset_extensions_manifest->{ $blocks_variation }
771
				: array();
772
773
		/*
774
		 * Experimental and Beta blocks need the production blocks as well.
775
		 */
776 View Code Duplication
		if (
777
			'experimental' === $blocks_variation
778
			|| 'beta' === $blocks_variation
779
		) {
780
			$production_extensions = isset( $preset_extensions_manifest->production )
781
				? (array) $preset_extensions_manifest->production
782
				: array();
783
784
			$preset_extensions = array_unique( array_merge( $preset_extensions, $production_extensions ) );
785
		}
786
787
		/*
788
		 * Beta blocks need the experimental blocks as well.
789
		 *
790
		 * If you've chosen to see Beta blocks,
791
		 * we want to make all blocks available to you:
792
		 * - Production
793
		 * - Experimental
794
		 * - Beta
795
		 */
796 View Code Duplication
		if ( 'beta' === $blocks_variation ) {
797
			$production_extensions = isset( $preset_extensions_manifest->experimental )
798
				? (array) $preset_extensions_manifest->experimental
799
				: array();
800
801
			$preset_extensions = array_unique( array_merge( $preset_extensions, $production_extensions ) );
802
		}
803
804
		return $preset_extensions;
805
	}
806
807
	private static function plans_greater_than_or_equal_to( $plan ) {
808
		$plan_position = array_search( $plan, WPCOM_PLANS );
809
		return array_slice( WPCOM_PLANS, $plan_position, WPCOM_PLANS . length );
810
	}
811
812
	private static function is_block_available( $block_name, $availability ) {
813
		if (
814
			defined( 'IS_WPCOM' )
815
			&& IS_WPCOM
816
			&& function_exists( 'has_any_blog_stickers' )
817
		) {
818
			if ( is_bool( $availability['wpcom'] ) ) {
819
				return $availability['wpcom'];
820
			}
821
822
			$supported_plans = self::plans_greater_than_or_equal_to( $availability['wpcom'] );
823
			if ( has_any_blog_stickers(
824
				$supported_plans,
825
				get_current_blog_id()
826
			) ) {
827
				return true;
828
			}
829
			return $availability['wpcom'];
830
		}
831
832
		if ( is_bool( $availability['jetpack'] ) ) {
833
			return $availability['jetpack'];
834
		}
835
836
		return Jetpack_Plan::supports( BLOCK_NAME );
837
	}
838
839
	public static function register_block_with_availability( $block_name, $callback, $availability ) {
840
		$availability_check_result = self::is_block_available( $block_name, $availability );
841
		if ( is_bool( $availability_check_result ) && $availability_check_result ) {
842
			jetpack_register_block(
843
				$block_name,
844
				array( 'render_callback' => $callback )
845
			);
846
		} else {
847
			self::set_extension_unavailable(
848
				$block_name,
849
				'missing_plan',
850
				array(
851
					'required_feature' => $block_name,
852
					'required_plan'    => $availability_check_result,
853
				)
854
			);
855
		}
856
	}
857
}
858