Completed
Push — add/block-plan-version-gating ( a6bd2c...d4f04c )
by
unknown
07:09
created

Jetpack_Gutenberg::is_editor_version_available()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

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