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-assets/src/class-assets.php +82 -10 13.6.2 → 16.3 View file →
@@ -7,8 +7,9 @@
7 7
8 8 namespace Automattic\Jetpack;
9 9
10 10 use Automattic\Jetpack\Assets\Semver;
11 +use Automattic\Jetpack\Assets\Shared_Stores_Assets;
11 12 use Automattic\Jetpack\Constants as Jetpack_Constants;
12 13 use InvalidArgumentException;
13 14
14 15 /**
@@ -31,13 +32,36 @@
31 32
32 33 /**
33 34 * The registered textdomain mappings.
34 35 *
35 - * @var array `array( mapped_domain => array( string target_domain, string target_type, string semver, string path_prefix ) )`.
36 + * @var array `array( mapped_domain => array( string target_domain, string target_type, string semver ) )`.
36 37 */
37 38 private static $domain_map = array();
38 39
39 40 /**
41 + * The registered package paths, by textdomain.
42 + *
43 + * Separate from `$domain_map` because the two answer different questions:
44 + * the map says which domain a package's strings are translated under, while
45 + * this says where the package's files live — the prefix WordPress hashes to
46 + * name a JS translation file. A package whose textdomain is already its
47 + * plugin's has nothing to alias but still needs the path.
48 + *
49 + * Note the entries are keyed by domain, not by script: `downloadI18n()`
50 + * prepends a domain's prefix to every bundle path looked up under it. That
51 + * is only ever one package's path, so a plugin must not both bundle a
52 + * package whose textdomain is the plugin's own and load its own
53 + * `wp-jp-i18n-loader` bundles under that same domain — the package's prefix
54 + * would be applied to the plugin's catalogs too, and they would all 404. No
55 + * plugin does both today. One that needs to should give the package a
56 + * distinct textdomain, the way `jetpack-backup-pkg` and
57 + * `jetpack-videopress-pkg` do.
58 + *
59 + * @var array `array( domain => array( string semver, string path_prefix ) )`.
60 + */
61 + private static $domain_paths = array();
62 +
63 + /**
40 64 * Constructor.
41 65 *
42 66 * Static-only class, so nothing here.
43 67 */
@@ -378,9 +402,10 @@
378 402 $options['css_dependencies']
379 403 );
380 404 $ver = $options['version'] ?? $asset['version'];
381 405 } else {
382 - $ver = $options['version'] ?? filemtime( "$dir/$path" );
406 + // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
407 + $ver = $options['version'] ?? @filemtime( "$dir/$path" );
383 408 }
384 409
385 410 if ( $options['async'] && '' === $options['strategy'] ) { // Handle the deprecated `async` option
386 411 $options['strategy'] = 'defer';
@@ -445,8 +470,21 @@
445 470 }
446 471 }
447 472
448 473 /**
474 + * Re-hook the bootstraps an older copy's `actions.php` did not know about. See JETPACK-2649.
475 + *
476 + * Static callables only: `add_action()` dedupes those, but closures and object callables would
477 + * register twice. Callers must run before `wp_loaded`.
478 + *
479 + * @access private
480 + * @since 5.0.5
481 + */
482 + public static function ensure_package_bootstrap() {
483 + Shared_Stores_Assets::configure();
484 + }
485 +
486 + /**
449 487 * 'wp_default_scripts' action handler.
450 488 *
451 489 * This registers the `wp-jp-i18n-loader` script for use by Webpack bundles built with
452 490 * `@automattic/i18n-loader-webpack-plugin`.
@@ -472,10 +510,12 @@
472 510 } elseif ( strpos( $lang_dir, $abspath ) === 0 ) {
473 511 $data['baseUrl'] = site_url( substr( trailingslashit( $lang_dir ), strlen( untrailingslashit( $abspath ) ) ) );
474 512 }
475 513
476 - foreach ( self::$domain_map as $from => list( $to, $type, , $path ) ) {
514 + foreach ( self::$domain_map as $from => list( $to, $type ) ) {
477 515 $data['domainMap'][ $from ] = ( 'core' === $type ? '' : "{$type}/" ) . $to;
516 + }
517 + foreach ( self::$domain_paths as $from => list( , $path ) ) {
478 518 if ( '' !== $path ) {
479 519 $data['domainPaths'][ $from ] = trailingslashit( $path );
480 520 }
481 521 }
@@ -508,9 +548,16 @@
508 548 );
509 549 }
510 550 $url = self::normalize_path( plugins_url( $path, __FILE__ ) );
511 551 $url = add_query_arg( 'minify', 'true', $url );
512 - $wp_scripts->add( 'wp-jp-i18n-loader', $url, $asset['dependencies'], $asset['version'] );
552 +
553 + $handle = 'wp-jp-i18n-loader';
554 +
555 + $wp_scripts->add( $handle, $url, $asset['dependencies'], $asset['version'] );
556 +
557 + // Ensure the script is loaded in the footer and deferred.
558 + $wp_scripts->add_data( $handle, 'group', 1 );
559 +
513 560 if ( ! is_array( $data ) ||
514 561 ! isset( $data['baseUrl'] ) || ! ( is_string( $data['baseUrl'] ) || false === $data['baseUrl'] ) ||
515 562 ! isset( $data['locale'] ) || ! is_string( $data['locale'] ) ||
516 563 ! isset( $data['domainMap'] ) || ! is_array( $data['domainMap'] ) ||
@@ -515,19 +562,19 @@
515 562 ! isset( $data['locale'] ) || ! is_string( $data['locale'] ) ||
516 563 ! isset( $data['domainMap'] ) || ! is_array( $data['domainMap'] ) ||
517 564 ! isset( $data['domainPaths'] ) || ! is_array( $data['domainPaths'] )
518 565 ) {
519 - $wp_scripts->add_inline_script( 'wp-jp-i18n-loader', 'console.warn( "I18n state deleted by jetpack_i18n_state hook" );' );
566 + $wp_scripts->add_inline_script( $handle, 'console.warn( "I18n state deleted by jetpack_i18n_state hook" );' );
520 567 } elseif ( ! $data['baseUrl'] ) {
521 - $wp_scripts->add_inline_script( 'wp-jp-i18n-loader', 'console.warn( "Failed to determine languages base URL. Is WP_LANG_DIR in the WordPress root?" );' );
568 + $wp_scripts->add_inline_script( $handle, 'console.warn( "Failed to determine languages base URL. Is WP_LANG_DIR in the WordPress root?" );' );
522 569 } else {
523 570 $data['domainMap'] = (object) $data['domainMap']; // Ensure it becomes a json object.
524 571 $data['domainPaths'] = (object) $data['domainPaths']; // Ensure it becomes a json object.
525 - $wp_scripts->add_inline_script( 'wp-jp-i18n-loader', 'wp.jpI18nLoader.state = ' . wp_json_encode( $data, JSON_UNESCAPED_SLASHES ) . ';' );
572 + $wp_scripts->add_inline_script( $handle, 'wp.jpI18nLoader.state = ' . wp_json_encode( $data, JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ) . ';' );
526 573 }
527 574
528 575 // Deprecated state module: Depend on wp-i18n to ensure global `wp` exists and because anything needing this will need that too.
529 - $wp_scripts->add( 'wp-jp-i18n-state', false, array( 'wp-deprecated', 'wp-jp-i18n-loader' ) );
576 + $wp_scripts->add( 'wp-jp-i18n-state', false, array( 'wp-deprecated', $handle ) );
530 577 $wp_scripts->add_inline_script( 'wp-jp-i18n-state', 'wp.deprecated( "wp-jp-i18n-state", { alternative: "wp-jp-i18n-loader" } );' );
531 578 $wp_scripts->add_inline_script( 'wp-jp-i18n-state', 'wp.jpI18nState = wp.jpI18nLoader.state;' );
532 579 }
533 580
@@ -576,13 +623,30 @@
576 623 ''
577 624 );
578 625 }
579 626
627 + // Where the package lives is needed for JS translation files whether or
628 + // not its domain is aliased, so it is recorded before the self-alias
629 + // check below.
630 + if (
631 + empty( self::$domain_paths[ $from ] ) ||
632 + Semver::compare( $ver, self::$domain_paths[ $from ][0] ) > 0
633 + ) {
634 + self::$domain_paths[ $from ] = array( $ver, $path );
635 + }
636 +
637 + // A self-alias would make filter_gettext() re-translate into the same
638 + // domain, recursing infinitely on any untranslated string (a package
639 + // textdomain can collide with its containing plugin's slug).
640 + if ( $from === $to ) {
641 + return;
642 + }
643 +
580 644 if ( empty( self::$domain_map[ $from ] ) ) {
581 645 self::init_domain_map_hooks( $from, array() === self::$domain_map );
582 - self::$domain_map[ $from ] = array( $to, $totype, $ver, $path );
646 + self::$domain_map[ $from ] = array( $to, $totype, $ver );
583 647 } elseif ( Semver::compare( $ver, self::$domain_map[ $from ][2] ) > 0 ) {
584 - self::$domain_map[ $from ] = array( $to, $totype, $ver, $path );
648 + self::$domain_map[ $from ] = array( $to, $totype, $ver );
585 649 }
586 650 }
587 651
588 652 /**
@@ -592,8 +656,11 @@
592 656 * with the following properties:
593 657 * - 'domain': String, `$to`
594 658 * - 'type': String, `$totype`
595 659 * - 'packages': Array, mapping `$from` to `array( 'path' => $path, 'ver' => $ver )` (or to the string `$ver` for back compat).
660 + * - 'paths': Array, same shape, for packages whose textdomain is already
661 + * `$to`. Those must not be aliased — that would recurse — but their
662 + * paths are still needed to locate their JavaScript translations.
596 663 *
597 664 * @since 1.15.0
598 665 * @param string $file Mapping file.
599 666 */
@@ -606,8 +673,13 @@
606 673 'ver' => $fromdata,
607 674 );
608 675 }
609 676 self::alias_textdomain( $from, $data['domain'], $data['type'], $fromdata['ver'], $fromdata['path'] );
677 + }
678 + // Aliasing a domain to itself is a no-op that `alias_textdomain()`
679 + // declines, leaving just the path registration these entries are for.
680 + foreach ( $data['paths'] ?? array() as $from => $fromdata ) {
681 + self::alias_textdomain( $from, $from, $data['type'], $fromdata['ver'], $fromdata['path'] );
610 682 }
611 683 }
612 684
613 685 /**