PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3
Jetpack – WP Security, Backup, Speed, & Growth v16.3
16.3 16.3-beta 16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 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 All 508 releases
← All changes | jetpack_vendor/automattic/jetpack-blocks/src/class-blocks.php +581 -0 16.2-beta → 16.3 View file →
@@ -1,0 +1,581 @@
1 +<?php
2 +/** Blocks package.
3 + *
4 + * @since 1.1.0
5 + *
6 + * This package lifts elements from Jetpack's Jetpack_Gutenberg class.
7 + * It is now an standalone package reusable outside Jetpack.
8 + *
9 + * @package automattic/jetpack-blocks
10 + */
11 +
12 +namespace Automattic\Jetpack;
13 +
14 +use Automattic\Jetpack\Constants as Jetpack_Constants;
15 +use Jetpack_Gutenberg;
16 +
17 +/**
18 + * Register and manage blocks within a plugin. Used to manage block registration, enqueues, and more.
19 + *
20 + * @since 1.1.0
21 + */
22 +class Blocks {
23 + /**
24 + * Wrapper function to safely register a Gutenberg block type
25 + *
26 + * @see register_block_type
27 + * @see Automattic\Jetpack\Blocks::is_gutenberg_version_available
28 + *
29 + * @since 1.1.0
30 + *
31 + * @param string $slug Slug of the block or absolute path to the block source code directory.
32 + * @param array $args {
33 + * Arguments that are passed into register_block_type.
34 + * See register_block_type for full list of arguments.
35 + * Can also include 2 extra arguments not currently supported by register_block_type.
36 + *
37 + * @type array $version_requirements Array containing required Gutenberg version and, if known, the WordPress version that was released with this minimum version.
38 + * @type bool $plan_check Should we check for a specific plan before registering the block.
39 + * }
40 + *
41 + * @return \WP_Block_Type|false The registered block type on success, or false on failure.
42 + */
43 + public static function jetpack_register_block( $slug, $args = array() ) {
44 + // Slug doesn't start with `jetpack/`, isn't an absolute path, or doesn't contain a slash
45 + // (synonym of a namespace) at all.
46 + if ( ! str_starts_with( $slug, 'jetpack/' ) && ! path_is_absolute( $slug ) && ! strpos( $slug, '/' ) ) {
47 + _doing_it_wrong( 'jetpack_register_block', 'Prefix the block with jetpack/ ', 'Jetpack 9.0.0' );
48 + $slug = 'jetpack/' . $slug;
49 + }
50 +
51 + $block_type = $slug;
52 +
53 + // If a path is passed, make sure to get the block.json file from the build directory and get
54 + // the block name from that file.
55 + if ( path_is_absolute( $slug ) ) {
56 + $block_type = self::get_path_to_block_metadata( $slug );
57 + $slug = self::get_block_name_from_path_convention( $slug );
58 + }
59 +
60 + if (
61 + isset( $args['version_requirements'] )
62 + && ! self::is_gutenberg_version_available( $args['version_requirements'], $slug )
63 + ) {
64 + return false;
65 + }
66 +
67 + // Checking whether block is registered to ensure it isn't registered twice.
68 + if ( self::is_registered( $slug ) ) {
69 + return false;
70 + }
71 +
72 + $feature_name = self::remove_extension_prefix( $slug );
73 +
74 + // This is only useful in Jetpack.
75 + if ( ! self::is_standalone_block() ) {
76 + // If the block is dynamic, and a Jetpack block, wrap the render_callback to check availability.
77 + if ( ! empty( $args['plan_check'] ) ) {
78 + $existing_attributes = array();
79 + $gated_blocks = array(
80 + 'jetpack/calendly',
81 + 'jetpack/donations',
82 + 'jetpack/payment-buttons',
83 + 'jetpack/paypal-payment-buttons',
84 + );
85 + if ( in_array( $slug, $gated_blocks, true ) &&
86 + is_string( $block_type ) &&
87 + file_exists( $block_type )
88 + ) {
89 + $metadata = self::get_block_metadata( $block_type );
90 + $existing_attributes = $metadata['attributes'] ?? array();
91 + }
92 +
93 + // Set up attributes.
94 + if ( ! isset( $args['attributes'] ) ) {
95 + $args['attributes'] = array();
96 + }
97 + $args['attributes'] = array_merge(
98 + $existing_attributes,
99 + $args['attributes'],
100 + array(
101 + // Indicates that this block should display an upgrade nudge on the frontend when applicable.
102 + 'shouldDisplayFrontendBanner' => array(
103 + 'type' => 'boolean',
104 + 'default' => true,
105 + ),
106 + )
107 + );
108 + if ( isset( $args['render_callback'] ) ) {
109 + $args['render_callback'] = Jetpack_Gutenberg::get_render_callback_with_availability_check( $feature_name, $args['render_callback'] );
110 + }
111 + $method_name = 'set_availability_for_plan';
112 + } else {
113 + $method_name = 'set_extension_available';
114 + }
115 +
116 + add_action(
117 + 'jetpack_register_gutenberg_extensions',
118 + function () use ( $feature_name, $method_name ) {
119 + call_user_func( array( 'Jetpack_Gutenberg', $method_name ), $feature_name );
120 + }
121 + );
122 +
123 + // Ensure editor styles are registered so that the site editor knows about the
124 + // editor style dependency when copying styles to the editor iframe.
125 + if ( ! isset( $args['editor_style'] ) ) {
126 + $args['editor_style'] = 'jetpack-blocks-editor';
127 + }
128 +
129 + // Keep track of the JS loading strategy for any block that specifies it.
130 + if ( isset( $args['js_loading_strategy'] ) ) {
131 + Jetpack_Gutenberg::set_block_js_loading_strategy( $feature_name, $args['js_loading_strategy'] );
132 + }
133 + }
134 +
135 + return register_block_type( $block_type, $args );
136 + }
137 +
138 + /**
139 + * Get the block metadata. Accepts a block.json file's path (or its folder's) or its content, in
140 + * which case it becomes an identity function.
141 + *
142 + * It's used by other helpers in this class so that they can accept various types as argument.
143 + *
144 + * @param string|array $arg Path to block.json or its parent folder, or its content as an array.
145 + *
146 + * @return array The block metadata.
147 + */
148 + private static function get_block_metadata( $arg ) {
149 + $metadata = is_array( $arg ) ? $arg : null;
150 +
151 + if ( ! isset( $metadata ) ) {
152 + $path = is_string( $arg ) ? $arg : null;
153 +
154 + if ( isset( $path ) && ! empty( $path ) ) {
155 + $metadata = self::get_block_metadata_from_file( self::get_path_to_block_metadata( $path ) );
156 + }
157 + }
158 +
159 + return $metadata ?? array();
160 + }
161 +
162 + /**
163 + * Read block metadata from a block.json file.
164 + *
165 + * @param string $filename The path to the block.json file or its directory.
166 + *
167 + * @return array The block metadata.
168 + */
169 + public static function get_block_metadata_from_file( $filename ) {
170 + $metadata = array();
171 + $needle = '/block.json';
172 + $filename = $needle === substr( $filename, -strlen( $needle ) ) ? $filename : $filename . $needle;
173 +
174 + if ( file_exists( $filename ) ) {
175 + try {
176 + $metadata = wp_json_file_decode( $filename, array( 'associative' => true ) );
177 + } catch ( \Exception $e ) {
178 + $metadata = array();
179 + }
180 + }
181 +
182 + return $metadata;
183 + }
184 +
185 + /**
186 + * Get the block name (includes the `jetpack` prefix).
187 + *
188 + * @param string|array $arg Path to block.json or its parent folder, or its content as an array.
189 + *
190 + * @return string The block name.
191 + */
192 + public static function get_block_name( $arg ) {
193 + $metadata = self::get_block_metadata( $arg );
194 +
195 + return self::get_block_name_from_metadata( $metadata );
196 + }
197 +
198 + /**
199 + * Get the block name from the path convention.
200 + * For example, path "./extensions/blocks/pinterest" is assumed to define
201 + * a block named "jetpack/pinterest" without checking any files.
202 + *
203 + * Any exceptions should be added to the $breaks_convention array.
204 + * For example, blocks/premium-content defines "premium-content\/container", not "jetpack/premium-content".
205 + * These paths will use the code that checks the disk for the block name.
206 + *
207 + * The unit test test_get_block_name_from_path_convention_matches_get_block_name() verifies that
208 + * all names are correctly guessed.
209 + *
210 + * Run with `./vendor/bin/phpunit --filter WP_Test_Jetpack_Gutenberg`.
211 + *
212 + * @param string $path The path to extract the block name from.
213 + *
214 + * @return string The block name with 'jetpack/' prefix.
215 + */
216 + public static function get_block_name_from_path_convention( $path ) {
217 + $path_parts = explode( '/', $path );
218 + if ( count( $path_parts ) <= 0 ) {
219 + $block_type = self::get_path_to_block_metadata( $path );
220 + return self::get_block_name( $block_type );
221 + }
222 +
223 + $last_part = $path_parts[ count( $path_parts ) - 1 ];
224 + $breaks_convention = array( 'premium-content' );
225 +
226 + if ( in_array( $last_part, $breaks_convention, true ) ) {
227 + $block_type = self::get_path_to_block_metadata( $path );
228 + return self::get_block_name( $block_type );
229 + }
230 +
231 + return 'jetpack/' . $last_part;
232 + }
233 +
234 + /**
235 + * Get the block name from the its metadata.
236 + *
237 + * @param array $metadata The block metadata.
238 + *
239 + * @return string The block name.
240 + */
241 + public static function get_block_name_from_metadata( $metadata ) {
242 + return ! isset( $metadata['name'] ) || empty( $metadata['name'] ) ? '' : $metadata['name'];
243 + }
244 +
245 + /**
246 + * Get the block feature name (i.e. the name without the `jetpack` prefix).
247 + *
248 + * @param string|array $arg Path to block.json or its parent folder, or its content as an array.
249 + *
250 + * @return string The block feature name.
251 + */
252 + public static function get_block_feature( $arg ) {
253 + $metadata = self::get_block_metadata( $arg );
254 +
255 + return self::get_block_feature_from_metadata( $metadata );
256 + }
257 +
258 + /**
259 + * Get the block feature name (i.e. the name without the `jetpack` prefix) from its metadata.
260 + *
261 + * @param array $metadata The block metadata.
262 + *
263 + * @return string The block feature name.
264 + */
265 + public static function get_block_feature_from_metadata( $metadata ) {
266 + return str_replace( 'jetpack/', '', self::get_block_name_from_metadata( $metadata ) );
267 + }
268 +
269 + /**
270 + * Check if an extension/block is already registered
271 + *
272 + * @since 1.1.0
273 + *
274 + * @param string $slug Name of extension/block to check.
275 + *
276 + * @return bool
277 + */
278 + public static function is_registered( $slug ) {
279 + return \WP_Block_Type_Registry::get_instance()->is_registered( $slug );
280 + }
281 +
282 + /**
283 + * Remove the 'jetpack/' or jetpack-' prefix from an extension name
284 + *
285 + * @since 1.1.0
286 + *
287 + * @param string $extension_name The extension name.
288 + *
289 + * @return string The unprefixed extension name.
290 + */
291 + public static function remove_extension_prefix( $extension_name ) {
292 + if ( str_starts_with( $extension_name, 'jetpack/' ) || str_starts_with( $extension_name, 'jetpack-' ) ) {
293 + return substr( $extension_name, strlen( 'jetpack/' ) );
294 + }
295 + return $extension_name;
296 + }
297 +
298 + /**
299 + * Check to see if a minimum version of Gutenberg is available. Because a Gutenberg version is not available in
300 + * php if the Gutenberg plugin is not installed, if we know which minimum WP release has the required version we can
301 + * optionally fall back to that.
302 + *
303 + * @since 1.1.0
304 + *
305 + * @param array $version_requirements {
306 + * An array containing the required Gutenberg version and, if known, the WordPress version that was released with this minimum version.
307 + *
308 + * @type string $gutenberg Gutenberg version.
309 + * @type string $wp Optional. WordPress version.
310 + * }
311 + * @param string $slug The slug of the block or plugin that has the Gutenberg version requirement.
312 + *
313 + * @return boolean True if the version of Gutenberg required by the block or plugin is available.
314 + */
315 + public static function is_gutenberg_version_available( $version_requirements, $slug ) {
316 + global $wp_version;
317 +
318 + // Bail if we don't at least have the Gutenberg version requirement, the WP version is optional.
319 + if ( empty( $version_requirements['gutenberg'] ) ) {
320 + return false;
321 + }
322 +
323 + // If running a local dev build of Gutenberg plugin GUTENBERG_DEVELOPMENT_MODE is set so assume correct version.
324 + if ( defined( 'GUTENBERG_DEVELOPMENT_MODE' ) && GUTENBERG_DEVELOPMENT_MODE ) {
325 + return true;
326 + }
327 +
328 + $version_available = false;
329 +
330 + // If running a production build of the Gutenberg plugin then GUTENBERG_VERSION is set, otherwise if WP version
331 + // with required version of Gutenberg is known check that.
332 + if ( defined( 'GUTENBERG_VERSION' ) ) {
333 + $version_available = version_compare( GUTENBERG_VERSION, $version_requirements['gutenberg'], '>=' );
334 + } elseif ( ! empty( $version_requirements['wp'] ) ) {
335 + $version_available = version_compare( $wp_version, $version_requirements['wp'], '>=' );
336 + }
337 +
338 + if (
339 + ! $version_available
340 + && ! self::is_standalone_block() // This is only useful in Jetpack.
341 + ) {
342 + $slug = Jetpack_Gutenberg::remove_extension_prefix( $slug );
343 + Jetpack_Gutenberg::set_extension_unavailable(
344 + $slug,
345 + 'incorrect_gutenberg_version',
346 + array(
347 + 'required_feature' => $slug,
348 + 'required_version' => $version_requirements,
349 + 'current_version' => array(
350 + 'wp' => $wp_version,
351 + 'gutenberg' => defined( 'GUTENBERG_VERSION' ) ? GUTENBERG_VERSION : null,
352 + ),
353 + )
354 + );
355 + }
356 +
357 + return $version_available;
358 + }
359 +
360 + /**
361 + * Get CSS classes for a block.
362 + *
363 + * @since 1.1.0
364 + *
365 + * @param string $slug Block slug.
366 + * @param array $attr Block attributes.
367 + * @param array $extra Potential extra classes you may want to provide.
368 + *
369 + * @return string $classes List of CSS classes for a block.
370 + */
371 + public static function classes( $slug, $attr, $extra = array() ) {
372 + if ( empty( $slug ) ) {
373 + return '';
374 + }
375 +
376 + // Basic block name class.
377 + $classes = array(
378 + 'wp-block-jetpack-' . $slug,
379 + );
380 +
381 + // Add alignment if provided.
382 + if (
383 + ! empty( $attr['align'] )
384 + && in_array( $attr['align'], array( 'left', 'center', 'right', 'wide', 'full' ), true )
385 + ) {
386 + $classes[] = 'align' . $attr['align'];
387 + }
388 +
389 + // Add custom classes if provided in the block editor.
390 + if ( ! empty( $attr['className'] ) ) {
391 + $classes[] = $attr['className'];
392 + }
393 +
394 + // Add any extra classes.
395 + if ( is_array( $extra ) && ! empty( $extra ) ) {
396 + $classes = array_merge( $classes, array_filter( $extra ) );
397 + }
398 +
399 + return implode( ' ', $classes );
400 + }
401 +
402 + /**
403 + * Does the page return AMP content.
404 + *
405 + * @since 1.1.0
406 + *
407 + * @return bool $is_amp_request Are we on an AMP view.
408 + */
409 + public static function is_amp_request() {
410 + $is_amp_request = ( function_exists( 'is_amp_endpoint' ) && is_amp_endpoint() );
411 +
412 + /** This filter is documented in 3rd-party/class.jetpack-amp-support.php */
413 + return apply_filters( 'jetpack_is_amp_request', $is_amp_request );
414 + }
415 +
416 + /**
417 + * Is the current theme an FSE/Site Editor theme.
418 + *
419 + * @since 1.4.0
420 + * @since 1.4.22 Remove support for deprecated `gutenberg_is_fse_theme` function.
421 + *
422 + * @return bool True if the current theme is an FSE/Site Editor theme.
423 + */
424 + public static function is_fse_theme() {
425 + $is_fse_theme = wp_is_block_theme();
426 +
427 + /**
428 + * Returns true if the current theme is an FSE/Site Editor theme.
429 + *
430 + * @since 1.4.0
431 + *
432 + * @param boolean $is_fse_theme Is the theme an FSE theme.
433 + */
434 + return apply_filters( 'jetpack_is_fse_theme', $is_fse_theme );
435 + }
436 +
437 + /**
438 + * Check whether or the block being registered is a standalone block,
439 + * running in a context outside of the Jetpack plugin.
440 + *
441 + * @since 1.3.0
442 + *
443 + * @return bool
444 + */
445 + public static function is_standalone_block() {
446 + $is_standalone_block = ! class_exists( Jetpack_Gutenberg::class );
447 +
448 + /**
449 + * Returns true if the block is not being registered within a Jetpack plugin context.
450 + *
451 + * @since 1.3.0
452 + *
453 + * @param boolean $is_standalone_block Is the block running standalone versus as part of the Jetpack plugin.
454 + */
455 + return apply_filters( 'jetpack_is_standalone_block', $is_standalone_block );
456 + }
457 +
458 + /**
459 + * Returns the path to the directory containing the block.json metadata file of a block, given its
460 + * source code directory and, optionally, the directory that holds the blocks built files of
461 + * the package. If the directory doesn't exist, falls back to the source directory.
462 + *
463 + * @since 1.6.0
464 + *
465 + * @param string $block_src_dir The path to the folder containing the block source code.
466 + * Typically this is done by passing __DIR__ as the argument.
467 + * @param string $package_dist_dir Optional. A full path to the directory containing the blocks
468 + * built files of the package. Default empty.
469 + *
470 + * @return string The path to the directory.
471 + */
472 + public static function get_path_to_block_metadata( $block_src_dir, $package_dist_dir = '' ) {
473 + $dir = basename( $block_src_dir );
474 + $dist_path = $package_dist_dir;
475 +
476 + if ( empty( $dist_path ) ) {
477 + $plugin_file = Jetpack_Constants::get_constant( 'JETPACK__PLUGIN_FILE' );
478 + // Guard against strange situations where JETPACK__PLUGIN_FILE is undefined.
479 + if ( empty( $plugin_file ) ) {
480 + return $block_src_dir;
481 + }
482 +
483 + $dist_path = dirname( $plugin_file ) . '/_inc/blocks';
484 + }
485 +
486 + $result = realpath( "$dist_path/$dir" );
487 +
488 + return false === $result ? $block_src_dir : $result;
489 + }
490 +
491 + /**
492 + * Determine whether a site should use the default set of blocks, or a custom set.
493 + * Possible variations are currently beta, experimental, and production.
494 + *
495 + * @since 3.1.0
496 + *
497 + * @return string $block_variation production|beta|experimental
498 + */
499 + public static function get_variation() {
500 + // Default to production blocks.
501 + $block_variation = 'production';
502 +
503 + /*
504 + * Prefer to use this JETPACK_BLOCKS_VARIATION constant
505 + * or the jetpack_blocks_variation filter
506 + * to set the block variation in your code.
507 + */
508 + $default = Constants::get_constant( 'JETPACK_BLOCKS_VARIATION' );
509 + if ( ! empty( $default ) && in_array( $default, array( 'beta', 'experimental', 'production' ), true ) ) {
510 + $block_variation = $default;
511 + }
512 +
513 + /**
514 + * Alternative to `JETPACK_BETA_BLOCKS`, set to `true` to load Beta Blocks.
515 + *
516 + * @since jetpack-6.9.0
517 + * @deprecated jetpack-11.8.0 Use jetpack_blocks_variation filter instead.
518 + *
519 + * @param boolean
520 + */
521 + $is_beta = apply_filters_deprecated(
522 + 'jetpack_load_beta_blocks',
523 + array( false ),
524 + 'jetpack-11.8.0',
525 + 'jetpack_blocks_variation'
526 + );
527 +
528 + /*
529 + * Switch to beta blocks if you use the JETPACK_BETA_BLOCKS constant
530 + * or the deprecated jetpack_load_beta_blocks filter.
531 + * This only applies when not using the newer JETPACK_BLOCKS_VARIATION constant.
532 + */
533 + if ( empty( $default )
534 + && (
535 + $is_beta
536 + || Constants::is_true( 'JETPACK_BETA_BLOCKS' )
537 + )
538 + ) {
539 + $block_variation = 'beta';
540 + }
541 +
542 + /**
543 + * Alternative to `JETPACK_EXPERIMENTAL_BLOCKS`, set to `true` to load Experimental Blocks.
544 + *
545 + * @since jetpack-6.9.0
546 + * @deprecated jetpack-11.8.0 Use jetpack_blocks_variation filter instead.
547 + *
548 + * @param boolean
549 + */
550 + $is_experimental = apply_filters_deprecated(
551 + 'jetpack_load_experimental_blocks',
552 + array( false ),
553 + 'jetpack-11.8.0',
554 + 'jetpack_blocks_variation'
555 + );
556 +
557 + /*
558 + * Switch to experimental blocks if you use the JETPACK_EXPERIMENTAL_BLOCKS constant
559 + * or the deprecated jetpack_load_experimental_blocks filter.
560 + * This only applies when not using the newer JETPACK_BLOCKS_VARIATION constant.
561 + */
562 + if ( empty( $default )
563 + && (
564 + $is_experimental
565 + || Constants::is_true( 'JETPACK_EXPERIMENTAL_BLOCKS' )
566 + )
567 + ) {
568 + $block_variation = 'experimental';
569 + }
570 +
571 + /**
572 + * Allow customizing the variation of blocks in use on a site.
573 + * Overwrites any previously set values, whether by constant or filter.
574 + *
575 + * @since jetpack-8.1.0
576 + *
577 + * @param string $block_variation Can be beta, experimental, and production. Defaults to production.
578 + */
579 + return apply_filters( 'jetpack_blocks_variation', $block_variation );
580 + }
581 +}