Completed
Push — instant-search-master ( a786a9...893b7c )
by
unknown
85:32 queued 79:17
created

Jetpack_Gutenberg::validate_block_embed_url()   C

Complexity

Conditions 14
Paths 10

Size

Total Lines 54

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 14
nc 10
nop 3
dl 0
loc 54
rs 6.2666
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
		 * Alternative to `JETPACK_EXPERIMENTAL_BLOCKS`, set to `true` to load Experimental Blocks.
318
		 *
319
		 * @since 8.4.0
320
		 *
321
		 * @param boolean
322
		 */
323
		if ( apply_filters( 'jetpack_load_experimental_blocks', false ) ) {
324
			Constants::set_constant( 'JETPACK_EXPERIMENTAL_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...
325
		}
326
327
		/**
328
		 * Filter the whitelist of block editor extensions that are available through Jetpack.
329
		 *
330
		 * @since 7.0.0
331
		 *
332
		 * @param array
333
		 */
334
		self::$extensions = apply_filters( 'jetpack_set_available_extensions', self::get_available_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.8.0
342
		 *
343
		 * @param array
344
		 */
345
		self::$extensions = apply_filters( 'jetpack_set_available_blocks', self::$extensions );
346
347
		/**
348
		 * Filter the whitelist of block editor plugins that are available through Jetpack.
349
		 *
350
		 * @deprecated 7.0.0 Use jetpack_set_available_extensions instead
351
		 *
352
		 * @since 6.9.0
353
		 *
354
		 * @param array
355
		 */
356
		self::$extensions = apply_filters( 'jetpack_set_available_plugins', self::$extensions );
357
	}
358
359
	/**
360
	 * Resets the class to its original state
361
	 *
362
	 * Used in unit tests
363
	 *
364
	 * @return void
365
	 */
366
	public static function reset() {
367
		self::$extensions   = array();
368
		self::$availability = array();
369
	}
370
371
	/**
372
	 * Return the Gutenberg extensions (blocks and plugins) directory
373
	 *
374
	 * @return string The Gutenberg extensions directory
375
	 */
376
	public static function get_blocks_directory() {
377
		/**
378
		 * Filter to select Gutenberg blocks directory
379
		 *
380
		 * @since 6.9.0
381
		 *
382
		 * @param string default: '_inc/blocks/'
383
		 */
384
		return apply_filters( 'jetpack_blocks_directory', '_inc/blocks/' );
385
	}
386
387
	/**
388
	 * Checks for a given .json file in the blocks folder.
389
	 *
390
	 * @param string $preset The name of the .json file to look for.
391
	 *
392
	 * @return bool True if the file is found.
393
	 */
394
	public static function preset_exists( $preset ) {
395
		return file_exists( JETPACK__PLUGIN_DIR . self::get_blocks_directory() . $preset . '.json' );
396
	}
397
398
	/**
399
	 * Decodes JSON loaded from a preset file in the blocks folder
400
	 *
401
	 * @param string $preset The name of the .json file to load.
402
	 *
403
	 * @return mixed Returns an object if the file is present, or false if a valid .json file is not present.
404
	 */
405
	public static function get_preset( $preset ) {
406
		return json_decode(
407
			// phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
408
			file_get_contents( JETPACK__PLUGIN_DIR . self::get_blocks_directory() . $preset . '.json' )
409
		);
410
	}
411
412
	/**
413
	 * Returns a whitelist of Jetpack Gutenberg extensions (blocks and plugins), based on index.json
414
	 *
415
	 * @return array A list of blocks: eg [ 'publicize', 'markdown' ]
416
	 */
417
	public static function get_jetpack_gutenberg_extensions_whitelist() {
418
		$preset_extensions_manifest = self::preset_exists( 'index' )
419
			? self::get_preset( 'index' )
420
			: (object) array();
421
		$blocks_variation           = self::blocks_variation();
422
423
		return self::get_extensions_preset_for_variation( $preset_extensions_manifest, $blocks_variation );
424
	}
425
426
	/**
427
	 * Returns a diff from a combined list of whitelisted extensions and extensions determined to be excluded
428
	 *
429
	 * @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...
430
	 *
431
	 * @return array A list of blocks: eg array( 'publicize', 'markdown' )
432
	 */
433
	public static function get_available_extensions( $whitelisted_extensions = null ) {
434
		$exclusions             = get_option( 'jetpack_excluded_extensions', array() );
435
		$whitelisted_extensions = is_null( $whitelisted_extensions ) ? self::get_jetpack_gutenberg_extensions_whitelist() : $whitelisted_extensions;
436
437
		return array_diff( $whitelisted_extensions, $exclusions );
438
	}
439
440
	/**
441
	 * Get availability of each block / plugin.
442
	 *
443
	 * @return array A list of block and plugins and their availablity status
444
	 */
445
	public static function get_availability() {
446
		/**
447
		 * Fires before Gutenberg extensions availability is computed.
448
		 *
449
		 * In the function call you supply, use `jetpack_register_block()` to set a block as available.
450
		 * Alternatively, use `Jetpack_Gutenberg::set_extension_available()` (for a non-block plugin), and
451
		 * `Jetpack_Gutenberg::set_extension_unavailable()` (if the block or plugin should not be registered
452
		 * but marked as unavailable).
453
		 *
454
		 * @since 7.0.0
455
		 */
456
		do_action( 'jetpack_register_gutenberg_extensions' );
457
458
		$available_extensions = array();
459
460
		foreach ( self::$extensions as $extension ) {
461
			$is_available = self::is_registered( 'jetpack/' . $extension ) ||
462
			( isset( self::$availability[ $extension ] ) && true === self::$availability[ $extension ] );
463
464
			$available_extensions[ $extension ] = array(
465
				'available' => $is_available,
466
			);
467
468
			if ( ! $is_available ) {
469
				$reason  = isset( self::$availability[ $extension ] ) ? self::$availability[ $extension ]['reason'] : 'missing_module';
470
				$details = isset( self::$availability[ $extension ] ) ? self::$availability[ $extension ]['details'] : array();
471
				$available_extensions[ $extension ]['unavailable_reason'] = $reason;
472
				$available_extensions[ $extension ]['details']            = $details;
473
			}
474
		}
475
476
		return $available_extensions;
477
	}
478
479
	/**
480
	 * Check if an extension/block is already registered
481
	 *
482
	 * @since 7.2
483
	 *
484
	 * @param string $slug Name of extension/block to check.
485
	 *
486
	 * @return bool
487
	 */
488
	public static function is_registered( $slug ) {
489
		return WP_Block_Type_Registry::get_instance()->is_registered( $slug );
490
	}
491
492
	/**
493
	 * Check if Gutenberg editor is available
494
	 *
495
	 * @since 6.7.0
496
	 *
497
	 * @return bool
498
	 */
499
	public static function is_gutenberg_available() {
500
		return true;
501
	}
502
503
	/**
504
	 * Check whether conditions indicate Gutenberg Extensions (blocks and plugins) should be loaded
505
	 *
506
	 * Loading blocks and plugins is enabled by default and may be disabled via filter:
507
	 *   add_filter( 'jetpack_gutenberg', '__return_false' );
508
	 *
509
	 * @since 6.9.0
510
	 *
511
	 * @return bool
512
	 */
513
	public static function should_load() {
514
		if ( ! Jetpack::is_active() && ! ( new Status() )->is_development_mode() ) {
515
			return false;
516
		}
517
518
		/**
519
		 * Filter to disable Gutenberg blocks
520
		 *
521
		 * @since 6.5.0
522
		 *
523
		 * @param bool true Whether to load Gutenberg blocks
524
		 */
525
		return (bool) apply_filters( 'jetpack_gutenberg', true );
526
	}
527
528
	/**
529
	 * Only enqueue block assets when needed.
530
	 *
531
	 * @param string $type Slug of the block.
532
	 * @param array  $script_dependencies Script dependencies. Will be merged with automatically
533
	 *                                    detected script dependencies from the webpack build.
534
	 *
535
	 * @return void
536
	 */
537
	public static function load_assets_as_required( $type, $script_dependencies = array() ) {
538
		if ( is_admin() ) {
539
			// A block's view assets will not be required in wp-admin.
540
			return;
541
		}
542
543
		$type = sanitize_title_with_dashes( $type );
544
		self::load_styles_as_required( $type );
545
		self::load_scripts_as_required( $type, $script_dependencies );
546
	}
547
548
	/**
549
	 * Only enqueue block sytles when needed.
550
	 *
551
	 * @param string $type Slug of the block.
552
	 *
553
	 * @since 7.2.0
554
	 *
555
	 * @return void
556
	 */
557
	public static function load_styles_as_required( $type ) {
558
		if ( is_admin() ) {
559
			// A block's view assets will not be required in wp-admin.
560
			return;
561
		}
562
563
		// Enqueue styles.
564
		$style_relative_path = self::get_blocks_directory() . $type . '/view' . ( is_rtl() ? '.rtl' : '' ) . '.css';
565
		if ( self::block_has_asset( $style_relative_path ) ) {
566
			$style_version = self::get_asset_version( $style_relative_path );
567
			$view_style    = plugins_url( $style_relative_path, JETPACK__PLUGIN_FILE );
568
			wp_enqueue_style( 'jetpack-block-' . $type, $view_style, array(), $style_version );
569
		}
570
571
	}
572
573
	/**
574
	 * Only enqueue block scripts when needed.
575
	 *
576
	 * @param string $type Slug of the block.
577
	 * @param array  $script_dependencies Script dependencies. Will be merged with automatically
578
	 *                             detected script dependencies from the webpack build.
579
	 *
580
	 * @since 7.2.0
581
	 *
582
	 * @return void
583
	 */
584
	public static function load_scripts_as_required( $type, $script_dependencies = array() ) {
585
		if ( is_admin() ) {
586
			// A block's view assets will not be required in wp-admin.
587
			return;
588
		}
589
590
		// Enqueue script.
591
		$script_relative_path  = self::get_blocks_directory() . $type . '/view.js';
592
		$script_deps_path      = JETPACK__PLUGIN_DIR . self::get_blocks_directory() . $type . '/view.asset.php';
593
		$script_dependencies[] = 'wp-polyfill';
594
		if ( file_exists( $script_deps_path ) ) {
595
			$asset_manifest      = include $script_deps_path;
596
			$script_dependencies = array_unique( array_merge( $script_dependencies, $asset_manifest['dependencies'] ) );
597
		}
598
599
		if ( ( ! class_exists( 'Jetpack_AMP_Support' ) || ! Jetpack_AMP_Support::is_amp_request() ) && self::block_has_asset( $script_relative_path ) ) {
600
			$script_version = self::get_asset_version( $script_relative_path );
601
			$view_script    = plugins_url( $script_relative_path, JETPACK__PLUGIN_FILE );
602
			wp_enqueue_script( 'jetpack-block-' . $type, $view_script, $script_dependencies, $script_version, false );
603
		}
604
605
		wp_localize_script(
606
			'jetpack-block-' . $type,
607
			'Jetpack_Block_Assets_Base_Url',
608
			plugins_url( self::get_blocks_directory(), JETPACK__PLUGIN_FILE )
609
		);
610
	}
611
612
	/**
613
	 * Check if an asset exists for a block.
614
	 *
615
	 * @param string $file Path of the file we are looking for.
616
	 *
617
	 * @return bool $block_has_asset Does the file exist.
618
	 */
619
	public static function block_has_asset( $file ) {
620
		return file_exists( JETPACK__PLUGIN_DIR . $file );
621
	}
622
623
	/**
624
	 * Get the version number to use when loading the file. Allows us to bypass cache when developing.
625
	 *
626
	 * @param string $file Path of the file we are looking for.
627
	 *
628
	 * @return string $script_version Version number.
629
	 */
630
	public static function get_asset_version( $file ) {
631
		return Jetpack::is_development_version() && self::block_has_asset( $file )
632
			? filemtime( JETPACK__PLUGIN_DIR . $file )
633
			: JETPACK__VERSION;
634
	}
635
636
	/**
637
	 * Load Gutenberg editor assets
638
	 *
639
	 * @since 6.7.0
640
	 *
641
	 * @return void
642
	 */
643
	public static function enqueue_block_editor_assets() {
644
		if ( ! self::should_load() ) {
645
			return;
646
		}
647
648
		// Required for Analytics. See _inc/lib/admin-pages/class.jetpack-admin-page.php.
649
		if ( ! ( new Status() )->is_development_mode() && Jetpack::is_active() ) {
650
			wp_enqueue_script( 'jp-tracks', '//stats.wp.com/w.js', array(), gmdate( 'YW' ), true );
651
		}
652
653
		$rtl              = is_rtl() ? '.rtl' : '';
654
		$blocks_dir       = self::get_blocks_directory();
655
		$blocks_variation = self::blocks_variation();
656
657
		if ( 'production' !== $blocks_variation ) {
658
			$blocks_env = '-' . esc_attr( $blocks_variation );
659
		} else {
660
			$blocks_env = '';
661
		}
662
663
		$editor_script = plugins_url( "{$blocks_dir}editor{$blocks_env}.js", JETPACK__PLUGIN_FILE );
664
		$editor_style  = plugins_url( "{$blocks_dir}editor{$blocks_env}{$rtl}.css", JETPACK__PLUGIN_FILE );
665
666
		$editor_deps_path = JETPACK__PLUGIN_DIR . $blocks_dir . "editor{$blocks_env}.asset.php";
667
		$editor_deps      = array( 'wp-polyfill' );
668
		if ( file_exists( $editor_deps_path ) ) {
669
			$asset_manifest = include $editor_deps_path;
670
			$editor_deps    = $asset_manifest['dependencies'];
671
		}
672
673
		$version = Jetpack::is_development_version() && file_exists( JETPACK__PLUGIN_DIR . $blocks_dir . 'editor.js' )
674
			? filemtime( JETPACK__PLUGIN_DIR . $blocks_dir . 'editor.js' )
675
			: JETPACK__VERSION;
676
677
		if ( method_exists( 'Jetpack', 'build_raw_urls' ) ) {
678
			$site_fragment = Jetpack::build_raw_urls( home_url() );
679
		} elseif ( class_exists( 'WPCOM_Masterbar' ) && method_exists( 'WPCOM_Masterbar', 'get_calypso_site_slug' ) ) {
680
			$site_fragment = WPCOM_Masterbar::get_calypso_site_slug( get_current_blog_id() );
681
		} else {
682
			$site_fragment = '';
683
		}
684
685
		wp_enqueue_script(
686
			'jetpack-blocks-editor',
687
			$editor_script,
688
			$editor_deps,
689
			$version,
690
			false
691
		);
692
693
		wp_localize_script(
694
			'jetpack-blocks-editor',
695
			'Jetpack_Block_Assets_Base_Url',
696
			plugins_url( $blocks_dir . '/', JETPACK__PLUGIN_FILE )
697
		);
698
699
		if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
700
			$user      = wp_get_current_user();
701
			$user_data = array(
702
				'userid'   => $user->ID,
703
				'username' => $user->user_login,
704
			);
705
			$blog_id   = get_current_blog_id();
706
		} else {
707
			$user_data = Jetpack_Tracks_Client::get_connected_user_tracks_identity();
708
			$blog_id   = Jetpack_Options::get_option( 'id', 0 );
709
		}
710
711
		wp_localize_script(
712
			'jetpack-blocks-editor',
713
			'Jetpack_Editor_Initial_State',
714
			array(
715
				'available_blocks' => self::get_availability(),
716
				'jetpack'          => array( 'is_active' => Jetpack::is_active() ),
717
				'siteFragment'     => $site_fragment,
718
				'tracksUserData'   => $user_data,
719
				'wpcomBlogId'      => $blog_id,
720
			)
721
		);
722
723
		wp_set_script_translations( 'jetpack-blocks-editor', 'jetpack' );
724
725
		wp_enqueue_style( 'jetpack-blocks-editor', $editor_style, array(), $version );
726
	}
727
728
	/**
729
	 * Some blocks do not depend on a specific module,
730
	 * and can consequently be loaded outside of the usual modules.
731
	 * We will look for such modules in the extensions/ directory.
732
	 *
733
	 * @since 7.1.0
734
	 */
735
	public static function load_independent_blocks() {
736
		if ( self::should_load() ) {
737
			/**
738
			 * Look for files that match our list of available Jetpack Gutenberg extensions (blocks and plugins).
739
			 * If available, load them.
740
			 */
741
			foreach ( self::$extensions as $extension ) {
742
				$extension_file_glob = glob( JETPACK__PLUGIN_DIR . 'extensions/*/' . $extension . '/' . $extension . '.php' );
743
				if ( ! empty( $extension_file_glob ) ) {
744
					include_once $extension_file_glob[0];
745
				}
746
			}
747
		}
748
	}
749
750
	/**
751
	 * Get CSS classes for a block.
752
	 *
753
	 * @since 7.7.0
754
	 *
755
	 * @param string $slug  Block slug.
756
	 * @param array  $attr  Block attributes.
757
	 * @param array  $extra Potential extra classes you may want to provide.
758
	 *
759
	 * @return string $classes List of CSS classes for a block.
760
	 */
761
	public static function block_classes( $slug = '', $attr, $extra = array() ) {
762
		if ( empty( $slug ) ) {
763
			return '';
764
		}
765
766
		// Basic block name class.
767
		$classes = array(
768
			'wp-block-jetpack-' . $slug,
769
		);
770
771
		// Add alignment if provided.
772
		if (
773
			! empty( $attr['align'] )
774
			&& in_array( $attr['align'], array( 'left', 'center', 'right', 'wide', 'full' ), true )
775
		) {
776
			array_push( $classes, 'align' . $attr['align'] );
777
		}
778
779
		// Add custom classes if provided in the block editor.
780
		if ( ! empty( $attr['className'] ) ) {
781
			array_push( $classes, $attr['className'] );
782
		}
783
784
		// Add any extra classes.
785
		if ( is_array( $extra ) && ! empty( $extra ) ) {
786
			$classes = array_merge( $classes, $extra );
787
		}
788
789
		return implode( ' ', $classes );
790
	}
791
792
	/**
793
	 * Determine whether a site should use the default set of blocks, or a custom set.
794
	 * Possible variations are currently beta, experimental, and production.
795
	 *
796
	 * @since 8.1.0
797
	 *
798
	 * @return string $block_varation production|beta|experimental
799
	 */
800
	public static function blocks_variation() {
801
		// Default to production blocks.
802
		$block_varation = 'production';
803
804
		if ( Constants::is_true( 'JETPACK_BETA_BLOCKS' ) ) {
805
			$block_varation = 'beta';
806
		}
807
808
		/*
809
		 * Switch to experimental blocks if you use the JETPACK_EXPERIMENTAL_BLOCKS constant.
810
		 */
811
		if ( Constants::is_true( 'JETPACK_EXPERIMENTAL_BLOCKS' ) ) {
812
			$block_varation = 'experimental';
813
		}
814
815
		/**
816
		 * Allow customizing the variation of blocks in use on a site.
817
		 *
818
		 * @since 8.1.0
819
		 *
820
		 * @param string $block_variation Can be beta, experimental, and production. Defaults to production.
821
		 */
822
		return apply_filters( 'jetpack_blocks_variation', $block_varation );
823
	}
824
825
	/**
826
	 * Get a list of extensions available for the variation you chose.
827
	 *
828
	 * @since 8.1.0
829
	 *
830
	 * @param obj    $preset_extensions_manifest List of extensions available in Jetpack.
831
	 * @param string $blocks_variation           Subset of blocks. production|beta|experimental.
832
	 *
833
	 * @return array $preset_extensions Array of extensions for that variation
834
	 */
835
	public static function get_extensions_preset_for_variation( $preset_extensions_manifest, $blocks_variation ) {
836
		$preset_extensions = isset( $preset_extensions_manifest->{ $blocks_variation } )
837
				? (array) $preset_extensions_manifest->{ $blocks_variation }
838
				: array();
839
840
		/*
841
		 * Experimental and Beta blocks need the production blocks as well.
842
		 */
843 View Code Duplication
		if (
844
			'experimental' === $blocks_variation
845
			|| 'beta' === $blocks_variation
846
		) {
847
			$production_extensions = isset( $preset_extensions_manifest->production )
848
				? (array) $preset_extensions_manifest->production
849
				: array();
850
851
			$preset_extensions = array_unique( array_merge( $preset_extensions, $production_extensions ) );
852
		}
853
854
		/*
855
		 * Beta blocks need the experimental blocks as well.
856
		 *
857
		 * If you've chosen to see Beta blocks,
858
		 * we want to make all blocks available to you:
859
		 * - Production
860
		 * - Experimental
861
		 * - Beta
862
		 */
863 View Code Duplication
		if ( 'beta' === $blocks_variation ) {
864
			$production_extensions = isset( $preset_extensions_manifest->experimental )
865
				? (array) $preset_extensions_manifest->experimental
866
				: array();
867
868
			$preset_extensions = array_unique( array_merge( $preset_extensions, $production_extensions ) );
869
		}
870
871
		return $preset_extensions;
872
	}
873
874
	/**
875
	 * Validate a URL used in a SSR block.
876
	 *
877
	 * @since 8.3.0
878
	 *
879
	 * @param string $url      URL saved as an attribute in block.
880
	 * @param array  $allowed  Array of allowed hosts for that block, or regexes to check against.
881
	 * @param bool   $is_regex Array of regexes matching the URL that could be used in block.
882
	 *
883
	 * @return bool|string
884
	 */
885
	public static function validate_block_embed_url( $url, $allowed = array(), $is_regex = false ) {
886
		if (
887
			empty( $url )
888
			|| ! is_array( $allowed )
889
			|| empty( $allowed )
890
		) {
891
			return false;
892
		}
893
894
		$url_components = wp_parse_url( $url );
895
896
		// Bail early if we cannot find a host.
897
		if ( empty( $url_components['host'] ) ) {
898
			return false;
899
		}
900
901
		// Normalize URL.
902
		$url = sprintf(
903
			'%s://%s%s%s',
904
			isset( $url_components['scheme'] ) ? $url_components['scheme'] : 'https',
905
			$url_components['host'],
906
			isset( $url_components['path'] ) ? $url_components['path'] : '/',
907
			isset( $url_components['query'] ) ? '?' . $url_components['query'] : ''
908
		);
909
910
		if ( ! empty( $url_components['fragment'] ) ) {
911
			$url = $url . '#' . rawurlencode( $url_components['fragment'] );
912
		}
913
914
		/*
915
		 * If we're using a whitelist of hosts,
916
		 * check if the URL belongs to one of the domains allowed for that block.
917
		 */
918
		if (
919
			false === $is_regex
920
			&& in_array( $url_components['host'], $allowed, true )
921
		) {
922
			return $url;
923
		}
924
925
		/*
926
		 * If we are using an array of regexes to check against,
927
		 * loop through that.
928
		 */
929
		if ( true === $is_regex ) {
930
			foreach ( $allowed as $regex ) {
931
				if ( 1 === preg_match( $regex, $url ) ) {
932
					return $url;
933
				}
934
			}
935
		}
936
937
		return false;
938
	}
939
940
}
941