PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 8.3.1
Jetpack – WP Security, Backup, Speed, & Growth v8.3.1
16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 14.2.2 14.3.1 All 501 releases
jetpack / class.jetpack-gutenberg.php

class.jetpack-gutenberg.php in Jetpack – WP Security, Backup, Speed, & Growth 8.3.1, at class.jetpack-gutenberg.php

930 lines 27.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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 );
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 );
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'] );
228 } else {
229 self::register_block( $slug, $args );
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 );
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.
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 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 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 /**
864 * Validate a URL used in a SSR block.
865 *
866 * @since 8.3.0
867 *
868 * @param string $url URL saved as an attribute in block.
869 * @param array $allowed Array of allowed hosts for that block, or regexes to check against.
870 * @param bool $is_regex Array of regexes matching the URL that could be used in block.
871 *
872 * @return bool|string
873 */
874 public static function validate_block_embed_url( $url, $allowed = array(), $is_regex = false ) {
875 if (
876 empty( $url )
877 || ! is_array( $allowed )
878 || empty( $allowed )
879 ) {
880 return false;
881 }
882
883 $url_components = wp_parse_url( $url );
884
885 // Bail early if we cannot find a host.
886 if ( empty( $url_components['host'] ) ) {
887 return false;
888 }
889
890 // Normalize URL.
891 $url = sprintf(
892 '%s://%s%s%s',
893 $url_components['scheme'],
894 $url_components['host'],
895 $url_components['path'] ? $url_components['path'] : '/',
896 $url_components['query'] ? '?' . $url_components['query'] : ''
897 );
898
899 if ( ! empty( $url_components['fragment'] ) ) {
900 $url = $url . '#' . rawurlencode( $url_components['fragment'] );
901 }
902
903 /*
904 * If we're using a whitelist of hosts,
905 * check if the URL belongs to one of the domains allowed for that block.
906 */
907 if (
908 false === $is_regex
909 && in_array( $url_components['host'], $allowed, true )
910 ) {
911 return $url;
912 }
913
914 /*
915 * If we are using an array of regexes to check against,
916 * loop through that.
917 */
918 if ( true === $is_regex ) {
919 foreach ( $allowed as $regex ) {
920 if ( 1 === preg_match( $regex, $url ) ) {
921 return $url;
922 }
923 }
924 }
925
926 return false;
927 }
928
929 }
930