PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 9.1.3
Jetpack – WP Security, Backup, Speed, & Growth v9.1.3
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 9.1.3, at class.jetpack-gutenberg.php

1,072 lines 32.5 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\Blocks;
10 use Automattic\Jetpack\Constants;
11 use Automattic\Jetpack\Status;
12
13 /**
14 * Wrapper function to safely register a gutenberg block type
15 *
16 * @deprecated 9.1.0 Use Automattic\\Jetpack\\Blocks::jetpack_register_block instead
17 *
18 * @see register_block_type
19 *
20 * @since 6.7.0
21 *
22 * @param string $slug Slug of the block.
23 * @param array $args Arguments that are passed into register_block_type.
24 *
25 * @return WP_Block_Type|false The registered block type on success, or false on failure.
26 */
27 function jetpack_register_block( $slug, $args = array() ) {
28 _deprecated_function( __METHOD__, '9.1.0', 'Automattic\\Jetpack\\Blocks::jetpack_register_block' );
29 return Blocks::jetpack_register_block( $slug, $args );
30 }
31
32 /**
33 * Helper function to register a Jetpack Gutenberg plugin
34 *
35 * @deprecated 7.1.0 Use Jetpack_Gutenberg::set_extension_available() instead
36 *
37 * @param string $slug Slug of the plugin.
38 *
39 * @since 6.9.0
40 *
41 * @return void
42 */
43 function jetpack_register_plugin( $slug ) {
44 _deprecated_function( __FUNCTION__, '7.1', 'Jetpack_Gutenberg::set_extension_available' );
45
46 Jetpack_Gutenberg::register_plugin( $slug );
47 }
48
49 /**
50 * Set the reason why an extension (block or plugin) is unavailable
51 *
52 * @deprecated 7.1.0 Use Jetpack_Gutenberg::set_extension_unavailable() instead
53 *
54 * @param string $slug Slug of the block.
55 * @param string $reason A string representation of why the extension is unavailable.
56 *
57 * @since 7.0.0
58 *
59 * @return void
60 */
61 function jetpack_set_extension_unavailability_reason( $slug, $reason ) {
62 _deprecated_function( __FUNCTION__, '7.1', 'Jetpack_Gutenberg::set_extension_unavailable' );
63
64 Jetpack_Gutenberg::set_extension_unavailability_reason( $slug, $reason );
65 }
66
67 /**
68 * General Gutenberg editor specific functionality
69 */
70 class Jetpack_Gutenberg {
71
72 /**
73 * Only these extensions can be registered. Used to control availability of beta blocks.
74 *
75 * @var array Extensions allowed list.
76 */
77 private static $extensions = array();
78
79 /**
80 * Keeps track of the reasons why a given extension is unavailable.
81 *
82 * @var array Extensions availability information
83 */
84 private static $availability = array();
85
86 /**
87 * Check to see if a minimum version of Gutenberg is available. Because a Gutenberg version is not available in
88 * php if the Gutenberg plugin is not installed, if we know which minimum WP release has the required version we can
89 * optionally fall back to that.
90 *
91 * @param array $version_requirements An array containing the required Gutenberg version and, if known, the WordPress version that was released with this minimum version.
92 * @param string $slug The slug of the block or plugin that has the gutenberg version requirement.
93 *
94 * @since 8.3.0
95 *
96 * @return boolean True if the version of gutenberg required by the block or plugin is available.
97 */
98 public static function is_gutenberg_version_available( $version_requirements, $slug ) {
99 global $wp_version;
100
101 // Bail if we don't at least have the gutenberg version requirement, the WP version is optional.
102 if ( empty( $version_requirements['gutenberg'] ) ) {
103 return false;
104 }
105
106 // If running a local dev build of gutenberg plugin GUTENBERG_DEVELOPMENT_MODE is set so assume correct version.
107 if ( defined( 'GUTENBERG_DEVELOPMENT_MODE' ) && GUTENBERG_DEVELOPMENT_MODE ) {
108 return true;
109 }
110
111 $version_available = false;
112
113 // If running a production build of the gutenberg plugin then GUTENBERG_VERSION is set, otherwise if WP version
114 // with required version of Gutenberg is known check that.
115 if ( defined( 'GUTENBERG_VERSION' ) ) {
116 $version_available = version_compare( GUTENBERG_VERSION, $version_requirements['gutenberg'], '>=' );
117 } elseif ( ! empty( $version_requirements['wp'] ) ) {
118 $version_available = version_compare( $wp_version, $version_requirements['wp'], '>=' );
119 }
120
121 if ( ! $version_available ) {
122 self::set_extension_unavailable(
123 $slug,
124 'incorrect_gutenberg_version',
125 array(
126 'required_feature' => $slug,
127 'required_version' => $version_requirements,
128 'current_version' => array(
129 'wp' => $wp_version,
130 'gutenberg' => defined( 'GUTENBERG_VERSION' ) ? GUTENBERG_VERSION : null,
131 ),
132 )
133 );
134 }
135
136 return $version_available;
137 }
138
139 /**
140 * Prepend the 'jetpack/' prefix to a block name
141 *
142 * @param string $block_name The block name.
143 *
144 * @return string The prefixed block name.
145 */
146 private static function prepend_block_prefix( $block_name ) {
147 return 'jetpack/' . $block_name;
148 }
149
150 /**
151 * Remove the 'jetpack/' or jetpack-' prefix from an extension name
152 *
153 * @param string $extension_name The extension name.
154 *
155 * @return string The unprefixed extension name.
156 */
157 public static function remove_extension_prefix( $extension_name ) {
158 if ( 0 === strpos( $extension_name, 'jetpack/' ) || 0 === strpos( $extension_name, 'jetpack-' ) ) {
159 return substr( $extension_name, strlen( 'jetpack/' ) );
160 }
161 return $extension_name;
162 }
163
164 /**
165 * Whether two arrays share at least one item
166 *
167 * @param array $a An array.
168 * @param array $b Another array.
169 *
170 * @return boolean True if $a and $b share at least one item
171 */
172 protected static function share_items( $a, $b ) {
173 return count( array_intersect( $a, $b ) ) > 0;
174 }
175
176 /**
177 * Register a plugin
178 *
179 * @deprecated 7.1.0 Use Jetpack_Gutenberg::set_extension_available() instead
180 *
181 * @param string $slug Slug of the plugin.
182 */
183 public static function register_plugin( $slug ) {
184 _deprecated_function( __METHOD__, '7.1', 'Jetpack_Gutenberg::set_extension_available' );
185
186 self::set_extension_available( $slug );
187 }
188
189 /**
190 * Set a (non-block) extension as available
191 *
192 * @param string $slug Slug of the extension.
193 */
194 public static function set_extension_available( $slug ) {
195 self::$availability[ self::remove_extension_prefix( $slug ) ] = true;
196 }
197
198 /**
199 * Set the reason why an extension (block or plugin) is unavailable
200 *
201 * @param string $slug Slug of the extension.
202 * @param string $reason A string representation of why the extension is unavailable.
203 * @param array $details A free-form array containing more information on why the extension is unavailable.
204 */
205 public static function set_extension_unavailable( $slug, $reason, $details = array() ) {
206 if (
207 // Extensions that require a plan may be eligible for upgrades.
208 'missing_plan' === $reason
209 && (
210 /**
211 * Filter 'jetpack_block_editor_enable_upgrade_nudge' with `true` to enable or `false`
212 * to disable paid feature upgrade nudges in the block editor.
213 *
214 * When this is changed to default to `true`, you should also update `modules/memberships/class-jetpack-memberships.php`
215 * See https://github.com/Automattic/jetpack/pull/13394#pullrequestreview-293063378
216 *
217 * @since 7.7.0
218 *
219 * @param boolean
220 */
221 ! apply_filters( 'jetpack_block_editor_enable_upgrade_nudge', false )
222 /** This filter is documented in _inc/lib/admin-pages/class.jetpack-react-page.php */
223 || ! apply_filters( 'jetpack_show_promotions', true )
224 )
225 ) {
226 // The block editor may apply an upgrade nudge if `missing_plan` is the reason.
227 // Add a descriptive suffix to disable behavior but provide informative reason.
228 $reason .= '__nudge_disabled';
229 }
230
231 self::$availability[ self::remove_extension_prefix( $slug ) ] = array(
232 'reason' => $reason,
233 'details' => $details,
234 );
235 }
236
237 /**
238 * Set the reason why an extension (block or plugin) is unavailable
239 *
240 * @deprecated 7.1.0 Use set_extension_unavailable() instead
241 *
242 * @param string $slug Slug of the extension.
243 * @param string $reason A string representation of why the extension is unavailable.
244 */
245 public static function set_extension_unavailability_reason( $slug, $reason ) {
246 _deprecated_function( __METHOD__, '7.1', 'Jetpack_Gutenberg::set_extension_unavailable' );
247
248 self::set_extension_unavailable( $slug, $reason );
249 }
250
251 /**
252 * Set up a list of allowed block editor extensions
253 *
254 * @return void
255 */
256 public static function init() {
257 if ( ! self::should_load() ) {
258 return;
259 }
260
261 /**
262 * Alternative to `JETPACK_BETA_BLOCKS`, set to `true` to load Beta Blocks.
263 *
264 * @since 6.9.0
265 *
266 * @param boolean
267 */
268 if ( apply_filters( 'jetpack_load_beta_blocks', false ) ) {
269 Constants::set_constant( 'JETPACK_BETA_BLOCKS', true );
270 }
271
272 /**
273 * Alternative to `JETPACK_EXPERIMENTAL_BLOCKS`, set to `true` to load Experimental Blocks.
274 *
275 * @since 8.4.0
276 *
277 * @param boolean
278 */
279 if ( apply_filters( 'jetpack_load_experimental_blocks', false ) ) {
280 Constants::set_constant( 'JETPACK_EXPERIMENTAL_BLOCKS', true );
281 }
282
283 /**
284 * Filter the list of block editor extensions that are available through Jetpack.
285 *
286 * @since 7.0.0
287 *
288 * @param array
289 */
290 self::$extensions = apply_filters( 'jetpack_set_available_extensions', self::get_available_extensions() );
291
292 /**
293 * Filter the list of block editor plugins that are available through Jetpack.
294 *
295 * @deprecated 7.0.0 Use jetpack_set_available_extensions instead
296 *
297 * @since 6.8.0
298 *
299 * @param array
300 */
301 self::$extensions = apply_filters( 'jetpack_set_available_blocks', self::$extensions );
302
303 /**
304 * Filter the list of block editor plugins that are available through Jetpack.
305 *
306 * @deprecated 7.0.0 Use jetpack_set_available_extensions instead
307 *
308 * @since 6.9.0
309 *
310 * @param array
311 */
312 self::$extensions = apply_filters( 'jetpack_set_available_plugins', self::$extensions );
313 }
314
315 /**
316 * Resets the class to its original state
317 *
318 * Used in unit tests
319 *
320 * @return void
321 */
322 public static function reset() {
323 self::$extensions = array();
324 self::$availability = array();
325 }
326
327 /**
328 * Return the Gutenberg extensions (blocks and plugins) directory
329 *
330 * @return string The Gutenberg extensions directory
331 */
332 public static function get_blocks_directory() {
333 /**
334 * Filter to select Gutenberg blocks directory
335 *
336 * @since 6.9.0
337 *
338 * @param string default: '_inc/blocks/'
339 */
340 return apply_filters( 'jetpack_blocks_directory', '_inc/blocks/' );
341 }
342
343 /**
344 * Checks for a given .json file in the blocks folder.
345 *
346 * @param string $preset The name of the .json file to look for.
347 *
348 * @return bool True if the file is found.
349 */
350 public static function preset_exists( $preset ) {
351 return file_exists( JETPACK__PLUGIN_DIR . self::get_blocks_directory() . $preset . '.json' );
352 }
353
354 /**
355 * Decodes JSON loaded from a preset file in the blocks folder
356 *
357 * @param string $preset The name of the .json file to load.
358 *
359 * @return mixed Returns an object if the file is present, or false if a valid .json file is not present.
360 */
361 public static function get_preset( $preset ) {
362 return json_decode(
363 // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
364 file_get_contents( JETPACK__PLUGIN_DIR . self::get_blocks_directory() . $preset . '.json' )
365 );
366 }
367
368 /**
369 * Returns a list of Jetpack Gutenberg extensions (blocks and plugins), based on index.json
370 *
371 * @return array A list of blocks: eg [ 'publicize', 'markdown' ]
372 */
373 public static function get_jetpack_gutenberg_extensions_allowed_list() {
374 $preset_extensions_manifest = self::preset_exists( 'index' )
375 ? self::get_preset( 'index' )
376 : (object) array();
377 $blocks_variation = self::blocks_variation();
378
379 return self::get_extensions_preset_for_variation( $preset_extensions_manifest, $blocks_variation );
380 }
381
382 /**
383 * Returns a list of Jetpack Gutenberg extensions (blocks and plugins), based on index.json
384 *
385 * @deprecated 8.7.0 Use get_jetpack_gutenberg_extensions_allowed_list()
386 *
387 * @return array A list of blocks: eg [ 'publicize', 'markdown' ]
388 */
389 public static function get_jetpack_gutenberg_extensions_whitelist() {
390 _deprecated_function( __FUNCTION__, 'jetpack-8.7.0', 'Jetpack_Gutenberg::get_jetpack_gutenberg_extensions_allowed_list' );
391 return self::get_jetpack_gutenberg_extensions_allowed_list();
392 }
393
394 /**
395 * Returns a diff from a combined list of allowed extensions and extensions determined to be excluded
396 *
397 * @param array $allowed_extensions An array of allowed extensions.
398 *
399 * @return array A list of blocks: eg array( 'publicize', 'markdown' )
400 */
401 public static function get_available_extensions( $allowed_extensions = null ) {
402 $exclusions = get_option( 'jetpack_excluded_extensions', array() );
403 $allowed_extensions = is_null( $allowed_extensions ) ? self::get_jetpack_gutenberg_extensions_allowed_list() : $allowed_extensions;
404
405 return array_diff( $allowed_extensions, $exclusions );
406 }
407
408 /**
409 * Return true if the extension has been registered and there's nothing in the availablilty array.
410 *
411 * @param string $extension The name of the extension.
412 *
413 * @return bool whether the extension has been registered and there's nothing in the availablilty array.
414 */
415 public static function is_registered_and_no_entry_in_availability( $extension ) {
416 return self::is_registered( 'jetpack/' . $extension ) && ! isset( self::$availability[ $extension ] );
417 }
418
419 /**
420 * Return true if the extension has a true entry in the availablilty array.
421 *
422 * @param string $extension The name of the extension.
423 *
424 * @return bool whether the extension has a true entry in the availablilty array.
425 */
426 public static function is_available( $extension ) {
427 return isset( self::$availability[ $extension ] ) && true === self::$availability[ $extension ];
428 }
429
430 /**
431 * Get availability of each block / plugin.
432 *
433 * @return array A list of block and plugins and their availablity status
434 */
435 public static function get_availability() {
436 /**
437 * Fires before Gutenberg extensions availability is computed.
438 *
439 * In the function call you supply, use `Blocks::jetpack_register_block()` to set a block as available.
440 * Alternatively, use `Jetpack_Gutenberg::set_extension_available()` (for a non-block plugin), and
441 * `Jetpack_Gutenberg::set_extension_unavailable()` (if the block or plugin should not be registered
442 * but marked as unavailable).
443 *
444 * @since 7.0.0
445 */
446 do_action( 'jetpack_register_gutenberg_extensions' );
447
448 $available_extensions = array();
449
450 foreach ( self::$extensions as $extension ) {
451 $is_available = self::is_registered_and_no_entry_in_availability( $extension ) || self::is_available( $extension );
452 $available_extensions[ $extension ] = array(
453 'available' => $is_available,
454 );
455
456 if ( ! $is_available ) {
457 $reason = isset( self::$availability[ $extension ] ) ? self::$availability[ $extension ]['reason'] : 'missing_module';
458 $details = isset( self::$availability[ $extension ] ) ? self::$availability[ $extension ]['details'] : array();
459 $available_extensions[ $extension ]['unavailable_reason'] = $reason;
460 $available_extensions[ $extension ]['details'] = $details;
461 }
462 }
463
464 return $available_extensions;
465 }
466
467 /**
468 * Check if an extension/block is already registered
469 *
470 * @since 7.2
471 *
472 * @param string $slug Name of extension/block to check.
473 *
474 * @return bool
475 */
476 public static function is_registered( $slug ) {
477 return WP_Block_Type_Registry::get_instance()->is_registered( $slug );
478 }
479
480 /**
481 * Check if Gutenberg editor is available
482 *
483 * @since 6.7.0
484 *
485 * @return bool
486 */
487 public static function is_gutenberg_available() {
488 return true;
489 }
490
491 /**
492 * Check whether conditions indicate Gutenberg Extensions (blocks and plugins) should be loaded
493 *
494 * Loading blocks and plugins is enabled by default and may be disabled via filter:
495 * add_filter( 'jetpack_gutenberg', '__return_false' );
496 *
497 * @since 6.9.0
498 *
499 * @return bool
500 */
501 public static function should_load() {
502 if ( ! Jetpack::is_active() && ! ( new Status() )->is_offline_mode() ) {
503 return false;
504 }
505
506 /**
507 * Filter to disable Gutenberg blocks
508 *
509 * @since 6.5.0
510 *
511 * @param bool true Whether to load Gutenberg blocks
512 */
513 return (bool) apply_filters( 'jetpack_gutenberg', true );
514 }
515
516 /**
517 * Only enqueue block assets when needed.
518 *
519 * @param string $type Slug of the block.
520 * @param array $script_dependencies Script dependencies. Will be merged with automatically
521 * detected script dependencies from the webpack build.
522 *
523 * @return void
524 */
525 public static function load_assets_as_required( $type, $script_dependencies = array() ) {
526 if ( is_admin() ) {
527 // A block's view assets will not be required in wp-admin.
528 return;
529 }
530
531 $type = sanitize_title_with_dashes( $type );
532 self::load_styles_as_required( $type );
533 self::load_scripts_as_required( $type, $script_dependencies );
534 }
535
536 /**
537 * Only enqueue block sytles when needed.
538 *
539 * @param string $type Slug of the block.
540 *
541 * @since 7.2.0
542 *
543 * @return void
544 */
545 public static function load_styles_as_required( $type ) {
546 if ( is_admin() ) {
547 // A block's view assets will not be required in wp-admin.
548 return;
549 }
550
551 // Enqueue styles.
552 $style_relative_path = self::get_blocks_directory() . $type . '/view' . ( is_rtl() ? '.rtl' : '' ) . '.css';
553 if ( self::block_has_asset( $style_relative_path ) ) {
554 $style_version = self::get_asset_version( $style_relative_path );
555 $view_style = plugins_url( $style_relative_path, JETPACK__PLUGIN_FILE );
556 wp_enqueue_style( 'jetpack-block-' . $type, $view_style, array(), $style_version );
557 }
558
559 }
560
561 /**
562 * Only enqueue block scripts when needed.
563 *
564 * @param string $type Slug of the block.
565 * @param array $script_dependencies Script dependencies. Will be merged with automatically
566 * detected script dependencies from the webpack build.
567 *
568 * @since 7.2.0
569 *
570 * @return void
571 */
572 public static function load_scripts_as_required( $type, $script_dependencies = array() ) {
573 if ( is_admin() ) {
574 // A block's view assets will not be required in wp-admin.
575 return;
576 }
577
578 // Enqueue script.
579 $script_relative_path = self::get_blocks_directory() . $type . '/view.js';
580 $script_deps_path = JETPACK__PLUGIN_DIR . self::get_blocks_directory() . $type . '/view.asset.php';
581 $script_dependencies[] = 'wp-polyfill';
582 if ( file_exists( $script_deps_path ) ) {
583 $asset_manifest = include $script_deps_path;
584 $script_dependencies = array_unique( array_merge( $script_dependencies, $asset_manifest['dependencies'] ) );
585 }
586
587 if ( ! Blocks::is_amp_request() && self::block_has_asset( $script_relative_path ) ) {
588 $script_version = self::get_asset_version( $script_relative_path );
589 $view_script = plugins_url( $script_relative_path, JETPACK__PLUGIN_FILE );
590 wp_enqueue_script( 'jetpack-block-' . $type, $view_script, $script_dependencies, $script_version, false );
591 }
592
593 wp_localize_script(
594 'jetpack-block-' . $type,
595 'Jetpack_Block_Assets_Base_Url',
596 plugins_url( self::get_blocks_directory(), JETPACK__PLUGIN_FILE )
597 );
598 }
599
600 /**
601 * Check if an asset exists for a block.
602 *
603 * @param string $file Path of the file we are looking for.
604 *
605 * @return bool $block_has_asset Does the file exist.
606 */
607 public static function block_has_asset( $file ) {
608 return file_exists( JETPACK__PLUGIN_DIR . $file );
609 }
610
611 /**
612 * Get the version number to use when loading the file. Allows us to bypass cache when developing.
613 *
614 * @param string $file Path of the file we are looking for.
615 *
616 * @return string $script_version Version number.
617 */
618 public static function get_asset_version( $file ) {
619 return Jetpack::is_development_version() && self::block_has_asset( $file )
620 ? filemtime( JETPACK__PLUGIN_DIR . $file )
621 : JETPACK__VERSION;
622 }
623
624 /**
625 * Load Gutenberg editor assets
626 *
627 * @since 6.7.0
628 *
629 * @return void
630 */
631 public static function enqueue_block_editor_assets() {
632 if ( ! self::should_load() ) {
633 return;
634 }
635
636 // Required for Analytics. See _inc/lib/admin-pages/class.jetpack-admin-page.php.
637 if ( ! ( new Status() )->is_offline_mode() && Jetpack::is_active() ) {
638 wp_enqueue_script( 'jp-tracks', '//stats.wp.com/w.js', array(), gmdate( 'YW' ), true );
639 }
640
641 $rtl = is_rtl() ? '.rtl' : '';
642 $blocks_dir = self::get_blocks_directory();
643 $blocks_variation = self::blocks_variation();
644
645 if ( 'production' !== $blocks_variation ) {
646 $blocks_env = '-' . esc_attr( $blocks_variation );
647 } else {
648 $blocks_env = '';
649 }
650
651 $editor_script = plugins_url( "{$blocks_dir}editor{$blocks_env}.js", JETPACK__PLUGIN_FILE );
652 $editor_style = plugins_url( "{$blocks_dir}editor{$blocks_env}{$rtl}.css", JETPACK__PLUGIN_FILE );
653
654 $editor_deps_path = JETPACK__PLUGIN_DIR . $blocks_dir . "editor{$blocks_env}.asset.php";
655 $editor_deps = array( 'wp-polyfill' );
656 if ( file_exists( $editor_deps_path ) ) {
657 $asset_manifest = include $editor_deps_path;
658 $editor_deps = $asset_manifest['dependencies'];
659 }
660
661 $version = Jetpack::is_development_version() && file_exists( JETPACK__PLUGIN_DIR . $blocks_dir . 'editor.js' )
662 ? filemtime( JETPACK__PLUGIN_DIR . $blocks_dir . 'editor.js' )
663 : JETPACK__VERSION;
664
665 if ( method_exists( 'Jetpack', 'build_raw_urls' ) ) {
666 $site_fragment = Jetpack::build_raw_urls( home_url() );
667 } elseif ( class_exists( 'WPCOM_Masterbar' ) && method_exists( 'WPCOM_Masterbar', 'get_calypso_site_slug' ) ) {
668 $site_fragment = WPCOM_Masterbar::get_calypso_site_slug( get_current_blog_id() );
669 } else {
670 $site_fragment = '';
671 }
672
673 wp_enqueue_script(
674 'jetpack-blocks-editor',
675 $editor_script,
676 $editor_deps,
677 $version,
678 false
679 );
680
681 wp_localize_script(
682 'jetpack-blocks-editor',
683 'Jetpack_Block_Assets_Base_Url',
684 plugins_url( $blocks_dir . '/', JETPACK__PLUGIN_FILE )
685 );
686
687 if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
688 $user = wp_get_current_user();
689 $user_data = array(
690 'userid' => $user->ID,
691 'username' => $user->user_login,
692 );
693 $blog_id = get_current_blog_id();
694 $is_current_user_connected = true;
695 } else {
696 $user_data = Jetpack_Tracks_Client::get_connected_user_tracks_identity();
697 $blog_id = Jetpack_Options::get_option( 'id', 0 );
698 $is_current_user_connected = Jetpack::is_user_connected();
699 }
700
701 wp_localize_script(
702 'jetpack-blocks-editor',
703 'Jetpack_Editor_Initial_State',
704 array(
705 'available_blocks' => self::get_availability(),
706 'jetpack' => array(
707 'is_active' => Jetpack::is_active(),
708 'is_current_user_connected' => $is_current_user_connected,
709 /** This filter is documented in class.jetpack-gutenberg.php */
710 'enable_upgrade_nudge' => apply_filters( 'jetpack_block_editor_enable_upgrade_nudge', false ),
711 ),
712 'siteFragment' => $site_fragment,
713 'adminUrl' => esc_url( admin_url() ),
714 'tracksUserData' => $user_data,
715 'wpcomBlogId' => $blog_id,
716 'allowedMimeTypes' => wp_get_mime_types(),
717 )
718 );
719
720 wp_set_script_translations( 'jetpack-blocks-editor', 'jetpack' );
721
722 wp_enqueue_style( 'jetpack-blocks-editor', $editor_style, array(), $version );
723 }
724
725 /**
726 * Some blocks do not depend on a specific module,
727 * and can consequently be loaded outside of the usual modules.
728 * We will look for such modules in the extensions/ directory.
729 *
730 * @since 7.1.0
731 */
732 public static function load_independent_blocks() {
733 if ( self::should_load() ) {
734 /**
735 * Look for files that match our list of available Jetpack Gutenberg extensions (blocks and plugins).
736 * If available, load them.
737 */
738 foreach ( self::$extensions as $extension ) {
739 $extension_file_glob = glob( JETPACK__PLUGIN_DIR . 'extensions/*/' . $extension . '/' . $extension . '.php' );
740 if ( ! empty( $extension_file_glob ) ) {
741 include_once $extension_file_glob[0];
742 }
743 }
744 }
745 }
746
747 /**
748 * Loads PHP components of extended-blocks.
749 *
750 * @since 8.9.0
751 */
752 public static function load_extended_blocks() {
753 if ( self::should_load() ) {
754 $extended_blocks = glob( JETPACK__PLUGIN_DIR . 'extensions/extended-blocks/*' );
755
756 foreach ( $extended_blocks as $block ) {
757 $name = basename( $block );
758 $path = JETPACK__PLUGIN_DIR . 'extensions/extended-blocks/' . $name . '/' . $name . '.php';
759
760 if ( file_exists( $path ) ) {
761 include_once $path;
762 }
763 }
764 }
765 }
766
767 /**
768 * Get CSS classes for a block.
769 *
770 * @since 7.7.0
771 *
772 * @param string $slug Block slug.
773 * @param array $attr Block attributes.
774 * @param array $extra Potential extra classes you may want to provide.
775 *
776 * @return string $classes List of CSS classes for a block.
777 */
778 public static function block_classes( $slug = '', $attr, $extra = array() ) {
779 _deprecated_function( __METHOD__, '9.0.0', 'Automattic\\Jetpack\\Blocks::classes' );
780 return Blocks::classes( $slug, $attr, $extra );
781 }
782
783 /**
784 * Determine whether a site should use the default set of blocks, or a custom set.
785 * Possible variations are currently beta, experimental, and production.
786 *
787 * @since 8.1.0
788 *
789 * @return string $block_varation production|beta|experimental
790 */
791 public static function blocks_variation() {
792 // Default to production blocks.
793 $block_varation = 'production';
794
795 if ( Constants::is_true( 'JETPACK_BETA_BLOCKS' ) ) {
796 $block_varation = 'beta';
797 }
798
799 /*
800 * Switch to experimental blocks if you use the JETPACK_EXPERIMENTAL_BLOCKS constant.
801 */
802 if ( Constants::is_true( 'JETPACK_EXPERIMENTAL_BLOCKS' ) ) {
803 $block_varation = 'experimental';
804 }
805
806 /**
807 * Allow customizing the variation of blocks in use on a site.
808 *
809 * @since 8.1.0
810 *
811 * @param string $block_variation Can be beta, experimental, and production. Defaults to production.
812 */
813 return apply_filters( 'jetpack_blocks_variation', $block_varation );
814 }
815
816 /**
817 * Get a list of extensions available for the variation you chose.
818 *
819 * @since 8.1.0
820 *
821 * @param obj $preset_extensions_manifest List of extensions available in Jetpack.
822 * @param string $blocks_variation Subset of blocks. production|beta|experimental.
823 *
824 * @return array $preset_extensions Array of extensions for that variation
825 */
826 public static function get_extensions_preset_for_variation( $preset_extensions_manifest, $blocks_variation ) {
827 $preset_extensions = isset( $preset_extensions_manifest->{ $blocks_variation } )
828 ? (array) $preset_extensions_manifest->{ $blocks_variation }
829 : array();
830
831 /*
832 * Experimental and Beta blocks need the production blocks as well.
833 */
834 if (
835 'experimental' === $blocks_variation
836 || 'beta' === $blocks_variation
837 ) {
838 $production_extensions = isset( $preset_extensions_manifest->production )
839 ? (array) $preset_extensions_manifest->production
840 : array();
841
842 $preset_extensions = array_unique( array_merge( $preset_extensions, $production_extensions ) );
843 }
844
845 /*
846 * Beta blocks need the experimental blocks as well.
847 *
848 * If you've chosen to see Beta blocks,
849 * we want to make all blocks available to you:
850 * - Production
851 * - Experimental
852 * - Beta
853 */
854 if ( 'beta' === $blocks_variation ) {
855 $production_extensions = isset( $preset_extensions_manifest->experimental )
856 ? (array) $preset_extensions_manifest->experimental
857 : array();
858
859 $preset_extensions = array_unique( array_merge( $preset_extensions, $production_extensions ) );
860 }
861
862 return $preset_extensions;
863 }
864
865 /**
866 * Validate a URL used in a SSR block.
867 *
868 * @since 8.3.0
869 *
870 * @param string $url URL saved as an attribute in block.
871 * @param array $allowed Array of allowed hosts for that block, or regexes to check against.
872 * @param bool $is_regex Array of regexes matching the URL that could be used in block.
873 *
874 * @return bool|string
875 */
876 public static function validate_block_embed_url( $url, $allowed = array(), $is_regex = false ) {
877 if (
878 empty( $url )
879 || ! is_array( $allowed )
880 || empty( $allowed )
881 ) {
882 return false;
883 }
884
885 $url_components = wp_parse_url( $url );
886
887 // Bail early if we cannot find a host.
888 if ( empty( $url_components['host'] ) ) {
889 return false;
890 }
891
892 // Normalize URL.
893 $url = sprintf(
894 '%s://%s%s%s',
895 isset( $url_components['scheme'] ) ? $url_components['scheme'] : 'https',
896 $url_components['host'],
897 isset( $url_components['path'] ) ? $url_components['path'] : '/',
898 isset( $url_components['query'] ) ? '?' . $url_components['query'] : ''
899 );
900
901 if ( ! empty( $url_components['fragment'] ) ) {
902 $url = $url . '#' . rawurlencode( $url_components['fragment'] );
903 }
904
905 /*
906 * If we're using an allowed list of hosts,
907 * check if the URL belongs to one of the domains allowed for that block.
908 */
909 if (
910 false === $is_regex
911 && in_array( $url_components['host'], $allowed, true )
912 ) {
913 return $url;
914 }
915
916 /*
917 * If we are using an array of regexes to check against,
918 * loop through that.
919 */
920 if ( true === $is_regex ) {
921 foreach ( $allowed as $regex ) {
922 if ( 1 === preg_match( $regex, $url ) ) {
923 return $url;
924 }
925 }
926 }
927
928 return false;
929 }
930
931 /**
932 * Output an UpgradeNudge Component on the frontend of a site.
933 *
934 * @since 8.4.0
935 *
936 * @param string $plan The plan that users need to purchase to make the block work.
937 *
938 * @return string
939 */
940 public static function upgrade_nudge( $plan ) {
941 if (
942 ! current_user_can( 'manage_options' )
943 /** This filter is documented in class.jetpack-gutenberg.php */
944 || ! apply_filters( 'jetpack_block_editor_enable_upgrade_nudge', false )
945 /** This filter is documented in _inc/lib/admin-pages/class.jetpack-react-page.php */
946 || ! apply_filters( 'jetpack_show_promotions', true )
947 || is_feed()
948 ) {
949 return;
950 }
951
952 jetpack_require_lib( 'components' );
953 return Jetpack_Components::render_upgrade_nudge(
954 array(
955 'plan' => $plan,
956 )
957 );
958 }
959
960 /**
961 * Output a notice within a block.
962 *
963 * @since 8.6.0
964 *
965 * @param string $message Notice we want to output.
966 * @param string $status Status of the notice. Can be one of success, info, warning, error. info by default.
967 * @param string $classes List of CSS classes.
968 *
969 * @return string
970 */
971 public static function notice( $message, $status = 'info', $classes = '' ) {
972 if (
973 empty( $message )
974 || ! in_array( $status, array( 'success', 'info', 'warning', 'error' ), true )
975 ) {
976 return '';
977 }
978
979 $color = '';
980 switch ( $status ) {
981 case 'success':
982 $color = '#46b450';
983 break;
984 case 'warning':
985 $color = '#ffb900';
986 break;
987 case 'error':
988 $color = '#dc3232';
989 break;
990 case 'info':
991 default:
992 $color = '#00a0d2';
993 break;
994 }
995
996 return sprintf(
997 '<div class="jetpack-block__notice %1$s %3$s" style="border-left:5px solid %4$s;padding:1em;background-color:#f8f9f9;">%2$s</div>',
998 esc_attr( $status ),
999 wp_kses(
1000 $message,
1001 array(
1002 'br' => array(),
1003 'p' => array(),
1004 )
1005 ),
1006 esc_attr( $classes ),
1007 sanitize_hex_color( $color )
1008 );
1009 }
1010
1011 /**
1012 * Set the availability of the block as the editor
1013 * is loaded.
1014 *
1015 * @param string $slug Slug of the block.
1016 */
1017 public static function set_availability_for_plan( $slug ) {
1018 $is_available = true;
1019 $plan = '';
1020 $slug = self::remove_extension_prefix( $slug );
1021
1022 if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
1023 if ( ! class_exists( 'Store_Product_List' ) ) {
1024 require WP_CONTENT_DIR . '/admin-plugins/wpcom-billing/store-product-list.php';
1025 }
1026 $features_data = Store_Product_List::get_site_specific_features_data();
1027 $is_available = in_array( $slug, $features_data['active'], true );
1028 if ( ! empty( $features_data['available'][ $slug ] ) ) {
1029 $plan = $features_data['available'][ $slug ][0];
1030 }
1031 } elseif ( ! jetpack_is_atomic_site() ) {
1032 /*
1033 * If it's Atomic then assume all features are available
1034 * otherwise check against the Jetpack plan.
1035 */
1036 $is_available = Jetpack_Plan::supports( $slug );
1037 $plan = Jetpack_Plan::get_minimum_plan_for_feature( $slug );
1038 }
1039 if ( $is_available ) {
1040 self::set_extension_available( $slug );
1041 } else {
1042 self::set_extension_unavailable(
1043 $slug,
1044 'missing_plan',
1045 array(
1046 'required_feature' => $slug,
1047 'required_plan' => $plan,
1048 )
1049 );
1050 }
1051 }
1052
1053 /**
1054 * Wraps the suplied render_callback in a function to check
1055 * the availability of the block before rendering it.
1056 *
1057 * @param string $slug The block slug, used to check for availability.
1058 * @param callable $render_callback The render_callback that will be called if the block is available.
1059 */
1060 public static function get_render_callback_with_availability_check( $slug, $render_callback ) {
1061 return function ( $prepared_attributes, $block_content ) use ( $render_callback, $slug ) {
1062 $availability = self::get_availability();
1063 $bare_slug = self::remove_extension_prefix( $slug );
1064 if ( isset( $availability[ $bare_slug ] ) && $availability[ $bare_slug ]['available'] ) {
1065 return call_user_func( $render_callback, $prepared_attributes, $block_content );
1066 }
1067
1068 return null;
1069 };
1070 }
1071 }
1072