Completed
Push — instant-search-master ( 404687...5ddf14 )
by
unknown
10:34 queued 04:11
created

is_gutenberg_version_available()   B

Complexity

Conditions 8
Paths 8

Size

Total Lines 40

Duplication

Lines 0
Ratio 0 %

Importance

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