From 2e11b0a7cfb4b2349ddc6396c3d5605f00892c1c Mon Sep 17 00:00:00 2001 From: Samiff Date: Tue, 14 Dec 2021 15:31:27 -0700 Subject: [PATCH 01/22] Banner: fix icon CSS margin offset (#22075) --- .../plugins/jetpack/_inc/client/components/banner/style.scss | 4 ---- .../plugins/jetpack/changelog/2021-12-10-22-17-18-511573 | 5 +++++ 2 files changed, 5 insertions(+), 4 deletions(-) create mode 100644 projects/plugins/jetpack/changelog/2021-12-10-22-17-18-511573 diff --git a/projects/plugins/jetpack/_inc/client/components/banner/style.scss b/projects/plugins/jetpack/_inc/client/components/banner/style.scss index 398b24aa5b7b8..edc38895129a7 100644 --- a/projects/plugins/jetpack/_inc/client/components/banner/style.scss +++ b/projects/plugins/jetpack/_inc/client/components/banner/style.scss @@ -104,10 +104,6 @@ color: white; display: none; padding: 3px 4px 4px 3px; - - .gridicon { - margin-bottom: -7px; - } } @include breakpoint( '>480px' ) { diff --git a/projects/plugins/jetpack/changelog/2021-12-10-22-17-18-511573 b/projects/plugins/jetpack/changelog/2021-12-10-22-17-18-511573 new file mode 100644 index 0000000000000..a11860ef02dbf --- /dev/null +++ b/projects/plugins/jetpack/changelog/2021-12-10-22-17-18-511573 @@ -0,0 +1,5 @@ +Significance: patch +Type: other +Comment: Banner: fix icon CSS margin offset. + + From dfc8ca3e7c993178c884c09c5ef42ffbc5382e7e Mon Sep 17 00:00:00 2001 From: Samiff Date: Tue, 14 Dec 2021 15:52:32 -0700 Subject: [PATCH 02/22] PHPCS: SEO Tools (#22072) * Remove seo-tools files frome excludelist * Run phpcs:fix [not verified] * Rename jetpack-seo-posts.php with "class-" * Manual phpcs fixes for class-jetpack-seo-posts.php * Rename jetpack-seo-titles.php with "class-" * Manual phpcs fixes for class-jetpack-seo-titles.php * Add changelog * Rename jetpack-seo-utils.php with "class-" * Manual phpcs fixes for class-jetpack-seo-utils.php * Rename jetpack-seo.php with "class-" * Fix array_key_exists param array_keys() not needed since array_key_exists() checks on keys already. * Manual phpcs fixes for class-jetpack-seo.php --- .../plugins/jetpack/changelog/phpcs-seo-tools | 5 ++ projects/plugins/jetpack/load-jetpack.php | 6 +- .../plugins/jetpack/modules/seo-tools.php | 4 +- ...-posts.php => class-jetpack-seo-posts.php} | 21 ++++--- ...itles.php => class-jetpack-seo-titles.php} | 18 ++++-- ...-utils.php => class-jetpack-seo-utils.php} | 5 ++ ...{jetpack-seo.php => class-jetpack-seo.php} | 61 +++++++++++++------ .../test-class.jetpack.seo-titles.php | 2 +- tools/phpcs-excludelist.json | 4 -- 9 files changed, 85 insertions(+), 41 deletions(-) create mode 100644 projects/plugins/jetpack/changelog/phpcs-seo-tools rename projects/plugins/jetpack/modules/seo-tools/{jetpack-seo-posts.php => class-jetpack-seo-posts.php} (81%) rename projects/plugins/jetpack/modules/seo-tools/{jetpack-seo-titles.php => class-jetpack-seo-titles.php} (95%) rename projects/plugins/jetpack/modules/seo-tools/{jetpack-seo-utils.php => class-jetpack-seo-utils.php} (97%) rename projects/plugins/jetpack/modules/seo-tools/{jetpack-seo.php => class-jetpack-seo.php} (77%) diff --git a/projects/plugins/jetpack/changelog/phpcs-seo-tools b/projects/plugins/jetpack/changelog/phpcs-seo-tools new file mode 100644 index 0000000000000..101cc6f0633eb --- /dev/null +++ b/projects/plugins/jetpack/changelog/phpcs-seo-tools @@ -0,0 +1,5 @@ +Significance: patch +Type: other +Comment: PHPCS: SEO Tools + + diff --git a/projects/plugins/jetpack/load-jetpack.php b/projects/plugins/jetpack/load-jetpack.php index cb6407a5ae89c..fa7ba5af84fe1 100644 --- a/projects/plugins/jetpack/load-jetpack.php +++ b/projects/plugins/jetpack/load-jetpack.php @@ -57,9 +57,9 @@ function jetpack_should_use_minified_assets() { require_once JETPACK__PLUGIN_DIR . 'class.jetpack-connection-banner.php'; require_once JETPACK__PLUGIN_DIR . 'class.jetpack-plan.php'; // Used by the API endpoints. -require_once JETPACK__PLUGIN_DIR . 'modules/seo-tools/jetpack-seo-utils.php'; -require_once JETPACK__PLUGIN_DIR . 'modules/seo-tools/jetpack-seo-titles.php'; -require_once JETPACK__PLUGIN_DIR . 'modules/seo-tools/jetpack-seo-posts.php'; +require_once JETPACK__PLUGIN_DIR . 'modules/seo-tools/class-jetpack-seo-utils.php'; +require_once JETPACK__PLUGIN_DIR . 'modules/seo-tools/class-jetpack-seo-titles.php'; +require_once JETPACK__PLUGIN_DIR . 'modules/seo-tools/class-jetpack-seo-posts.php'; require_once JETPACK__PLUGIN_DIR . 'modules/verification-tools/verification-tools-utils.php'; require_once JETPACK__PLUGIN_DIR . 'class-jetpack-xmlrpc-methods.php'; diff --git a/projects/plugins/jetpack/modules/seo-tools.php b/projects/plugins/jetpack/modules/seo-tools.php index 1553e65c99471..ed025f3f70173 100644 --- a/projects/plugins/jetpack/modules/seo-tools.php +++ b/projects/plugins/jetpack/modules/seo-tools.php @@ -39,8 +39,8 @@ } } -/** This filter is documented in modules/seo-tools/jetpack-seo-utils.php */ +/** This filter is documented in modules/seo-tools/class-jetpack-seo-utils.php */ if ( ! apply_filters( 'jetpack_disable_seo_tools', false ) ) { - require_once __DIR__ . '/seo-tools/jetpack-seo.php'; + require_once __DIR__ . '/seo-tools/class-jetpack-seo.php'; new Jetpack_SEO(); } diff --git a/projects/plugins/jetpack/modules/seo-tools/jetpack-seo-posts.php b/projects/plugins/jetpack/modules/seo-tools/class-jetpack-seo-posts.php similarity index 81% rename from projects/plugins/jetpack/modules/seo-tools/jetpack-seo-posts.php rename to projects/plugins/jetpack/modules/seo-tools/class-jetpack-seo-posts.php index 82c9b0a582285..140bc5ef52f95 100644 --- a/projects/plugins/jetpack/modules/seo-tools/jetpack-seo-posts.php +++ b/projects/plugins/jetpack/modules/seo-tools/class-jetpack-seo-posts.php @@ -1,7 +1,12 @@ 'string', - 'description' => __( 'Custom post description to be used in HTML tag.', 'jetpack' ), - 'single' => true, - 'default' => '', + 'type' => 'string', + 'description' => __( 'Custom post description to be used in HTML tag.', 'jetpack' ), + 'single' => true, + 'default' => '', 'show_in_rest' => array( - 'name' => self::DESCRIPTION_META_KEY + 'name' => self::DESCRIPTION_META_KEY, ), ); diff --git a/projects/plugins/jetpack/modules/seo-tools/jetpack-seo-titles.php b/projects/plugins/jetpack/modules/seo-tools/class-jetpack-seo-titles.php similarity index 95% rename from projects/plugins/jetpack/modules/seo-tools/jetpack-seo-titles.php rename to projects/plugins/jetpack/modules/seo-tools/class-jetpack-seo-titles.php index f7360eaf946d1..82564ff9674c4 100644 --- a/projects/plugins/jetpack/modules/seo-tools/jetpack-seo-titles.php +++ b/projects/plugins/jetpack/modules/seo-tools/class-jetpack-seo-titles.php @@ -1,4 +1,9 @@ $format_array ) { - if ( ! in_array( $format_type, array_keys( $allowed_tokens ) ) ) { + if ( ! array_key_exists( $format_type, $allowed_tokens ) ) { return false; } @@ -265,8 +271,8 @@ public static function are_valid_title_formats( $title_formats ) { return false; } - if ( 'token' == $item['type'] ) { - if ( ! in_array( $item['value'], $allowed_tokens[ $format_type ] ) ) { + if ( 'token' === $item['type'] ) { + if ( ! in_array( $item['value'], $allowed_tokens[ $format_type ], true ) ) { return false; } } diff --git a/projects/plugins/jetpack/modules/seo-tools/jetpack-seo-utils.php b/projects/plugins/jetpack/modules/seo-tools/class-jetpack-seo-utils.php similarity index 97% rename from projects/plugins/jetpack/modules/seo-tools/jetpack-seo-utils.php rename to projects/plugins/jetpack/modules/seo-tools/class-jetpack-seo-utils.php index 96277a09cae94..c6d8c2943f0ff 100644 --- a/projects/plugins/jetpack/modules/seo-tools/jetpack-seo-utils.php +++ b/projects/plugins/jetpack/modules/seo-tools/class-jetpack-seo-utils.php @@ -1,4 +1,9 @@ tags. + */ public function meta_tags() { global $wp_query; @@ -148,6 +171,7 @@ public function meta_tags() { $authors = $this->get_authors(); $meta['description'] = wp_sprintf( + /* translators: %1$s: A post category. %2$l: Post authors. */ _x( 'Posts about %1$s written by %2$l', 'Posts about Category written by John and Bob', 'jetpack' ), single_term_title( '', false ), $authors @@ -155,36 +179,39 @@ public function meta_tags() { } } elseif ( is_date() ) { if ( is_year() ) { - $period = get_query_var( 'year' ); + $period = get_query_var( 'year' ); + /* translators: %1$s: Number of posts published. %2$l: Post author. %3$s: A year date. */ $template = _nx( - '%1$s post published by %2$l in the year %3$s', // singular - '%1$s posts published by %2$l in the year %3$s', // plural - count( $wp_query->posts ), // number - '10 posts published by John in the year 2012', // context + '%1$s post published by %2$l in the year %3$s', // Singular. + '%1$s posts published by %2$l in the year %3$s', // Plural. + count( $wp_query->posts ), // Number. + '10 posts published by John in the year 2012', // Context. 'jetpack' ); } elseif ( is_month() ) { - $period = date( 'F Y', mktime( 0, 0, 0, get_query_var( 'monthnum' ), 1, get_query_var( 'year' ) ) ); + $period = gmdate( 'F Y', mktime( 0, 0, 0, get_query_var( 'monthnum' ), 1, get_query_var( 'year' ) ) ); + /* translators: %1$s: Number of posts published. %2$l: Post author. %3$s: A month/year date. */ $template = _nx( - '%1$s post published by %2$l during %3$s', // singular - '%1$s posts published by %2$l during %3$s', // plural - count( $wp_query->posts ), // number - '10 posts publishes by John during May 2012', // context + '%1$s post published by %2$l during %3$s', // Singular. + '%1$s posts published by %2$l during %3$s', // Plural. + count( $wp_query->posts ), // Number. + '10 posts publishes by John during May 2012', // Context. 'jetpack' ); } elseif ( is_day() ) { - $period = date( + $period = gmdate( 'F j, Y', mktime( 0, 0, 0, get_query_var( 'monthnum' ), get_query_var( 'day' ), get_query_var( 'year' ) ) ); + /* translators: %1$s: Number of posts published. %2$l: Post author. %3$s: A month/day/year date. */ $template = _nx( - '%1$s post published by %2$l on %3$s', // singular - '%1$s posts published by %2$l on %3$s', // plural - count( $wp_query->posts ), // number - '10 posts published by John on May 30, 2012', // context + '%1$s post published by %2$l on %3$s', // Singular. + '%1$s posts published by %2$l on %3$s', // Plural. + count( $wp_query->posts ), // Number. + '10 posts published by John on May 30, 2012', // Context. 'jetpack' ); } @@ -204,7 +231,7 @@ public function meta_tags() { */ $meta = apply_filters( 'jetpack_seo_meta_tags', $meta ); - // Output them + // Output them. foreach ( $meta as $name => $content ) { if ( ! empty( $content ) ) { echo '' . "\n"; diff --git a/projects/plugins/jetpack/tests/php/modules/seo-tools/test-class.jetpack.seo-titles.php b/projects/plugins/jetpack/tests/php/modules/seo-tools/test-class.jetpack.seo-titles.php index 6500dfa0982c7..4696cb5db5bde 100644 --- a/projects/plugins/jetpack/tests/php/modules/seo-tools/test-class.jetpack.seo-titles.php +++ b/projects/plugins/jetpack/tests/php/modules/seo-tools/test-class.jetpack.seo-titles.php @@ -5,7 +5,7 @@ * @package automattic/jetpack */ -require_jetpack_file( 'modules/seo-tools/jetpack-seo-titles.php' ); +require_jetpack_file( 'modules/seo-tools/class-jetpack-seo-titles.php' ); /** * Class WP_Test_Jetpack_SEO_Titles diff --git a/tools/phpcs-excludelist.json b/tools/phpcs-excludelist.json index 1d517d8c9b4c1..7a308172ed5ee 100644 --- a/tools/phpcs-excludelist.json +++ b/tools/phpcs-excludelist.json @@ -254,10 +254,6 @@ "projects/plugins/jetpack/modules/related-posts.php", "projects/plugins/jetpack/modules/related-posts/class.related-posts-customize.php", "projects/plugins/jetpack/modules/related-posts/jetpack-related-posts.php", - "projects/plugins/jetpack/modules/seo-tools/jetpack-seo-posts.php", - "projects/plugins/jetpack/modules/seo-tools/jetpack-seo-titles.php", - "projects/plugins/jetpack/modules/seo-tools/jetpack-seo-utils.php", - "projects/plugins/jetpack/modules/seo-tools/jetpack-seo.php", "projects/plugins/jetpack/modules/sharedaddy/recaptcha.php", "projects/plugins/jetpack/modules/sharedaddy/sharedaddy.php", "projects/plugins/jetpack/modules/sharedaddy/sharing-service.php", From 7de7ab7d4d50b4428fcd2b563bc7dba326b6d803 Mon Sep 17 00:00:00 2001 From: Richard Ortiz Date: Wed, 15 Dec 2021 13:09:56 +0100 Subject: [PATCH 03/22] Fixed admin menu icons corner cases. (#22070) * Fixed admin menu icons corner cases. We now identify admin menu dashicons that won't be rendered in Calypso, and provide a default icon for them. * IImproved performance by avoiding loading the core dashicon set for every admin menu item. Removed an unnecessary call to sanitize_html_class. * We now check that the dashicon's set value is true when matching items of thee set in order to avoid confusion. Co-authored-by: mmtr Co-authored-by: Bogdan Ungureanu --- ...-wpcom-rest-api-v2-endpoint-admin-menu.php | 34 +- .../fix-admin-menu-icons-plugin-edge-cases | 4 + .../masterbar/admin-menu/dashicon-set.php | 358 ++++++++++++++++++ ...-wpcom-rest-api-v2-endpoint-admin-menu.php | 5 + 4 files changed, 400 insertions(+), 1 deletion(-) create mode 100644 projects/plugins/jetpack/changelog/fix-admin-menu-icons-plugin-edge-cases create mode 100644 projects/plugins/jetpack/modules/masterbar/admin-menu/dashicon-set.php diff --git a/projects/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-admin-menu.php b/projects/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-admin-menu.php index 2a5bfce8a3af8..2c386ad814e9a 100644 --- a/projects/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-admin-menu.php +++ b/projects/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-admin-menu.php @@ -25,6 +25,14 @@ class WPCOM_REST_API_V2_Endpoint_Admin_Menu extends WP_REST_Controller { */ public $rest_base = 'admin-menu'; + /** + * + * Set of core dashicons. + * + * @var array + */ + private $dashicon_list; + /** * WPCOM_REST_API_V2_Endpoint_Admin_Menu constructor. */ @@ -322,13 +330,37 @@ private function prepare_menu_item_icon( $icon ) { if ( 0 === strpos( $icon, 'data:image/svg+xml' ) ) { $img = $icon; } elseif ( 0 === strpos( $icon, 'dashicons-' ) ) { - $img = sanitize_html_class( $icon ); + $img = $this->prepare_dashicon( $icon ); } } return $img; } + /** + * Prepares the dashicon for consumption by Calypso. If the dashicon isn't found in a list of known icons + * we will return the default dashicon. + * + * @param string $icon The dashicon string to check. + * + * @return string If the dashicon exists in core we return the dashicon, otherwise we return the default dashicon. + */ + private function prepare_dashicon( $icon ) { + if ( empty( $this->dashicon_set ) ) { + if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) { + $this->dashicon_list = include WP_CONTENT_DIR . '/mu-plugins/masterbar/admin-menu/dashicon-set.php'; + } else { + $this->dashicon_list = include JETPACK__PLUGIN_DIR . '/modules/masterbar/admin-menu/dashicon-set.php'; + } + } + + if ( isset( $this->dashicon_list[ $icon ] ) && $this->dashicon_list[ $icon ] ) { + return $icon; + } + + return 'dashicons-admin-generic'; + } + /** * Prepares a menu item url for consumption by Calypso. * diff --git a/projects/plugins/jetpack/changelog/fix-admin-menu-icons-plugin-edge-cases b/projects/plugins/jetpack/changelog/fix-admin-menu-icons-plugin-edge-cases new file mode 100644 index 0000000000000..0915e7bbd2ce6 --- /dev/null +++ b/projects/plugins/jetpack/changelog/fix-admin-menu-icons-plugin-edge-cases @@ -0,0 +1,4 @@ +Significance: patch +Type: bugfix + +We now identify admin menu dashicons that won't be rendered in Calypso, and provide a default icon for them. diff --git a/projects/plugins/jetpack/modules/masterbar/admin-menu/dashicon-set.php b/projects/plugins/jetpack/modules/masterbar/admin-menu/dashicon-set.php new file mode 100644 index 0000000000000..41695b060bc9b --- /dev/null +++ b/projects/plugins/jetpack/modules/masterbar/admin-menu/dashicon-set.php @@ -0,0 +1,358 @@ + true, + 'dashicons-admin-collapse' => true, + 'dashicons-admin-comments' => true, + 'dashicons-admin-customizer' => true, + 'dashicons-admin-generic' => true, + 'dashicons-admin-home' => true, + 'dashicons-admin-links' => true, + 'dashicons-admin-media' => true, + 'dashicons-admin-multisite' => true, + 'dashicons-admin-network' => true, + 'dashicons-admin-page' => true, + 'dashicons-admin-plugins' => true, + 'dashicons-admin-post' => true, + 'dashicons-admin-settings' => true, + 'dashicons-admin-site-alt' => true, + 'dashicons-admin-site-alt2' => true, + 'dashicons-admin-site-alt3' => true, + 'dashicons-admin-site' => true, + 'dashicons-admin-tools' => true, + 'dashicons-admin-users' => true, + 'dashicons-airplane' => true, + 'dashicons-album' => true, + 'dashicons-align-center' => true, + 'dashicons-align-full-width' => true, + 'dashicons-align-left' => true, + 'dashicons-align-none' => true, + 'dashicons-align-pull-left' => true, + 'dashicons-align-pull-right' => true, + 'dashicons-align-right' => true, + 'dashicons-align-wide' => true, + 'dashicons-amazon' => true, + 'dashicons-analytics' => true, + 'dashicons-archive' => true, + 'dashicons-arrow-down-alt' => true, + 'dashicons-arrow-down-alt2' => true, + 'dashicons-arrow-down' => true, + 'dashicons-arrow-left-alt' => true, + 'dashicons-arrow-left-alt2' => true, + 'dashicons-arrow-left' => true, + 'dashicons-arrow-right-alt' => true, + 'dashicons-arrow-right-alt2' => true, + 'dashicons-arrow-right' => true, + 'dashicons-arrow-up-alt' => true, + 'dashicons-arrow-up-alt2' => true, + 'dashicons-arrow-up-duplicate' => true, + 'dashicons-arrow-up' => true, + 'dashicons-art' => true, + 'dashicons-awards' => true, + 'dashicons-backup' => true, + 'dashicons-bank' => true, + 'dashicons-beer' => true, + 'dashicons-bell' => true, + 'dashicons-block-default' => true, + 'dashicons-book-alt' => true, + 'dashicons-book' => true, + 'dashicons-buddicons-activity' => true, + 'dashicons-buddicons-bbpress-logo' => true, + 'dashicons-buddicons-buddypress-logo' => true, + 'dashicons-buddicons-community' => true, + 'dashicons-buddicons-forums' => true, + 'dashicons-buddicons-friends' => true, + 'dashicons-buddicons-groups' => true, + 'dashicons-buddicons-pm' => true, + 'dashicons-buddicons-replies' => true, + 'dashicons-buddicons-topics' => true, + 'dashicons-buddicons-tracking' => true, + 'dashicons-building' => true, + 'dashicons-businessman' => true, + 'dashicons-businessperson' => true, + 'dashicons-businesswoman' => true, + 'dashicons-button' => true, + 'dashicons-calculator' => true, + 'dashicons-calendar-alt' => true, + 'dashicons-calendar' => true, + 'dashicons-camera-alt' => true, + 'dashicons-camera' => true, + 'dashicons-car' => true, + 'dashicons-carrot' => true, + 'dashicons-cart' => true, + 'dashicons-category' => true, + 'dashicons-chart-area' => true, + 'dashicons-chart-bar' => true, + 'dashicons-chart-line' => true, + 'dashicons-chart-pie' => true, + 'dashicons-clipboard' => true, + 'dashicons-clock' => true, + 'dashicons-cloud-saved' => true, + 'dashicons-cloud-upload' => true, + 'dashicons-cloud' => true, + 'dashicons-code-standards' => true, + 'dashicons-coffee' => true, + 'dashicons-color-picker' => true, + 'dashicons-columns' => true, + 'dashicons-controls-back' => true, + 'dashicons-controls-forward' => true, + 'dashicons-controls-pause' => true, + 'dashicons-controls-play' => true, + 'dashicons-controls-repeat' => true, + 'dashicons-controls-skipback' => true, + 'dashicons-controls-skipforward' => true, + 'dashicons-controls-volumeoff' => true, + 'dashicons-controls-volumeon' => true, + 'dashicons-cover-image' => true, + 'dashicons-dashboard' => true, + 'dashicons-database-add' => true, + 'dashicons-database-export' => true, + 'dashicons-database-import' => true, + 'dashicons-database-remove' => true, + 'dashicons-database-view' => true, + 'dashicons-database' => true, + 'dashicons-desktop' => true, + 'dashicons-dismiss' => true, + 'dashicons-download' => true, + 'dashicons-drumstick' => true, + 'dashicons-edit-large' => true, + 'dashicons-edit-page' => true, + 'dashicons-edit' => true, + 'dashicons-editor-aligncenter' => true, + 'dashicons-editor-alignleft' => true, + 'dashicons-editor-alignright' => true, + 'dashicons-editor-bold' => true, + 'dashicons-editor-break' => true, + 'dashicons-editor-code-duplicate' => true, + 'dashicons-editor-code' => true, + 'dashicons-editor-contract' => true, + 'dashicons-editor-customchar' => true, + 'dashicons-editor-expand' => true, + 'dashicons-editor-help' => true, + 'dashicons-editor-indent' => true, + 'dashicons-editor-insertmore' => true, + 'dashicons-editor-italic' => true, + 'dashicons-editor-justify' => true, + 'dashicons-editor-kitchensink' => true, + 'dashicons-editor-ltr' => true, + 'dashicons-editor-ol-rtl' => true, + 'dashicons-editor-ol' => true, + 'dashicons-editor-outdent' => true, + 'dashicons-editor-paragraph' => true, + 'dashicons-editor-paste-text' => true, + 'dashicons-editor-paste-word' => true, + 'dashicons-editor-quote' => true, + 'dashicons-editor-removeformatting' => true, + 'dashicons-editor-rtl' => true, + 'dashicons-editor-spellcheck' => true, + 'dashicons-editor-strikethrough' => true, + 'dashicons-editor-table' => true, + 'dashicons-editor-textcolor' => true, + 'dashicons-editor-ul' => true, + 'dashicons-editor-underline' => true, + 'dashicons-editor-unlink' => true, + 'dashicons-editor-video' => true, + 'dashicons-ellipsis' => true, + 'dashicons-email-alt' => true, + 'dashicons-email-alt2' => true, + 'dashicons-email' => true, + 'dashicons-embed-audio' => true, + 'dashicons-embed-generic' => true, + 'dashicons-embed-photo' => true, + 'dashicons-embed-post' => true, + 'dashicons-embed-video' => true, + 'dashicons-excerpt-view' => true, + 'dashicons-exit' => true, + 'dashicons-external' => true, + 'dashicons-facebook-alt' => true, + 'dashicons-facebook' => true, + 'dashicons-feedback' => true, + 'dashicons-filter' => true, + 'dashicons-flag' => true, + 'dashicons-food' => true, + 'dashicons-format-aside' => true, + 'dashicons-format-audio' => true, + 'dashicons-format-chat' => true, + 'dashicons-format-gallery' => true, + 'dashicons-format-image' => true, + 'dashicons-format-quote' => true, + 'dashicons-format-status' => true, + 'dashicons-format-video' => true, + 'dashicons-forms' => true, + 'dashicons-fullscreen-alt' => true, + 'dashicons-fullscreen-exit-alt' => true, + 'dashicons-games' => true, + 'dashicons-google' => true, + 'dashicons-googleplus' => true, + 'dashicons-grid-view' => true, + 'dashicons-groups' => true, + 'dashicons-hammer' => true, + 'dashicons-heading' => true, + 'dashicons-heart' => true, + 'dashicons-hidden' => true, + 'dashicons-hourglass' => true, + 'dashicons-html' => true, + 'dashicons-id-alt' => true, + 'dashicons-id' => true, + 'dashicons-image-crop' => true, + 'dashicons-image-filter' => true, + 'dashicons-image-flip-horizontal' => true, + 'dashicons-image-flip-vertical' => true, + 'dashicons-image-rotate-left' => true, + 'dashicons-image-rotate-right' => true, + 'dashicons-image-rotate' => true, + 'dashicons-images-alt' => true, + 'dashicons-images-alt2' => true, + 'dashicons-index-card' => true, + 'dashicons-info-outline' => true, + 'dashicons-info' => true, + 'dashicons-insert-after' => true, + 'dashicons-insert-before' => true, + 'dashicons-insert' => true, + 'dashicons-instagram' => true, + 'dashicons-laptop' => true, + 'dashicons-layout' => true, + 'dashicons-leftright' => true, + 'dashicons-lightbulb' => true, + 'dashicons-linkedin' => true, + 'dashicons-list-view' => true, + 'dashicons-location-alt' => true, + 'dashicons-location' => true, + 'dashicons-lock-duplicate' => true, + 'dashicons-lock' => true, + 'dashicons-marker' => true, + 'dashicons-media-archive' => true, + 'dashicons-media-audio' => true, + 'dashicons-media-code' => true, + 'dashicons-media-default' => true, + 'dashicons-media-document' => true, + 'dashicons-media-interactive' => true, + 'dashicons-media-spreadsheet' => true, + 'dashicons-media-text' => true, + 'dashicons-media-video' => true, + 'dashicons-megaphone' => true, + 'dashicons-menu-alt' => true, + 'dashicons-menu-alt2' => true, + 'dashicons-menu-alt3' => true, + 'dashicons-menu' => true, + 'dashicons-microphone' => true, + 'dashicons-migrate' => true, + 'dashicons-minus' => true, + 'dashicons-money-alt' => true, + 'dashicons-money' => true, + 'dashicons-move' => true, + 'dashicons-nametag' => true, + 'dashicons-networking' => true, + 'dashicons-no-alt' => true, + 'dashicons-no' => true, + 'dashicons-open-folder' => true, + 'dashicons-palmtree' => true, + 'dashicons-paperclip' => true, + 'dashicons-pdf' => true, + 'dashicons-performance' => true, + 'dashicons-pets' => true, + 'dashicons-phone' => true, + 'dashicons-pinterest' => true, + 'dashicons-playlist-audio' => true, + 'dashicons-playlist-video' => true, + 'dashicons-plugins-checked' => true, + 'dashicons-plus-alt' => true, + 'dashicons-plus-alt2' => true, + 'dashicons-plus' => true, + 'dashicons-podio' => true, + 'dashicons-portfolio' => true, + 'dashicons-post-status' => true, + 'dashicons-pressthis' => true, + 'dashicons-printer' => true, + 'dashicons-privacy' => true, + 'dashicons-products' => true, + 'dashicons-randomize' => true, + 'dashicons-reddit' => true, + 'dashicons-redo' => true, + 'dashicons-remove' => true, + 'dashicons-rest-api' => true, + 'dashicons-rss' => true, + 'dashicons-saved' => true, + 'dashicons-schedule' => true, + 'dashicons-screenoptions' => true, + 'dashicons-search' => true, + 'dashicons-share-alt' => true, + 'dashicons-share-alt2' => true, + 'dashicons-share' => true, + 'dashicons-shield-alt' => true, + 'dashicons-shield' => true, + 'dashicons-shortcode' => true, + 'dashicons-slides' => true, + 'dashicons-smartphone' => true, + 'dashicons-smiley' => true, + 'dashicons-sort' => true, + 'dashicons-sos' => true, + 'dashicons-spotify' => true, + 'dashicons-star-empty' => true, + 'dashicons-star-filled' => true, + 'dashicons-star-half' => true, + 'dashicons-sticky' => true, + 'dashicons-store' => true, + 'dashicons-superhero-alt' => true, + 'dashicons-superhero' => true, + 'dashicons-table-col-after' => true, + 'dashicons-table-col-before' => true, + 'dashicons-table-col-delete' => true, + 'dashicons-table-row-after' => true, + 'dashicons-table-row-before' => true, + 'dashicons-table-row-delete' => true, + 'dashicons-tablet' => true, + 'dashicons-tag' => true, + 'dashicons-tagcloud' => true, + 'dashicons-testimonial' => true, + 'dashicons-text-page' => true, + 'dashicons-text' => true, + 'dashicons-thumbs-down' => true, + 'dashicons-thumbs-up' => true, + 'dashicons-tickets-alt' => true, + 'dashicons-tickets' => true, + 'dashicons-tide' => true, + 'dashicons-translation' => true, + 'dashicons-trash' => true, + 'dashicons-twitch' => true, + 'dashicons-twitter-alt' => true, + 'dashicons-twitter' => true, + 'dashicons-undo' => true, + 'dashicons-universal-access-alt' => true, + 'dashicons-universal-access' => true, + 'dashicons-unlock' => true, + 'dashicons-update-alt' => true, + 'dashicons-update' => true, + 'dashicons-upload' => true, + 'dashicons-vault' => true, + 'dashicons-video-alt' => true, + 'dashicons-video-alt2' => true, + 'dashicons-video-alt3' => true, + 'dashicons-visibility' => true, + 'dashicons-warning' => true, + 'dashicons-welcome-add-page' => true, + 'dashicons-welcome-comments' => true, + 'dashicons-welcome-learn-more' => true, + 'dashicons-welcome-view-site' => true, + 'dashicons-welcome-widgets-menus' => true, + 'dashicons-welcome-write-blog' => true, + 'dashicons-whatsapp' => true, + 'dashicons-wordpress-alt' => true, + 'dashicons-wordpress' => true, + 'dashicons-xing' => true, + 'dashicons-yes-alt' => true, + 'dashicons-yes' => true, + 'dashicons-youtube' => true, + 'dashicons-editor-distractionfree' => true, + 'dashicons-exerpt-view' => true, + 'dashicons-format-links' => true, + 'dashicons-format-standard' => true, + 'dashicons-post-trash' => true, + 'dashicons-share1' => true, + 'dashicons-welcome-edit-page' => true, +); diff --git a/projects/plugins/jetpack/tests/php/core-api/wpcom-endpoints/test-class-wpcom-rest-api-v2-endpoint-admin-menu.php b/projects/plugins/jetpack/tests/php/core-api/wpcom-endpoints/test-class-wpcom-rest-api-v2-endpoint-admin-menu.php index 01e353ccd6d95..12ff3e7d1e3b0 100644 --- a/projects/plugins/jetpack/tests/php/core-api/wpcom-endpoints/test-class-wpcom-rest-api-v2-endpoint-admin-menu.php +++ b/projects/plugins/jetpack/tests/php/core-api/wpcom-endpoints/test-class-wpcom-rest-api-v2-endpoint-admin-menu.php @@ -349,6 +349,7 @@ public function test_if_the_first_submenu_url_is_used_for_menu_url() { * @throws \ReflectionException Noop. * @dataProvider menu_item_icon_data * @covers ::prepare_menu_item_icon + * @covers ::prepare_dashicon */ public function test_prepare_menu_item_icon( $icon, $expected ) { $class = new ReflectionClass( 'WPCOM_REST_API_V2_Endpoint_Admin_Menu' ); @@ -394,6 +395,10 @@ public function menu_item_icon_data() { 'dashicons-admin-media', 'dashicons-admin-media', ), + 'When the dashicon does not exist in the core dashicon list, we expect the default dashicon.' => array( + 'dashicons-admin-nope', + 'dashicons-admin-generic', + ), // SVG. array( 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBmaWxsPSJub25lIiBkPSJNMTMgNy41aDV2MmgtNXptMCA3aDV2MmgtNXpNMTkgM0g1Yy0xLjEgMC0yIC45LTIgMnYxNGMwIDEuMS45IDIgMiAyaDE0YzEuMSAwIDItLjkgMi0yVjVjMC0xLjEtLjktMi0yLTJ6bTAgMTZINVY1aDE0djE0ek0xMSA2SDZ2NWg1VjZ6bS0xIDRIN1Y3aDN2M3ptMSAzSDZ2NWg1di01em0tMSA0SDd2LTNoM3YzeiIvPjwvc3ZnPg==', From 55fa5548c32e4397ea97c68535b3d7ea27b67257 Mon Sep 17 00:00:00 2001 From: Brad Jorsch Date: Wed, 15 Dec 2021 08:41:49 -0500 Subject: [PATCH 04/22] Assets: Add textdomain aliasing (#22083) This adds the ability to register textdomain aliases with the Assets package. Assets then hooks into `__()` and so on to have them check the aliased-to domain if no translation is found for the aliased-from domain. It also hooks into script translation file loading to load a translation file for the aliased-to domain if no file exists for the aliased-from domain. This brings us one step closer to fixing #21690. Followups will switch all the packages to use alias-able textdomains (instead of them all using "jetpack") and arrange for the aliases to actually be registered. --- .../changelog/add-assets-textdomain-aliasing | 4 + projects/packages/assets/composer.json | 5 +- projects/packages/assets/src/class-assets.php | 270 ++++++++++++++++-- projects/packages/assets/src/class-semver.php | 122 ++++++++ .../packages/assets/tests/php/patchwork.json | 2 +- .../tests/php/test-assets-files/i18n-map.php | 9 + .../packages/assets/tests/php/test-assets.php | 234 +++++++++++++-- .../packages/assets/tests/php/test-semver.php | 216 ++++++++++++++ .../changelog/add-assets-textdomain-aliasing | 4 + projects/packages/connection-ui/composer.json | 2 +- projects/packages/connection-ui/package.json | 2 +- .../changelog/add-assets-textdomain-aliasing | 4 + projects/packages/jitm/composer.json | 2 +- projects/packages/jitm/src/class-jitm.php | 2 +- .../changelog/add-assets-textdomain-aliasing | 4 + projects/packages/lazy-images/composer.json | 2 +- .../changelog/add-assets-textdomain-aliasing | 4 + projects/packages/my-jetpack/composer.json | 2 +- projects/packages/my-jetpack/package.json | 2 +- .../changelog/add-assets-textdomain-aliasing | 4 + projects/packages/post-list/composer.json | 2 +- .../changelog/add-assets-textdomain-aliasing | 4 + projects/packages/tracking/composer.json | 2 +- .../changelog/add-assets-textdomain-aliasing | 5 + projects/plugins/backup/composer.json | 2 +- projects/plugins/backup/composer.lock | 19 +- .../changelog/add-assets-textdomain-aliasing | 5 + projects/plugins/boost/composer.lock | 13 +- .../changelog/add-assets-textdomain-aliasing | 5 + projects/plugins/jetpack/composer.json | 2 +- projects/plugins/jetpack/composer.lock | 27 +- 31 files changed, 905 insertions(+), 77 deletions(-) create mode 100644 projects/packages/assets/changelog/add-assets-textdomain-aliasing create mode 100644 projects/packages/assets/src/class-semver.php create mode 100644 projects/packages/assets/tests/php/test-assets-files/i18n-map.php create mode 100644 projects/packages/assets/tests/php/test-semver.php create mode 100644 projects/packages/connection-ui/changelog/add-assets-textdomain-aliasing create mode 100644 projects/packages/jitm/changelog/add-assets-textdomain-aliasing create mode 100644 projects/packages/lazy-images/changelog/add-assets-textdomain-aliasing create mode 100644 projects/packages/my-jetpack/changelog/add-assets-textdomain-aliasing create mode 100644 projects/packages/post-list/changelog/add-assets-textdomain-aliasing create mode 100644 projects/packages/tracking/changelog/add-assets-textdomain-aliasing create mode 100644 projects/plugins/backup/changelog/add-assets-textdomain-aliasing create mode 100644 projects/plugins/boost/changelog/add-assets-textdomain-aliasing create mode 100644 projects/plugins/jetpack/changelog/add-assets-textdomain-aliasing diff --git a/projects/packages/assets/changelog/add-assets-textdomain-aliasing b/projects/packages/assets/changelog/add-assets-textdomain-aliasing new file mode 100644 index 0000000000000..daa38960ab441 --- /dev/null +++ b/projects/packages/assets/changelog/add-assets-textdomain-aliasing @@ -0,0 +1,4 @@ +Significance: minor +Type: added + +Add `alias_textdomain()`. diff --git a/projects/packages/assets/composer.json b/projects/packages/assets/composer.json index c7674538777aa..1d6bdcefe3b91 100644 --- a/projects/packages/assets/composer.json +++ b/projects/packages/assets/composer.json @@ -9,7 +9,8 @@ "require-dev": { "brain/monkey": "2.6.1", "yoast/phpunit-polyfills": "1.0.2", - "automattic/jetpack-changelogger": "^3.0" + "automattic/jetpack-changelogger": "^3.0", + "wikimedia/testing-access-wrapper": "^1.0 | ^2.0" }, "autoload": { "files": [ @@ -50,7 +51,7 @@ "link-template": "https://github.com/Automattic/jetpack-assets/compare/v${old}...v${new}" }, "branch-alias": { - "dev-master": "1.14.x-dev" + "dev-master": "1.15.x-dev" } } } diff --git a/projects/packages/assets/src/class-assets.php b/projects/packages/assets/src/class-assets.php index a4b2af7a7ee0b..ce529b165a692 100644 --- a/projects/packages/assets/src/class-assets.php +++ b/projects/packages/assets/src/class-assets.php @@ -7,7 +7,9 @@ namespace Automattic\Jetpack; +use Automattic\Jetpack\Assets\Semver; use Automattic\Jetpack\Constants as Jetpack_Constants; +use InvalidArgumentException; /** * Class Assets @@ -19,6 +21,7 @@ class Assets { * @var array */ private $defer_script_handles = array(); + /** * The singleton instance of this class. * @@ -26,6 +29,13 @@ class Assets { */ protected static $instance; + /** + * The registered textdomain mappings. + * + * @var array `array( mapped_domain => array( string target_domain, string target_type, string semver ) )`. + */ + private static $domain_map = array(); + /** * Constructor. * @@ -33,6 +43,9 @@ class Assets { */ private function __construct() {} + // //////////////////// + // region Async script loading + /** * Get the singleton instance of the class. * @@ -85,6 +98,27 @@ public function script_add_async( $tag, $handle ) { return $tag; } + /** + * A helper function that lets you enqueue scripts in an async fashion. + * + * @param string $handle Name of the script. Should be unique. + * @param string $min_path Minimized script path. + * @param string $non_min_path Full Script path. + * @param array $deps Array of script dependencies. + * @param bool $ver The script version. + * @param bool $in_footer Should the script be included in the footer. + */ + public static function enqueue_async_script( $handle, $min_path, $non_min_path, $deps = array(), $ver = false, $in_footer = true ) { + $assets_instance = self::instance(); + $assets_instance->add_async_script( $handle ); + wp_enqueue_script( $handle, self::get_file_url_for_environment( $min_path, $non_min_path ), $deps, $ver, $in_footer ); + } + + // endregion . + + // //////////////////// + // region Utils + /** * Given a minified path, and a non-minified path, will return * a minified or non-minified file URL based on whether SCRIPT_DEBUG is set and truthy. @@ -137,22 +171,6 @@ public static function get_file_url_for_environment( $min_path, $non_min_path, $ return apply_filters( 'jetpack_get_file_for_environment', $url, $min_path, $non_min_path ); } - /** - * A helper function that lets you enqueue scripts in an async fashion. - * - * @param string $handle Name of the script. Should be unique. - * @param string $min_path Minimized script path. - * @param string $non_min_path Full Script path. - * @param array $deps Array of script dependencies. - * @param bool $ver The script version. - * @param bool $in_footer Should the script be included in the footer. - */ - public static function enqueue_async_script( $handle, $min_path, $non_min_path, $deps = array(), $ver = false, $in_footer = true ) { - $assets_instance = self::instance(); - $assets_instance->add_async_script( $handle ); - wp_enqueue_script( $handle, self::get_file_url_for_environment( $min_path, $non_min_path ), $deps, $ver, $in_footer ); - } - /** * Passes an array of URLs to wp_resource_hints. * @@ -270,6 +288,11 @@ public static function normalize_path( $path ) { return $ret; } + // endregion . + + // //////////////////// + // region Webpack-built script registration + /** * Register a Webpack-built script. * @@ -426,6 +449,10 @@ public static function wp_default_scripts_hook( $wp_scripts ) { $data['baseUrl'] = site_url( substr( trailingslashit( $lang_dir ), strlen( untrailingslashit( $abspath ) ) ) ); } + foreach ( self::$domain_map as $from => list( $to, $type ) ) { + $data['domainMap'][ $from ] = ( 'core' === $type ? '' : "{$type}/" ) . $to; + } + /** * Filters the i18n state data for use by Webpack bundles built with * `@automattic/i18n-loader-webpack-plugin`. @@ -458,4 +485,215 @@ public static function wp_default_scripts_hook( $wp_scripts ) { $wp_scripts->add_inline_script( 'wp-jp-i18n-state', $js, 'before' ); } + // endregion . + + // //////////////////// + // region Textdomain aliasing + + /** + * Register a textdomain alias. + * + * Composer packages included in plugins will likely not use the textdomain of the plugin, while + * WordPress's i18n infrastructure will include the translations in the plugin's domain. This + * allows for mapping the package's domain to the plugin's. + * + * Since multiple plugins may use the same package, we include the package's version here so + * as to choose the most recent translations (which are most likely to match the package + * selected by jetpack-autoloader). + * + * @since $$next-version$$ + * @param string $from Domain to alias. + * @param string $to Domain to alias it to. + * @param string $totype What is the target of the alias: 'plugins', 'themes', or 'core'. + * @param string $ver Version of the `$from` domain. + * @throws InvalidArgumentException If arguments are invalid. + */ + public static function alias_textdomain( $from, $to, $totype, $ver ) { + if ( ! in_array( $totype, array( 'plugins', 'themes', 'core' ), true ) ) { + throw new InvalidArgumentException( 'Type must be "plugins", "themes", or "core"' ); + } + + if ( did_action( 'wp_default_scripts' ) ) { + _doing_it_wrong( + __METHOD__, + sprintf( + /* translators: 1: wp_default_scripts. 2: Name of the domain being aliased. */ + esc_html__( 'Textdomain aliases should be registered before the %1$s hook. This notice was triggered by the %2$s domain.', 'jetpack' ), + 'wp_default_scripts', + '' . esc_html( $from ) . '' + ), + '' + ); + } + + if ( empty( self::$domain_map[ $from ] ) ) { + self::init_domain_map_hooks( $from, array() === self::$domain_map ); + self::$domain_map[ $from ] = array( $to, $totype, $ver ); + } elseif ( Semver::compare( $ver, self::$domain_map[ $from ][2] ) > 0 ) { + self::$domain_map[ $from ] = array( $to, $totype, $ver ); + } + } + + /** + * Register textdomain aliases from a mapping file. + * + * The mapping file is simply a PHP file that returns an array + * with the following properties: + * - 'domain': String, `$to` + * - 'type': String, `$totype` + * - 'packages': Array, mapping `$from` to `$ver`. + * + * @since $$next-version$$ + * @param string $file Mapping file. + */ + public static function alias_textdomains_from_file( $file ) { + $data = require $file; + foreach ( $data['packages'] as $from => $ver ) { + self::alias_textdomain( $from, $data['domain'], $data['type'], $ver ); + } + } + + /** + * Register the hooks for textdomain aliasing. + * + * @param string $domain Domain to alias. + * @param bool $firstcall If this is the first call. + */ + private static function init_domain_map_hooks( $domain, $firstcall ) { + // If WordPress's plugin API is available already, use it. If not, + // drop data into `$wp_filter` for `WP_Hook::build_preinitialized_hooks()`. + if ( function_exists( 'add_filter' ) ) { + $add_filter = 'add_filter'; + } else { + $add_filter = function ( $hook_name, $callback, $priority = 10, $accepted_args = 1 ) { + global $wp_filter; + // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited + $wp_filter[ $hook_name ][ $priority ][] = array( + 'accepted_args' => $accepted_args, + 'function' => $callback, + ); + }; + } + + $add_filter( "gettext_{$domain}", array( self::class, 'filter_gettext' ), 10, 3 ); + $add_filter( "ngettext_{$domain}", array( self::class, 'filter_ngettext' ), 10, 5 ); + $add_filter( "gettext_with_context_{$domain}", array( self::class, 'filter_gettext_with_context' ), 10, 4 ); + $add_filter( "ngettext_with_context_{$domain}", array( self::class, 'filter_ngettext_with_context' ), 10, 6 ); + if ( $firstcall ) { + $add_filter( 'load_script_translation_file', array( self::class, 'filter_load_script_translation_file' ), 10, 3 ); + } + } + + /** + * Filter for `gettext`. + * + * @since $$next-version$$ + * @param string $translation Translated text. + * @param string $text Text to translate. + * @param string $domain Text domain. + * @return string Translated text. + */ + public static function filter_gettext( $translation, $text, $domain ) { + if ( $translation === $text ) { + // phpcs:ignore WordPress.WP.I18n + $newtext = __( $text, self::$domain_map[ $domain ][0] ); + if ( $newtext !== $text ) { + return $newtext; + } + } + return $translation; + } + + /** + * Filter for `ngettext`. + * + * @since $$next-version$$ + * @param string $translation Translated text. + * @param string $single The text to be used if the number is singular. + * @param string $plural The text to be used if the number is plural. + * @param string $number The number to compare against to use either the singular or plural form. + * @param string $domain Text domain. + * @return string Translated text. + */ + public static function filter_ngettext( $translation, $single, $plural, $number, $domain ) { + if ( $translation === $single || $translation === $plural ) { + // phpcs:ignore WordPress.WP.I18n + $translation = _n( $single, $plural, $number, self::$domain_map[ $domain ][0] ); + } + return $translation; + } + + /** + * Filter for `gettext_with_context`. + * + * @since $$next-version$$ + * @param string $translation Translated text. + * @param string $text Text to translate. + * @param string $context Context information for the translators. + * @param string $domain Text domain. + * @return string Translated text. + */ + public static function filter_gettext_with_context( $translation, $text, $context, $domain ) { + if ( $translation === $text ) { + // phpcs:ignore WordPress.WP.I18n + $translation = _x( $text, $context, self::$domain_map[ $domain ][0] ); + } + return $translation; + } + + /** + * Filter for `ngettext_with_context`. + * + * @since $$next-version$$ + * @param string $translation Translated text. + * @param string $single The text to be used if the number is singular. + * @param string $plural The text to be used if the number is plural. + * @param string $number The number to compare against to use either the singular or plural form. + * @param string $context Context information for the translators. + * @param string $domain Text domain. + * @return string Translated text. + */ + public static function filter_ngettext_with_context( $translation, $single, $plural, $number, $context, $domain ) { + if ( $translation === $single || $translation === $plural ) { + // phpcs:ignore WordPress.WP.I18n + $translation = _nx( $single, $plural, $number, $context, self::$domain_map[ $domain ][0] ); + } + return $translation; + } + + /** + * Filter for `load_script_translation_file`. + * + * @since $$next-version$$ + * @param string|false $file Path to the translation file to load. False if there isn't one. + * @param string $handle Name of the script to register a translation domain to. + * @param string $domain The text domain. + */ + public static function filter_load_script_translation_file( $file, $handle, $domain ) { + if ( false !== $file && isset( self::$domain_map[ $domain ] ) && ! is_readable( $file ) ) { + // Determine the part of the filename after the domain. + $suffix = basename( $file ); + $l = strlen( $domain ); + if ( substr( $suffix, 0, $l ) !== $domain || '-' !== $suffix[ $l ] ) { + return $file; + } + $suffix = substr( $suffix, $l ); + $lang_dir = Jetpack_Constants::get_constant( 'WP_LANG_DIR' ); + + // Look for replacement files. + list( $newdomain, $type ) = self::$domain_map[ $domain ]; + $newfile = $lang_dir . ( 'core' === $type ? '/' : "/{$type}/" ) . $newdomain . $suffix; + if ( is_readable( $newfile ) ) { + return $newfile; + } + } + return $file; + } + + // endregion . + } + +// Enable section folding in vim: +// vim: foldmarker=//\ region,//\ endregion foldmethod=marker +// . diff --git a/projects/packages/assets/src/class-semver.php b/projects/packages/assets/src/class-semver.php new file mode 100644 index 0000000000000..5a5c659114c68 --- /dev/null +++ b/projects/packages/assets/src/class-semver.php @@ -0,0 +1,122 @@ +\d+)\.(?P\d+)\.(?P\d+)(?:-(?P(?:[0-9a-zA-Z-]+)(?:\.(?:[0-9a-zA-Z-]+))*))?(?:\+(?P[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/', $version, $m ) ) { + throw new InvalidArgumentException( "Version number \"$version\" is not in a recognized format." ); + } + $info = array( + 'major' => (int) $m['major'], + 'minor' => (int) $m['minor'], + 'patch' => (int) $m['patch'], + 'version' => sprintf( '%d.%d.%d', $m['major'], $m['minor'], $m['patch'] ), + 'prerelease' => isset( $m['prerelease'] ) && '' !== $m['prerelease'] ? $m['prerelease'] : null, + 'buildinfo' => isset( $m['buildinfo'] ) && '' !== $m['buildinfo'] ? $m['buildinfo'] : null, + ); + + if ( null !== $info['prerelease'] ) { + $sep = ''; + $prerelease = ''; + foreach ( explode( '.', $info['prerelease'] ) as $part ) { + if ( ctype_digit( $part ) ) { + $part = (int) $part; + } + $prerelease .= $sep . $part; + $sep = '.'; + } + $info['prerelease'] = $prerelease; + } + + return $info; + } + + /** + * Compare two version numbers. + * + * @param string $a First version. + * @param string $b Second version. + * @return int Less than, equal to, or greater than 0 depending on whether `$a` is less than, equal to, or greater than `$b`. + * @throws InvalidArgumentException If the version numbers are not in a recognized format. + */ + public static function compare( $a, $b ) { + $aa = self::parse( $a ); + $bb = self::parse( $b ); + if ( $aa['major'] !== $bb['major'] ) { + return $aa['major'] - $bb['major']; + } + if ( $aa['minor'] !== $bb['minor'] ) { + return $aa['minor'] - $bb['minor']; + } + if ( $aa['patch'] !== $bb['patch'] ) { + return $aa['patch'] - $bb['patch']; + } + + if ( null === $aa['prerelease'] ) { + return null === $bb['prerelease'] ? 0 : 1; + } + if ( null === $bb['prerelease'] ) { + return -1; + } + + $aaa = explode( '.', $aa['prerelease'] ); + $bbb = explode( '.', $bb['prerelease'] ); + $al = count( $aaa ); + $bl = count( $bbb ); + for ( $i = 0; $i < $al && $i < $bl; $i++ ) { + $a = $aaa[ $i ]; + $b = $bbb[ $i ]; + if ( ctype_digit( $a ) ) { + if ( ctype_digit( $b ) ) { + if ( (int) $a !== (int) $b ) { + return $a - $b; + } + } else { + return -1; + } + } elseif ( ctype_digit( $b ) ) { + return 1; + } else { + $tmp = strcmp( $a, $b ); + if ( 0 !== $tmp ) { + return $tmp; + } + } + } + return $al - $bl; + } + +} diff --git a/projects/packages/assets/tests/php/patchwork.json b/projects/packages/assets/tests/php/patchwork.json index c13996e5084dc..081567b053ec1 100644 --- a/projects/packages/assets/tests/php/patchwork.json +++ b/projects/packages/assets/tests/php/patchwork.json @@ -1,3 +1,3 @@ { - "redefinable-internals": [ "filemtime" ] + "redefinable-internals": [ "filemtime", "is_readable" ] } diff --git a/projects/packages/assets/tests/php/test-assets-files/i18n-map.php b/projects/packages/assets/tests/php/test-assets-files/i18n-map.php new file mode 100644 index 0000000000000..522298c29c572 --- /dev/null +++ b/projects/packages/assets/tests/php/test-assets-files/i18n-map.php @@ -0,0 +1,9 @@ + 'target', + 'type' => 'plugins', + 'packages' => array( + 'foo' => '1.2.3', + 'bar' => '4.5.6', + ), +); diff --git a/projects/packages/assets/tests/php/test-assets.php b/projects/packages/assets/tests/php/test-assets.php index 6e201d5923ae0..e798c4189c267 100644 --- a/projects/packages/assets/tests/php/test-assets.php +++ b/projects/packages/assets/tests/php/test-assets.php @@ -11,7 +11,9 @@ use Brain\Monkey; use Brain\Monkey\Filters; use Brain\Monkey\Functions; +use InvalidArgumentException; use PHPUnit\Framework\TestCase; +use Wikimedia\TestingAccessWrapper; /** * Assets test suite. @@ -34,12 +36,12 @@ public function set_up() { array( 'wp_parse_url' => 'parse_url', 'wp_json_encode' => 'json_encode', - '__' => function ( $text ) { - return $text; - }, 'esc_html' => function ( $text ) { return htmlspecialchars( $text, ENT_QUOTES ); }, + 'esc_html__' => function ( $text ) { + return htmlspecialchars( $text, ENT_QUOTES ); + }, 'add_query_arg' => function ( ...$args ) { $this->assertCount( 3, $args ); list( $k, $v, $url ) = $args; @@ -65,10 +67,9 @@ public function tear_down() { Jetpack_Constants::clear_constants(); // Clear the instance. - $rc = new \ReflectionClass( Assets::class ); - $rp = $rc->getProperty( 'instance' ); - $rp->setAccessible( true ); - $rp->setValue( null ); + $wrap = TestingAccessWrapper::newFromClass( Assets::class ); + $wrap->instance = null; + $wrap->domain_map = array(); } /** @@ -325,6 +326,9 @@ public static function provide_normalize_path() { public function test_register_script( $args, $expect, $extra = array() ) { Functions\stubs( array( + '__' => function ( $v ) { + return $v; + }, 'filemtime' => function ( $v ) { return crc32( basename( $v ) ); }, @@ -359,12 +363,9 @@ public function test_register_script( $args, $expect, $extra = array() ) { Assets::register_script( ...$args ); // Check whether $options['async'] was honored. - $rc = new \ReflectionClass( Assets::instance() ); - $rp = $rc->getProperty( 'defer_script_handles' ); - $rp->setAccessible( true ); $this->assertSame( isset( $extra['async'] ) ? $extra['async'] : array(), - $rp->getValue( Assets::instance() ) + TestingAccessWrapper::newFromObject( Assets::instance() )->defer_script_handles ); } @@ -597,12 +598,12 @@ public static function provide_register_script() { 'Bad path' => array( array( 'single-file', 'test-assets-files/single-js-file.jsx', __FILE__ ), array(), - array( 'exception' => new \InvalidArgumentException( '$path must end in ".js"' ) ), + array( 'exception' => new InvalidArgumentException( '$path must end in ".js"' ) ), ), 'Bad css_path' => array( array( 'single-file', 'test-assets-files/single-js-file.js', __FILE__, array( 'css_path' => 'foo.js' ) ), array(), - array( 'exception' => new \InvalidArgumentException( '$options[\'css_path\'] must end in ".css"' ) ), + array( 'exception' => new InvalidArgumentException( '$options[\'css_path\'] must end in ".css"' ) ), ), 'wp-i18n without textdomain' => array( array( 'everything', 'test-assets-files/everything.js', __FILE__, array() ), @@ -635,8 +636,9 @@ public static function provide_register_script() { */ public function test_wp_default_scripts_hook( $expect_filter, $expect_js, $options = array() ) { $options += array( - 'constants' => array(), - 'locale' => 'en_US', + 'constants' => array(), + 'locale' => 'en_US', + 'domain_map' => array(), ); $constants = $options['constants'] + array( @@ -647,6 +649,8 @@ public function test_wp_default_scripts_hook( $expect_filter, $expect_js, $optio Jetpack_Constants::set_constant( $k, $v ); } + TestingAccessWrapper::newFromClass( Assets::class )->domain_map = $options['domain_map']; + Functions\expect( 'determine_locale' )->andReturn( $options['locale'] ); Functions\expect( 'site_url' )->andReturnUsing( function ( $v ) { @@ -687,12 +691,21 @@ public function provide_wp_default_scripts_hook() { array( 'baseUrl' => 'http://example.com/wp-includes/languages/', 'locale' => 'de_DE', - 'domainMap' => array(), + 'domainMap' => array( + 'jetpack-foo' => 'plugins/jetpack', + 'jetpack-bar' => 'themes/sometheme', + 'core' => 'default', + ), ), - 'wp.jpI18nState = {"baseUrl":"http://example.com/wp-includes/languages/","locale":"de_DE","domainMap":{}};', + 'wp.jpI18nState = {"baseUrl":"http://example.com/wp-includes/languages/","locale":"de_DE","domainMap":{"jetpack-foo":"plugins/jetpack","jetpack-bar":"themes/sometheme","core":"default"}};', array( - 'constants' => array( 'WP_LANG_DIR' => '/path/to/wordpress/wp-includes/languages' ), - 'locale' => 'de_DE', + 'constants' => array( 'WP_LANG_DIR' => '/path/to/wordpress/wp-includes/languages' ), + 'locale' => 'de_DE', + 'domain_map' => array( + 'jetpack-foo' => array( 'jetpack', 'plugins', '1.2.3' ), + 'jetpack-bar' => array( 'sometheme', 'themes', '1.2.3' ), + 'core' => array( 'default', 'core', '1.2.3' ), + ), ), ), 'Bad WP_LANG_DIR' => array( @@ -774,4 +787,187 @@ public function provide_wp_default_scripts_hook() { ); } + /** Test textdomain aliasing and hook adding. */ + public function test_alias_textdomain() { + Filters\expectAdded( 'gettext_foo' )->once()->with( array( Assets::class, 'filter_gettext' ), 10, 3 ); + Filters\expectAdded( 'ngettext_foo' )->once()->with( array( Assets::class, 'filter_ngettext' ), 10, 5 ); + Filters\expectAdded( 'gettext_with_context_foo' )->once()->with( array( Assets::class, 'filter_gettext_with_context' ), 10, 4 ); + Filters\expectAdded( 'ngettext_with_context_foo' )->once()->with( array( Assets::class, 'filter_ngettext_with_context' ), 10, 6 ); + Filters\expectAdded( 'gettext_bar' )->once()->with( array( Assets::class, 'filter_gettext' ), 10, 3 ); + Filters\expectAdded( 'ngettext_bar' )->once()->with( array( Assets::class, 'filter_ngettext' ), 10, 5 ); + Filters\expectAdded( 'gettext_with_context_bar' )->once()->with( array( Assets::class, 'filter_gettext_with_context' ), 10, 4 ); + Filters\expectAdded( 'ngettext_with_context_bar' )->once()->with( array( Assets::class, 'filter_ngettext_with_context' ), 10, 6 ); + Filters\expectAdded( 'load_script_translation_file' )->once()->with( array( Assets::class, 'filter_load_script_translation_file' ), 10, 3 ); + + Assets::alias_textdomain( 'foo', 'one', 'plugins', '1.2.3' ); + Assets::alias_textdomain( 'foo', 'two', 'plugins', '1.2.4' ); + Assets::alias_textdomain( 'bar', 'one', 'themes', '1.2.3' ); + Assets::alias_textdomain( 'bar', 'two', 'themes', '1.2.2' ); + + $this->assertEquals( + array( + 'foo' => array( 'two', 'plugins', '1.2.4' ), + 'bar' => array( 'one', 'themes', '1.2.3' ), + ), + TestingAccessWrapper::newFromClass( Assets::class )->domain_map + ); + } + + /** Test textdomain aliasing with bad type. */ + public function test_alias_textdomain__bad_type() { + $this->expectException( InvalidArgumentException::class ); + $this->expectExceptionMessage( 'Type must be "plugins", "themes", or "core"' ); + Assets::alias_textdomain( 'foo', 'one', 'bogus', '1.2.5' ); + } + + /** Test alias_textdomains_from_file */ + public function test_alias_textdomains_from_file() { + Assets::alias_textdomains_from_file( __DIR__ . '/test-assets-files/i18n-map.php' ); + $this->assertEquals( + array( + 'foo' => array( 'target', 'plugins', '1.2.3' ), + 'bar' => array( 'target', 'plugins', '4.5.6' ), + ), + TestingAccessWrapper::newFromClass( Assets::class )->domain_map + ); + } + + /** Test textdomain aliasing called after wp_default_scripts. */ + public function test_alias_textdomain__after_wp_default_scripts() { + do_action( 'wp_default_scripts' ); + Functions\expect( '_doing_it_wrong' )->once()->with( + Assets::class . '::alias_textdomain', + 'Textdomain aliases should be registered before the wp_default_scripts hook. This notice was triggered by the foo domain.', + '' + ); + Assets::alias_textdomain( 'foo', 'one', 'plugins', '1.2.5' ); + } + + /** Test filter_gettext. */ + public function test_filter_gettext() { + TestingAccessWrapper::newFromClass( Assets::class )->domain_map = array( 'olddomain' => array( 'newdomain', 'plugins', '1.2.3' ) ); + + Functions\expect( '__' )->once()->with( 'foo', 'newdomain' )->andReturn( 'oo-fay' ); + Functions\expect( '__' )->never()->with( 'bar', 'newdomain' ); + + $this->assertEquals( 'oo-fay', Assets::filter_gettext( 'foo', 'foo', 'olddomain' ) ); + $this->assertEquals( 'foo', Assets::filter_gettext( 'foo', 'bar', 'olddomain' ) ); + $this->assertEquals( 'bar', Assets::filter_gettext( 'bar', 'foo', 'olddomain' ) ); + } + + /** Test filter_ngettext. */ + public function test_filter_ngettext() { + TestingAccessWrapper::newFromClass( Assets::class )->domain_map = array( 'olddomain' => array( 'newdomain', 'plugins', '1.2.3' ) ); + + Functions\expect( '_n' )->once()->with( 'foo', 'foos', 10, 'newdomain' )->andReturn( 'oos-fay' ); + Functions\expect( '_n' )->never()->with( 'bar', 'bars', 42, 'newdomain' ); + + $this->assertEquals( 'oos-fay', Assets::filter_ngettext( 'foo', 'foo', 'foos', 10, 'olddomain' ) ); + $this->assertEquals( 'foo', Assets::filter_ngettext( 'foo', 'bar', 'bars', 10, 'olddomain' ) ); + $this->assertEquals( 'bar', Assets::filter_ngettext( 'bar', 'foo', 'foos', 10, 'olddomain' ) ); + } + + /** Test filter_gettext_with_context. */ + public function test_filter_gettext_with_context() { + TestingAccessWrapper::newFromClass( Assets::class )->domain_map = array( 'olddomain' => array( 'newdomain', 'plugins', '1.2.3' ) ); + + Functions\expect( '_x' )->once()->with( 'foo', 'context', 'newdomain' )->andReturn( 'oo-fay' ); + Functions\expect( '_x' )->never()->with( 'bar', 'context', 'newdomain' ); + + $this->assertEquals( 'oo-fay', Assets::filter_gettext_with_context( 'foo', 'foo', 'context', 'olddomain' ) ); + $this->assertEquals( 'foo', Assets::filter_gettext_with_context( 'foo', 'bar', 'context', 'olddomain' ) ); + $this->assertEquals( 'bar', Assets::filter_gettext_with_context( 'bar', 'foo', 'context', 'olddomain' ) ); + } + + /** Test filter_ngettext_with_context. */ + public function test_filter_ngettext_with_context() { + TestingAccessWrapper::newFromClass( Assets::class )->domain_map = array( 'olddomain' => array( 'newdomain', 'plugins', '1.2.3' ) ); + + Functions\expect( '_nx' )->once()->with( 'foo', 'foos', 10, 'context', 'newdomain' )->andReturn( 'oos-fay' ); + Functions\expect( '_nx' )->never()->with( 'bar', 'bars', 42, 'context', 'newdomain' ); + + $this->assertEquals( 'oos-fay', Assets::filter_ngettext_with_context( 'foo', 'foo', 'foos', 10, 'context', 'olddomain' ) ); + $this->assertEquals( 'foo', Assets::filter_ngettext_with_context( 'foo', 'bar', 'bars', 10, 'context', 'olddomain' ) ); + $this->assertEquals( 'bar', Assets::filter_ngettext_with_context( 'bar', 'foo', 'foos', 10, 'context', 'olddomain' ) ); + } + + /** + * Test filter_load_script_translation_file. + * + * @dataProvider provide_filter_load_script_translation_file + * @param array $args Arguments to the filter. + * @param array $is_readable Expected files passed to `is_readable()` and the corresponding return values. + * @param string $expect Expected return value. + */ + public function test_filter_load_script_translation_file( $args, $is_readable, $expect ) { + Jetpack_Constants::set_constant( 'WP_LANG_DIR', '/path/to/wordpress/wp-content/languages' ); + TestingAccessWrapper::newFromClass( Assets::class )->domain_map = array( + 'one' => array( 'new1', 'plugins', '1.2.3' ), + 'two' => array( 'new2', 'themes', '1.2.3' ), + 'three' => array( 'new3', 'core', '1.2.3' ), + ); + Functions\when( 'is_readable' )->alias( + function ( $file ) use ( $is_readable ) { + if ( isset( $is_readable[ $file ] ) ) { + return $is_readable[ $file ]; + } + throw new InvalidArgumentException( "Unexpected call to is_readable( $file )" ); + } + ); + + $this->assertSame( $expect, Assets::filter_load_script_translation_file( ...$args ) ); + } + + /** Data provider for test_filter_load_script_translation_file. */ + public function provide_filter_load_script_translation_file() { + return array( + 'Passed false' => array( + array( false, 'handle', 'one' ), + array(), + false, + ), + 'Unmapped domain' => array( + array( '/path/to/wherever/one-en_piglatin-abcdefhash.json', 'handle', 'four' ), + array(), + '/path/to/wherever/one-en_piglatin-abcdefhash.json', + ), + 'Requested file exists' => array( + array( '/path/to/wherever/one-en_piglatin-abcdefhash.json', 'handle', 'one' ), + array( '/path/to/wherever/one-en_piglatin-abcdefhash.json' => true ), + '/path/to/wherever/one-en_piglatin-abcdefhash.json', + ), + 'Mapped file exists' => array( + array( '/path/to/wherever/one-en_piglatin-abcdefhash.json', 'handle', 'one' ), + array( + '/path/to/wherever/one-en_piglatin-abcdefhash.json' => false, + '/path/to/wordpress/wp-content/languages/plugins/new1-en_piglatin-abcdefhash.json' => true, + ), + '/path/to/wordpress/wp-content/languages/plugins/new1-en_piglatin-abcdefhash.json', + ), + 'Mapped file is missing' => array( + array( '/path/to/wherever/two-en_piglatin-abcdefhash.json', 'handle', 'two' ), + array( + '/path/to/wherever/two-en_piglatin-abcdefhash.json' => false, + '/path/to/wordpress/wp-content/languages/themes/new2-en_piglatin-abcdefhash.json' => false, + ), + '/path/to/wherever/two-en_piglatin-abcdefhash.json', + ), + 'Mapped to core' => array( + array( '/path/to/wherever/three-en_piglatin-abcdefhash.json', 'handle', 'three' ), + array( + '/path/to/wherever/three-en_piglatin-abcdefhash.json' => false, + '/path/to/wordpress/wp-content/languages/new3-en_piglatin-abcdefhash.json' => true, + ), + '/path/to/wordpress/wp-content/languages/new3-en_piglatin-abcdefhash.json', + ), + 'Domain not in requested file' => array( + array( '/path/to/wherever/something-en_piglatin-abcdefhash.json', 'handle', 'two' ), + array( + '/path/to/wherever/something-en_piglatin-abcdefhash.json' => false, + ), + '/path/to/wherever/something-en_piglatin-abcdefhash.json', + ), + ); + } + } diff --git a/projects/packages/assets/tests/php/test-semver.php b/projects/packages/assets/tests/php/test-semver.php new file mode 100644 index 0000000000000..087faee5b9d7c --- /dev/null +++ b/projects/packages/assets/tests/php/test-semver.php @@ -0,0 +1,216 @@ +expectException( InvalidArgumentException::class ); + $this->expectExceptionMessage( $expect->getMessage() ); + Semver::parse( $version ); + } else { + $this->assertSame( $expect, Semver::parse( $version ) ); + } + } + + /** + * Data provider for testParse. + */ + public function provideParse() { + return array( + array( + '1.2.3', + array( + 'major' => 1, + 'minor' => 2, + 'patch' => 3, + 'version' => '1.2.3', + 'prerelease' => null, + 'buildinfo' => null, + ), + ), + array( + '11.22.33', + array( + 'major' => 11, + 'minor' => 22, + 'patch' => 33, + 'version' => '11.22.33', + 'prerelease' => null, + 'buildinfo' => null, + ), + ), + array( + '0.0.0', + array( + 'major' => 0, + 'minor' => 0, + 'patch' => 0, + 'version' => '0.0.0', + 'prerelease' => null, + 'buildinfo' => null, + ), + ), + array( + '1.2.3-alpha', + array( + 'major' => 1, + 'minor' => 2, + 'patch' => 3, + 'version' => '1.2.3', + 'prerelease' => 'alpha', + 'buildinfo' => null, + ), + ), + array( + '1.2.3-alpha.1', + array( + 'major' => 1, + 'minor' => 2, + 'patch' => 3, + 'version' => '1.2.3', + 'prerelease' => 'alpha.1', + 'buildinfo' => null, + ), + ), + array( + '1.2.3+foobar', + array( + 'major' => 1, + 'minor' => 2, + 'patch' => 3, + 'version' => '1.2.3', + 'prerelease' => null, + 'buildinfo' => 'foobar', + ), + ), + array( + '1.2.3+foobar.2', + array( + 'major' => 1, + 'minor' => 2, + 'patch' => 3, + 'version' => '1.2.3', + 'prerelease' => null, + 'buildinfo' => 'foobar.2', + ), + ), + array( + '1.2.3-alpha+foobar', + array( + 'major' => 1, + 'minor' => 2, + 'patch' => 3, + 'version' => '1.2.3', + 'prerelease' => 'alpha', + 'buildinfo' => 'foobar', + ), + ), + array( + '1.2.3-alpha.1+foobar.2', + array( + 'major' => 1, + 'minor' => 2, + 'patch' => 3, + 'version' => '1.2.3', + 'prerelease' => 'alpha.1', + 'buildinfo' => 'foobar.2', + ), + ), + array( + '0001.0002.000-000alpha000.0001+000foobar000.0002', + array( + 'major' => 1, + 'minor' => 2, + 'patch' => 0, + 'version' => '1.2.0', + 'prerelease' => '000alpha000.1', + 'buildinfo' => '000foobar000.0002', + ), + ), + + array( '1.2', new InvalidArgumentException( 'Version number "1.2" is not in a recognized format.' ) ), + array( '1.2.x', new InvalidArgumentException( 'Version number "1.2.x" is not in a recognized format.' ) ), + array( '1.x.4', new InvalidArgumentException( 'Version number "1.x.4" is not in a recognized format.' ) ), + array( '1..4', new InvalidArgumentException( 'Version number "1..4" is not in a recognized format.' ) ), + array( '.2.3', new InvalidArgumentException( 'Version number ".2.3" is not in a recognized format.' ) ), + array( '1.2.', new InvalidArgumentException( 'Version number "1.2." is not in a recognized format.' ) ), + array( '1.2.-1', new InvalidArgumentException( 'Version number "1.2.-1" is not in a recognized format.' ) ), + array( 'v1.2.3', new InvalidArgumentException( 'Version number "v1.2.3" is not in a recognized format.' ) ), + array( '1.2.3.4', new InvalidArgumentException( 'Version number "1.2.3.4" is not in a recognized format.' ) ), + array( '1.2-alpha', new InvalidArgumentException( 'Version number "1.2-alpha" is not in a recognized format.' ) ), + array( '1.2.3-?', new InvalidArgumentException( 'Version number "1.2.3-?" is not in a recognized format.' ) ), + array( '1.2.3+?', new InvalidArgumentException( 'Version number "1.2.3+?" is not in a recognized format.' ) ), + array( '1.2.3-a..b', new InvalidArgumentException( 'Version number "1.2.3-a..b" is not in a recognized format.' ) ), + array( '1.2.3+a..b', new InvalidArgumentException( 'Version number "1.2.3+a..b" is not in a recognized format.' ) ), + ); + } + + /** + * Test compare. + * + * @dataProvider provideCompare + * @param string $a Version A. + * @param string $expect Expected result converted to a string, '>', '==', or '<'. + * @param string $b Version B. + */ + public function testCompare( $a, $expect, $b ) { + $ret = Semver::compare( $a, $b ); + $this->assertIsInt( $ret ); + $ret = $ret < 0 ? '<' : ( $ret > 0 ? '>' : '==' ); + $this->assertSame( $expect, $ret ); + } + + /** + * Data provider for testCompare. + */ + public function provideCompare() { + return array( + array( '1.0.0', '==', '1.0.0' ), + array( '1.0.0', '<', '2.0.0' ), + array( '2.0.0', '>', '1.0.0' ), + array( '1.1.0', '>', '1.0.0' ), + array( '1.0.0', '<', '1.1.0' ), + array( '1.999.999', '<', '2.0.0' ), + array( '1.1.0', '<', '1.1.1' ), + array( '1.0.999', '<', '1.1.0' ), + array( '1.1.2', '>', '1.1.1' ), + array( '1.1.1-dev', '<', '1.1.1' ), + array( '1.1.1', '>', '1.1.1-p1' ), + array( '1.1.1-alpha', '<', '1.1.1-beta' ), + array( '1.1.1-dev', '>', '1.1.1-beta' ), // No special treatment for "dev". + array( '1.1.1-alpha.9', '<', '1.1.1-beta.1' ), + array( '1.1.1-beta.9', '>', '1.1.1-beta.1' ), + array( '1.1.1-beta.9', '==', '1.1.1-beta.9' ), + array( '1.1.1-beta.9.1', '>', '1.1.1-beta.9' ), + array( '1.1.1-beta.9', '<', '1.1.1-beta.a' ), + array( '1.1.1-beta.1a', '>', '1.1.1-beta.9' ), + array( '1.1.1-BETA', '<', '1.1.1-beta' ), // Case sensitive. + array( '1.1.1-ZETA', '<', '1.1.1-beta' ), // Case sensitive. + array( '1.1.1-alpha2', '>', '1.1.1-alpha10' ), // No natural sorting. + array( '1.1.1+beta.9.1', '==', '1.1.1+beta.9' ), + ); + } + +} diff --git a/projects/packages/connection-ui/changelog/add-assets-textdomain-aliasing b/projects/packages/connection-ui/changelog/add-assets-textdomain-aliasing new file mode 100644 index 0000000000000..c47cb18e82997 --- /dev/null +++ b/projects/packages/connection-ui/changelog/add-assets-textdomain-aliasing @@ -0,0 +1,4 @@ +Significance: patch +Type: changed + +Updated package dependencies. diff --git a/projects/packages/connection-ui/composer.json b/projects/packages/connection-ui/composer.json index c6d2fd05c71c2..bcee9cbdec16a 100644 --- a/projects/packages/connection-ui/composer.json +++ b/projects/packages/connection-ui/composer.json @@ -4,7 +4,7 @@ "type": "jetpack-library", "license": "GPL-2.0-or-later", "require": { - "automattic/jetpack-assets": "^1.14", + "automattic/jetpack-assets": "^1.15", "automattic/jetpack-connection": "^1.33", "automattic/jetpack-constants": "^1.6", "automattic/jetpack-device-detection": "^1.4", diff --git a/projects/packages/connection-ui/package.json b/projects/packages/connection-ui/package.json index 6a3e709ed6d8e..54d750e9f6502 100644 --- a/projects/packages/connection-ui/package.json +++ b/projects/packages/connection-ui/package.json @@ -1,6 +1,6 @@ { "name": "jetpack-connection-manager-ui", - "version": "2.2.0", + "version": "2.2.1-alpha", "description": "Jetpack Connection Manager UI", "main": "_inc/admin.jsx", "repository": "https://github.com/Automattic/jetpack-connection-ui", diff --git a/projects/packages/jitm/changelog/add-assets-textdomain-aliasing b/projects/packages/jitm/changelog/add-assets-textdomain-aliasing new file mode 100644 index 0000000000000..c47cb18e82997 --- /dev/null +++ b/projects/packages/jitm/changelog/add-assets-textdomain-aliasing @@ -0,0 +1,4 @@ +Significance: patch +Type: changed + +Updated package dependencies. diff --git a/projects/packages/jitm/composer.json b/projects/packages/jitm/composer.json index 9596283f38ec7..aead463a40623 100644 --- a/projects/packages/jitm/composer.json +++ b/projects/packages/jitm/composer.json @@ -5,7 +5,7 @@ "license": "GPL-2.0-or-later", "require": { "automattic/jetpack-a8c-mc-stats": "^1.4", - "automattic/jetpack-assets": "^1.14", + "automattic/jetpack-assets": "^1.15", "automattic/jetpack-connection": "^1.33", "automattic/jetpack-device-detection": "^1.4", "automattic/jetpack-logo": "^1.5", diff --git a/projects/packages/jitm/src/class-jitm.php b/projects/packages/jitm/src/class-jitm.php index bd8862fc49d1a..d42d24ef86f18 100644 --- a/projects/packages/jitm/src/class-jitm.php +++ b/projects/packages/jitm/src/class-jitm.php @@ -20,7 +20,7 @@ */ class JITM { - const PACKAGE_VERSION = '2.1.1'; + const PACKAGE_VERSION = '2.1.2-alpha'; /** * The configuration method that is called from the jetpack-config package. diff --git a/projects/packages/lazy-images/changelog/add-assets-textdomain-aliasing b/projects/packages/lazy-images/changelog/add-assets-textdomain-aliasing new file mode 100644 index 0000000000000..c47cb18e82997 --- /dev/null +++ b/projects/packages/lazy-images/changelog/add-assets-textdomain-aliasing @@ -0,0 +1,4 @@ +Significance: patch +Type: changed + +Updated package dependencies. diff --git a/projects/packages/lazy-images/composer.json b/projects/packages/lazy-images/composer.json index 9ccce03aac67c..8cb39c25ec931 100644 --- a/projects/packages/lazy-images/composer.json +++ b/projects/packages/lazy-images/composer.json @@ -5,7 +5,7 @@ "license": "GPL-2.0-or-later", "require": { "automattic/jetpack-constants": "^1.6", - "automattic/jetpack-assets": "^1.14" + "automattic/jetpack-assets": "^1.15" }, "require-dev": { "automattic/wordbless": "dev-master", diff --git a/projects/packages/my-jetpack/changelog/add-assets-textdomain-aliasing b/projects/packages/my-jetpack/changelog/add-assets-textdomain-aliasing new file mode 100644 index 0000000000000..c47cb18e82997 --- /dev/null +++ b/projects/packages/my-jetpack/changelog/add-assets-textdomain-aliasing @@ -0,0 +1,4 @@ +Significance: patch +Type: changed + +Updated package dependencies. diff --git a/projects/packages/my-jetpack/composer.json b/projects/packages/my-jetpack/composer.json index 405a18a8680d3..b0f175a7c1be6 100644 --- a/projects/packages/my-jetpack/composer.json +++ b/projects/packages/my-jetpack/composer.json @@ -5,7 +5,7 @@ "license": "GPL-2.0-or-later", "require": { "automattic/jetpack-admin-ui": "^0.2", - "automattic/jetpack-assets": "^1.14", + "automattic/jetpack-assets": "^1.15", "automattic/jetpack-connection": "^1.33" }, "require-dev": { diff --git a/projects/packages/my-jetpack/package.json b/projects/packages/my-jetpack/package.json index 8e6551d8535fc..a082b6ab7b12b 100644 --- a/projects/packages/my-jetpack/package.json +++ b/projects/packages/my-jetpack/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@automattic/jetpack-my-jetpack", - "version": "0.2.0", + "version": "0.2.1-alpha", "description": "WP Admin page with information and configuration shared among all Jetpack stand-alone plugins", "homepage": "https://jetpack.com", "bugs": { diff --git a/projects/packages/post-list/changelog/add-assets-textdomain-aliasing b/projects/packages/post-list/changelog/add-assets-textdomain-aliasing new file mode 100644 index 0000000000000..c47cb18e82997 --- /dev/null +++ b/projects/packages/post-list/changelog/add-assets-textdomain-aliasing @@ -0,0 +1,4 @@ +Significance: patch +Type: changed + +Updated package dependencies. diff --git a/projects/packages/post-list/composer.json b/projects/packages/post-list/composer.json index 0b80fbd5c32d8..b0a184571b839 100644 --- a/projects/packages/post-list/composer.json +++ b/projects/packages/post-list/composer.json @@ -4,7 +4,7 @@ "type": "jetpack-library", "license": "GPL-2.0-or-later", "require": { - "automattic/jetpack-assets": "^1.14" + "automattic/jetpack-assets": "^1.15" }, "require-dev": { "automattic/wordbless": "@dev", diff --git a/projects/packages/tracking/changelog/add-assets-textdomain-aliasing b/projects/packages/tracking/changelog/add-assets-textdomain-aliasing new file mode 100644 index 0000000000000..c47cb18e82997 --- /dev/null +++ b/projects/packages/tracking/changelog/add-assets-textdomain-aliasing @@ -0,0 +1,4 @@ +Significance: patch +Type: changed + +Updated package dependencies. diff --git a/projects/packages/tracking/composer.json b/projects/packages/tracking/composer.json index b738fa8ca1d92..d66aec048361e 100644 --- a/projects/packages/tracking/composer.json +++ b/projects/packages/tracking/composer.json @@ -4,7 +4,7 @@ "type": "jetpack-library", "license": "GPL-2.0-or-later", "require": { - "automattic/jetpack-assets": "^1.14", + "automattic/jetpack-assets": "^1.15", "automattic/jetpack-options": "^1.14", "automattic/jetpack-status": "^1.9", "automattic/jetpack-terms-of-service": "^1.9" diff --git a/projects/plugins/backup/changelog/add-assets-textdomain-aliasing b/projects/plugins/backup/changelog/add-assets-textdomain-aliasing new file mode 100644 index 0000000000000..9aa70e3ec1f75 --- /dev/null +++ b/projects/plugins/backup/changelog/add-assets-textdomain-aliasing @@ -0,0 +1,5 @@ +Significance: patch +Type: changed +Comment: Updated composer.lock. + + diff --git a/projects/plugins/backup/composer.json b/projects/plugins/backup/composer.json index 49ff040f6803f..a854229bd1677 100644 --- a/projects/plugins/backup/composer.json +++ b/projects/plugins/backup/composer.json @@ -4,7 +4,7 @@ "type": "library", "license": "GPL-2.0-or-later", "require": { - "automattic/jetpack-assets": "1.14.x-dev", + "automattic/jetpack-assets": "1.15.x-dev", "automattic/jetpack-admin-ui": "0.2.x-dev", "automattic/jetpack-autoloader": "2.10.x-dev", "automattic/jetpack-backup": "1.1.x-dev", diff --git a/projects/plugins/backup/composer.lock b/projects/plugins/backup/composer.lock index 3486dabda255b..6d1080dfc0a66 100644 --- a/projects/plugins/backup/composer.lock +++ b/projects/plugins/backup/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "fa38a8c879a0d2b449d25d78c78bd87a", + "content-hash": "dfc6087e671c788b7b1a59ea52062c6d", "packages": [ { "name": "automattic/jetpack-a8c-mc-stats", @@ -119,7 +119,7 @@ "dist": { "type": "path", "url": "../../packages/assets", - "reference": "79f3118db7691394547c3b21105a6e34efb919f0" + "reference": "eada358ef29aa64b35432c2baacb8975a4efdb31" }, "require": { "automattic/jetpack-constants": "^1.6" @@ -127,6 +127,7 @@ "require-dev": { "automattic/jetpack-changelogger": "^3.0", "brain/monkey": "2.6.1", + "wikimedia/testing-access-wrapper": "^1.0 | ^2.0", "yoast/phpunit-polyfills": "1.0.2" }, "type": "jetpack-library", @@ -137,7 +138,7 @@ "link-template": "https://github.com/Automattic/jetpack-assets/compare/v${old}...v${new}" }, "branch-alias": { - "dev-master": "1.14.x-dev" + "dev-master": "1.15.x-dev" } }, "autoload": { @@ -459,10 +460,10 @@ "dist": { "type": "path", "url": "../../packages/connection-ui", - "reference": "7e66dcb91fc171d2c6dd32ea7577383f46c9713a" + "reference": "324391767bedf3f55344993e8fca80248882dac6" }, "require": { - "automattic/jetpack-assets": "^1.14", + "automattic/jetpack-assets": "^1.15", "automattic/jetpack-connection": "^1.33", "automattic/jetpack-constants": "^1.6", "automattic/jetpack-device-detection": "^1.4", @@ -780,11 +781,11 @@ "dist": { "type": "path", "url": "../../packages/my-jetpack", - "reference": "ee2aa1d10634d764536a53ca1aa2fb5b39fefafe" + "reference": "9ec2ffc40a13fb67c5783d4e8888d10a692546ca" }, "require": { "automattic/jetpack-admin-ui": "^0.2", - "automattic/jetpack-assets": "^1.14", + "automattic/jetpack-assets": "^1.15", "automattic/jetpack-connection": "^1.33" }, "require-dev": { @@ -1222,10 +1223,10 @@ "dist": { "type": "path", "url": "../../packages/tracking", - "reference": "6ca70d07e77418303bb798c4cd430906af03509e" + "reference": "f727a1f82029f200bf7d4b4b63b080963edda25e" }, "require": { - "automattic/jetpack-assets": "^1.14", + "automattic/jetpack-assets": "^1.15", "automattic/jetpack-options": "^1.14", "automattic/jetpack-status": "^1.9", "automattic/jetpack-terms-of-service": "^1.9" diff --git a/projects/plugins/boost/changelog/add-assets-textdomain-aliasing b/projects/plugins/boost/changelog/add-assets-textdomain-aliasing new file mode 100644 index 0000000000000..9aa70e3ec1f75 --- /dev/null +++ b/projects/plugins/boost/changelog/add-assets-textdomain-aliasing @@ -0,0 +1,5 @@ +Significance: patch +Type: changed +Comment: Updated composer.lock. + + diff --git a/projects/plugins/boost/composer.lock b/projects/plugins/boost/composer.lock index 6c51b444e3d31..20317d2d59fb9 100644 --- a/projects/plugins/boost/composer.lock +++ b/projects/plugins/boost/composer.lock @@ -119,7 +119,7 @@ "dist": { "type": "path", "url": "../../packages/assets", - "reference": "79f3118db7691394547c3b21105a6e34efb919f0" + "reference": "eada358ef29aa64b35432c2baacb8975a4efdb31" }, "require": { "automattic/jetpack-constants": "^1.6" @@ -127,6 +127,7 @@ "require-dev": { "automattic/jetpack-changelogger": "^3.0", "brain/monkey": "2.6.1", + "wikimedia/testing-access-wrapper": "^1.0 | ^2.0", "yoast/phpunit-polyfills": "1.0.2" }, "type": "jetpack-library", @@ -137,7 +138,7 @@ "link-template": "https://github.com/Automattic/jetpack-assets/compare/v${old}...v${new}" }, "branch-alias": { - "dev-master": "1.14.x-dev" + "dev-master": "1.15.x-dev" } }, "autoload": { @@ -536,10 +537,10 @@ "dist": { "type": "path", "url": "../../packages/lazy-images", - "reference": "8ad70d0affc48b1fac679e0784bcf1c8d672b966" + "reference": "b3f5c18b00298f84d6f73c1f9c663ac0dbadb4a7" }, "require": { - "automattic/jetpack-assets": "^1.14", + "automattic/jetpack-assets": "^1.15", "automattic/jetpack-constants": "^1.6" }, "require-dev": { @@ -856,10 +857,10 @@ "dist": { "type": "path", "url": "../../packages/tracking", - "reference": "6ca70d07e77418303bb798c4cd430906af03509e" + "reference": "f727a1f82029f200bf7d4b4b63b080963edda25e" }, "require": { - "automattic/jetpack-assets": "^1.14", + "automattic/jetpack-assets": "^1.15", "automattic/jetpack-options": "^1.14", "automattic/jetpack-status": "^1.9", "automattic/jetpack-terms-of-service": "^1.9" diff --git a/projects/plugins/jetpack/changelog/add-assets-textdomain-aliasing b/projects/plugins/jetpack/changelog/add-assets-textdomain-aliasing new file mode 100644 index 0000000000000..a1c1831fa1ef7 --- /dev/null +++ b/projects/plugins/jetpack/changelog/add-assets-textdomain-aliasing @@ -0,0 +1,5 @@ +Significance: patch +Type: other +Comment: Updated composer.lock. + + diff --git a/projects/plugins/jetpack/composer.json b/projects/plugins/jetpack/composer.json index 067761314d20e..c91481e498374 100644 --- a/projects/plugins/jetpack/composer.json +++ b/projects/plugins/jetpack/composer.json @@ -13,7 +13,7 @@ "ext-openssl": "*", "automattic/jetpack-a8c-mc-stats": "1.4.x-dev", "automattic/jetpack-abtest": "1.9.x-dev", - "automattic/jetpack-assets": "1.14.x-dev", + "automattic/jetpack-assets": "1.15.x-dev", "automattic/jetpack-autoloader": "2.10.x-dev", "automattic/jetpack-backup": "1.1.x-dev", "automattic/jetpack-blocks": "1.4.x-dev", diff --git a/projects/plugins/jetpack/composer.lock b/projects/plugins/jetpack/composer.lock index f153e520585e2..80efa1f0e96e1 100644 --- a/projects/plugins/jetpack/composer.lock +++ b/projects/plugins/jetpack/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "7202f2201caddfbea5f6e11b164c9cdc", + "content-hash": "8dfee577cf2ac018576b8d72ae0e638b", "packages": [ { "name": "automattic/jetpack-a8c-mc-stats", @@ -177,7 +177,7 @@ "dist": { "type": "path", "url": "../../packages/assets", - "reference": "79f3118db7691394547c3b21105a6e34efb919f0" + "reference": "eada358ef29aa64b35432c2baacb8975a4efdb31" }, "require": { "automattic/jetpack-constants": "^1.6" @@ -185,6 +185,7 @@ "require-dev": { "automattic/jetpack-changelogger": "^3.0", "brain/monkey": "2.6.1", + "wikimedia/testing-access-wrapper": "^1.0 | ^2.0", "yoast/phpunit-polyfills": "1.0.2" }, "type": "jetpack-library", @@ -195,7 +196,7 @@ "link-template": "https://github.com/Automattic/jetpack-assets/compare/v${old}...v${new}" }, "branch-alias": { - "dev-master": "1.14.x-dev" + "dev-master": "1.15.x-dev" } }, "autoload": { @@ -611,10 +612,10 @@ "dist": { "type": "path", "url": "../../packages/connection-ui", - "reference": "7e66dcb91fc171d2c6dd32ea7577383f46c9713a" + "reference": "324391767bedf3f55344993e8fca80248882dac6" }, "require": { - "automattic/jetpack-assets": "^1.14", + "automattic/jetpack-assets": "^1.15", "automattic/jetpack-connection": "^1.33", "automattic/jetpack-constants": "^1.6", "automattic/jetpack-device-detection": "^1.4", @@ -932,11 +933,11 @@ "dist": { "type": "path", "url": "../../packages/jitm", - "reference": "fefedd87770502b5f74998fd91a8abeb1c11d627" + "reference": "c654b26c8a645bb8c3ab427d36f51fc24de46872" }, "require": { "automattic/jetpack-a8c-mc-stats": "^1.4", - "automattic/jetpack-assets": "^1.14", + "automattic/jetpack-assets": "^1.15", "automattic/jetpack-connection": "^1.33", "automattic/jetpack-device-detection": "^1.4", "automattic/jetpack-logo": "^1.5", @@ -1006,10 +1007,10 @@ "dist": { "type": "path", "url": "../../packages/lazy-images", - "reference": "8ad70d0affc48b1fac679e0784bcf1c8d672b966" + "reference": "b3f5c18b00298f84d6f73c1f9c663ac0dbadb4a7" }, "require": { - "automattic/jetpack-assets": "^1.14", + "automattic/jetpack-assets": "^1.15", "automattic/jetpack-constants": "^1.6" }, "require-dev": { @@ -1180,11 +1181,11 @@ "dist": { "type": "path", "url": "../../packages/my-jetpack", - "reference": "ee2aa1d10634d764536a53ca1aa2fb5b39fefafe" + "reference": "9ec2ffc40a13fb67c5783d4e8888d10a692546ca" }, "require": { "automattic/jetpack-admin-ui": "^0.2", - "automattic/jetpack-assets": "^1.14", + "automattic/jetpack-assets": "^1.15", "automattic/jetpack-connection": "^1.33" }, "require-dev": { @@ -1747,10 +1748,10 @@ "dist": { "type": "path", "url": "../../packages/tracking", - "reference": "6ca70d07e77418303bb798c4cd430906af03509e" + "reference": "f727a1f82029f200bf7d4b4b63b080963edda25e" }, "require": { - "automattic/jetpack-assets": "^1.14", + "automattic/jetpack-assets": "^1.15", "automattic/jetpack-options": "^1.14", "automattic/jetpack-status": "^1.9", "automattic/jetpack-terms-of-service": "^1.9" From 1092ed85efe819b969803c015e241fd6cf48bfd6 Mon Sep 17 00:00:00 2001 From: Jeremy Herve Date: Wed, 15 Dec 2021 14:43:27 +0100 Subject: [PATCH 05/22] Widgets: fix linting errors, part 2 (#22062) Co-authored-by: Brad Jorsch --- .../search/changelog/update-phpcs-widgets-2 | 4 + projects/packages/search/package.json | 2 +- .../search/src/class-template-tags.php | 6 +- .../jetpack/changelog/update-phpcs-widgets-2 | 4 + .../jetpack/modules/subscriptions/views.php | 6 +- .../jetpack/modules/widgets/authors.php | 2 +- .../jetpack/modules/widgets/blog-stats.php | 2 +- .../class-jetpack-instagram-widget.php | 2 +- .../jetpack/modules/widgets/contact-info.php | 2 +- .../modules/widgets/facebook-likebox.php | 2 +- .../jetpack/modules/widgets/flickr.php | 2 +- .../jetpack/modules/widgets/gallery.php | 2 +- .../jetpack/modules/widgets/goodreads.php | 2 +- .../modules/widgets/google-translate.php | 2 +- .../modules/widgets/gravatar-profile.php | 2 +- .../jetpack/modules/widgets/image-widget.php | 3 +- .../jetpack/modules/widgets/mailchimp.php | 62 +++-- .../migrate-to-core/gallery-widget.php | 125 +++++----- .../widgets/migrate-to-core/image-widget.php | 141 ++++++------ .../milestone/class-milestone-widget.php | 2 +- .../jetpack/modules/widgets/my-community.php | 77 ++++--- .../modules/widgets/rsslinks-widget.php | 206 ++++++++++------- .../jetpack/modules/widgets/search.php | 54 +++-- .../jetpack/modules/widgets/social-icons.php | 2 +- .../modules/widgets/social-media-icons.php | 60 ++--- .../jetpack/modules/widgets/top-posts.php | 216 ++++++++++++------ .../modules/widgets/twitter-timeline.php | 37 +-- .../modules/widgets/upcoming-events.php | 149 ++++++++---- ...lass.jetpack-display-posts-widget-base.php | 2 +- tools/phpcs-excludelist.json | 10 - 30 files changed, 738 insertions(+), 450 deletions(-) create mode 100644 projects/packages/search/changelog/update-phpcs-widgets-2 create mode 100644 projects/plugins/jetpack/changelog/update-phpcs-widgets-2 diff --git a/projects/packages/search/changelog/update-phpcs-widgets-2 b/projects/packages/search/changelog/update-phpcs-widgets-2 new file mode 100644 index 0000000000000..7f5f1ce06e32f --- /dev/null +++ b/projects/packages/search/changelog/update-phpcs-widgets-2 @@ -0,0 +1,4 @@ +Significance: patch +Type: changed + +Do not escape widget title value diff --git a/projects/packages/search/package.json b/projects/packages/search/package.json index cc899347bc558..31f0f3209da02 100644 --- a/projects/packages/search/package.json +++ b/projects/packages/search/package.json @@ -1,6 +1,6 @@ { "name": "jetpack-search", - "version": "0.3.0", + "version": "0.3.1-alpha", "description": "Package for Jetpack Search products", "main": "main.js", "directories": { diff --git a/projects/packages/search/src/class-template-tags.php b/projects/packages/search/src/class-template-tags.php index bc40103543334..2f66318dba9ba 100644 --- a/projects/packages/search/src/class-template-tags.php +++ b/projects/packages/search/src/class-template-tags.php @@ -225,7 +225,7 @@ public static function render_instant_filter( $filter ) { ?>
  • - > + > 1 ? '-' . Jetpack_Subscriptions_Widget::$instance_count : '' ) . '">' . esc_attr( $instance['title_following'] ) . '' . $after_title . "\n"; + echo $before_title . '' . $after_title . "\n"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } } else { if ( ! empty( $instance['title'] ) ) { - echo $before_title . '' . $after_title . "\n"; + echo $before_title . '' . $after_title . "\n"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } } } if ( self::is_jetpack() && empty( $instance['show_only_email_and_button'] ) ) { - echo $args['before_title'] . esc_attr( $instance['title'] ) . $args['after_title'] . "\n"; + echo $args['before_title'] . $instance['title'] . $args['after_title'] . "\n"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } } diff --git a/projects/plugins/jetpack/modules/widgets/authors.php b/projects/plugins/jetpack/modules/widgets/authors.php index 741856d84ccc1..402794fc9cc97 100644 --- a/projects/plugins/jetpack/modules/widgets/authors.php +++ b/projects/plugins/jetpack/modules/widgets/authors.php @@ -116,7 +116,7 @@ public function widget( $args, $instance ) { echo $args['before_widget']; /** This filter is documented in core/src/wp-includes/default-widgets.php */ $title = apply_filters( 'widget_title', $instance['title'] ); - echo $args['before_title'] . esc_html( $title ) . $args['after_title']; + echo $args['before_title'] . $title . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo '
      '; $default_post_type = 'post'; diff --git a/projects/plugins/jetpack/modules/widgets/blog-stats.php b/projects/plugins/jetpack/modules/widgets/blog-stats.php index b28718c99ce2b..7265d1142a26c 100644 --- a/projects/plugins/jetpack/modules/widgets/blog-stats.php +++ b/projects/plugins/jetpack/modules/widgets/blog-stats.php @@ -137,7 +137,7 @@ function widget( $args, $instance ) { echo $args['before_widget']; if ( ! empty( $title ) ) { - echo $args['before_title'] . esc_html( $title ) . $args['after_title']; + echo $args['before_title'] . $title . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } // Get the Site Stats. diff --git a/projects/plugins/jetpack/modules/widgets/class-jetpack-instagram-widget.php b/projects/plugins/jetpack/modules/widgets/class-jetpack-instagram-widget.php index 1ae6ebc0eecfb..c334f0fb21b0b 100644 --- a/projects/plugins/jetpack/modules/widgets/class-jetpack-instagram-widget.php +++ b/projects/plugins/jetpack/modules/widgets/class-jetpack-instagram-widget.php @@ -284,7 +284,7 @@ public function widget( $args, $instance ) { if ( ! empty( $instance['title'] ) ) { echo $args['before_title']; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped - echo esc_html( $instance['title'] ); + echo $instance['title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo $args['after_title']; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } diff --git a/projects/plugins/jetpack/modules/widgets/contact-info.php b/projects/plugins/jetpack/modules/widgets/contact-info.php index fe37ad69577df..de9afc9d5afad 100644 --- a/projects/plugins/jetpack/modules/widgets/contact-info.php +++ b/projects/plugins/jetpack/modules/widgets/contact-info.php @@ -108,7 +108,7 @@ public function widget( $args, $instance ) { echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped if ( '' !== $instance['title'] ) { - echo $args['before_title'] . esc_html( $instance['title'] ) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + echo $args['before_title'] . $instance['title'] . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } /** diff --git a/projects/plugins/jetpack/modules/widgets/facebook-likebox.php b/projects/plugins/jetpack/modules/widgets/facebook-likebox.php index 5059d18e3fccd..c7e38e6416c7c 100644 --- a/projects/plugins/jetpack/modules/widgets/facebook-likebox.php +++ b/projects/plugins/jetpack/modules/widgets/facebook-likebox.php @@ -122,7 +122,7 @@ public function widget( $args, $instance ) { if ( ! empty( $title ) ) : echo $before_title; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped - $likebox_widget_title = '' . esc_html( $title ) . ''; + $likebox_widget_title = '' . $title . ''; /** * Filter Facebook Likebox's widget title. * diff --git a/projects/plugins/jetpack/modules/widgets/flickr.php b/projects/plugins/jetpack/modules/widgets/flickr.php index ebf0fc2c3a1f6..9fb809629c353 100644 --- a/projects/plugins/jetpack/modules/widgets/flickr.php +++ b/projects/plugins/jetpack/modules/widgets/flickr.php @@ -156,7 +156,7 @@ public function widget( $args, $instance ) { ); } } else { - echo $args['before_title'] . esc_html( $instance['title'] ) . $args['after_title']; + echo $args['before_title'] . $instance['title'] . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped require( dirname( __FILE__ ) . '/flickr/widget.php' ); } echo $args['after_widget']; diff --git a/projects/plugins/jetpack/modules/widgets/gallery.php b/projects/plugins/jetpack/modules/widgets/gallery.php index 58655b934d070..65c3a60cd8713 100644 --- a/projects/plugins/jetpack/modules/widgets/gallery.php +++ b/projects/plugins/jetpack/modules/widgets/gallery.php @@ -107,7 +107,7 @@ public function widget( $args, $instance ) { $title = apply_filters( 'widget_title', $instance['title'] ); if ( $title ) { - echo $before_title . esc_html( $title ) . $after_title . "\n"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + echo $before_title . $title . $after_title . "\n"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } echo '
      ' . "\n"; diff --git a/projects/plugins/jetpack/modules/widgets/goodreads.php b/projects/plugins/jetpack/modules/widgets/goodreads.php index d6ea651549b7f..f9b3db769011f 100644 --- a/projects/plugins/jetpack/modules/widgets/goodreads.php +++ b/projects/plugins/jetpack/modules/widgets/goodreads.php @@ -80,7 +80,7 @@ function widget( $args, $instance ) { } echo $args['before_widget']; - echo $args['before_title'] . $title . $args['after_title']; + echo $args['before_title'] . $title . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped $goodreads_url = 'https://www.goodreads.com/review/custom_widget/' . urlencode( $instance['user_id'] ) . '.' . urlencode( $instance['title'] ) . ':%20' . urlencode( $instance['shelf'] ) . '?cover_position=&cover_size=small&num_books=5&order=d&shelf=' . urlencode( $instance['shelf'] ) . '&sort=date_added&widget_bg_transparent=&widget_id=' . esc_attr( $this->goodreads_widget_id ); diff --git a/projects/plugins/jetpack/modules/widgets/google-translate.php b/projects/plugins/jetpack/modules/widgets/google-translate.php index b00f13cd85174..0e270677af779 100644 --- a/projects/plugins/jetpack/modules/widgets/google-translate.php +++ b/projects/plugins/jetpack/modules/widgets/google-translate.php @@ -146,7 +146,7 @@ public function widget( $args, $instance ) { echo $args['before_widget']; if ( ! empty( $title ) ) { - echo $args['before_title'] . esc_html( $title ) . $args['after_title']; + echo $args['before_title'] . $title . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } echo '
      '; echo $args['after_widget']; diff --git a/projects/plugins/jetpack/modules/widgets/gravatar-profile.php b/projects/plugins/jetpack/modules/widgets/gravatar-profile.php index b9091bbe9aed6..d2b13d12fcd42 100644 --- a/projects/plugins/jetpack/modules/widgets/gravatar-profile.php +++ b/projects/plugins/jetpack/modules/widgets/gravatar-profile.php @@ -65,7 +65,7 @@ function widget( $args, $instance ) { if ( current_user_can( 'edit_theme_options' ) ) { echo $args['before_widget']; if ( ! empty( $title ) ) { - echo $args['before_title'] . $title . $args['after_title']; + echo $args['before_title'] . $title . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } echo '

      ' . sprintf( __( 'You need to select what to show in this Gravatar Profile widget.', 'jetpack' ), admin_url( 'widgets.php' ) ) . '

      '; echo $args['after_widget']; diff --git a/projects/plugins/jetpack/modules/widgets/image-widget.php b/projects/plugins/jetpack/modules/widgets/image-widget.php index d455b4c2993ab..e498f577e6766 100644 --- a/projects/plugins/jetpack/modules/widgets/image-widget.php +++ b/projects/plugins/jetpack/modules/widgets/image-widget.php @@ -67,9 +67,8 @@ public function widget( $args, $instance ) { /** This filter is documented in core/src/wp-includes/default-widgets.php */ $title = apply_filters( 'widget_title', $instance['title'] ); - if ( $title ) { - echo $args['before_title'] . esc_html( $title ) . $args['after_title']; + echo $args['before_title'] . $title . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } if ( '' != $instance['img_url'] ) { diff --git a/projects/plugins/jetpack/modules/widgets/mailchimp.php b/projects/plugins/jetpack/modules/widgets/mailchimp.php index a2aff1ba7b254..6f43b4db3d148 100644 --- a/projects/plugins/jetpack/modules/widgets/mailchimp.php +++ b/projects/plugins/jetpack/modules/widgets/mailchimp.php @@ -1,4 +1,10 @@ - '' ) ); // Regular expresion that will match maichimp shortcode. @@ -60,23 +68,21 @@ function widget( $args, $instance ) { do_action( 'jetpack_stats_extra', 'widget_view', 'mailchimp_subscriber_popup' ); } - /** * Deals with the settings when they are saved by the admin. * - * @param array $new_instance New configuration values - * @param array $old_instance Old configuration values + * @param array $new_instance New configuration values. + * @param array $old_instance Old configuration values. * * @return array */ - function update( $new_instance, $old_instance ) { + public function update( $new_instance, $old_instance ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable $instance = array(); $instance['code'] = MailChimp_Subscriber_Popup::reversal( $new_instance['code'] ); return $instance; } - /** * Displays the form for this widget on the Widgets page of the WP Admin area. * @@ -84,20 +90,30 @@ function update( $new_instance, $old_instance ) { * * @return void */ - function form( $instance ) { + public function form( $instance ) { $instance = wp_parse_args( $instance, array( 'code' => '' ) ); - ?> -

      - - -

      + $label = sprintf( + wp_kses( + /* Translators: %s is a link to the MailChimp support docs. */ + __( 'Code: ( ? )', 'jetpack' ), + array( + 'a' => array( + 'href' => array(), + 'target' => array(), + ), + ) + ), + 'https://en.support.wordpress.com/mailchimp/' + ); -

      ', + esc_attr( $this->get_field_id( 'code' ) ), + esc_attr( $this->get_field_name( 'code' ) ), + esc_textarea( $instance['code'] ), + $label // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- escaped above. + ); } - } - } diff --git a/projects/plugins/jetpack/modules/widgets/migrate-to-core/gallery-widget.php b/projects/plugins/jetpack/modules/widgets/migrate-to-core/gallery-widget.php index b31c7d91b7652..ffee5b5b7a686 100644 --- a/projects/plugins/jetpack/modules/widgets/migrate-to-core/gallery-widget.php +++ b/projects/plugins/jetpack/modules/widgets/migrate-to-core/gallery-widget.php @@ -6,22 +6,23 @@ * * @package automattic/jetpack */ + /** * Migrates all active instances of Jetpack's Gallery widget to Core's Media Gallery widget. */ function jetpack_migrate_gallery_widget() { - // Only trigger the migration from wp-admin and outside unit tests + // Only trigger the migration from wp-admin and outside unit tests. if ( ! is_admin() || defined( 'PHPUNIT_JETPACK_TESTSUITE' ) ) { return; } - // Only migrate if the new widget is available and we haven't yet migrated + // Only migrate if the new widget is available and we haven't yet migrated. if ( ! class_exists( 'WP_Widget_Media_Gallery' ) || Jetpack_Options::get_option( 'gallery_widget_migration' ) ) { return; } - $old_widgets = get_option( 'widget_gallery', array() ); - $media_gallery = get_option( 'widget_media_gallery', array() ); + $old_widgets = get_option( 'widget_gallery', array() ); + $media_gallery = get_option( 'widget_media_gallery', array() ); $sidebars_widgets = wp_get_sidebars_widgets(); // Array to store legacy widget ids in to unregister on success. @@ -30,10 +31,13 @@ function jetpack_migrate_gallery_widget() { $old_widgets = array_filter( $old_widgets, 'jetpack_migrate_gallery_widget_is_importable' ); foreach ( $old_widgets as $id => $widget ) { $new_id = $id; - // Try to get an unique id for the new type of widget. - // It may be the case that the user has already created a core Gallery Widget - // before the migration begins. (Maybe Jetpack was deactivated during core's upgrade). - for( $i = 0; $i < 10 && in_array( $new_id, array_keys( $media_gallery ) ); $i++, $new_id++ ); + + /* + * Try to get an unique id for the new type of widget. + * It may be the case that the user has already created a core Gallery Widget + * before the migration begins. (Maybe Jetpack was deactivated during core's upgrade). + */ + for ( $i = 0; $i < 10 && array_key_exists( $new_id, array( $media_gallery ) ); $i++, $new_id++ ); // phpcs:ignore Generic.CodeAnalysis.ForLoopWithTestFunctionCall.NotAllowed $widget_copy = jetpack_migrate_gallery_widget_upgrade_widget( $widget ); @@ -64,24 +68,35 @@ function jetpack_migrate_gallery_widget() { wp_set_sidebars_widgets( $sidebars_widgets ); // Log if we migrated all, or some for this site. - foreach ( $widgets_to_unregister as $w ) { + foreach ( $widgets_to_unregister as $w ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable jetpack_migrate_gallery_widget_bump_stats( 'gallery-widget-migrated' ); } - // We need to refresh on widgets page for changes to take effect. - // The jetpack_refresh_on_widget_page function is already defined in migrate-to-core/image-widget.php + /* + * We need to refresh on widgets page for changes to take effect. + * The jetpack_refresh_on_widget_page function is already defined + * in migrate-to-core/image-widget.php + */ add_action( 'current_screen', 'jetpack_refresh_on_widget_page' ); } Jetpack_Options::update_option( 'gallery_widget_migration', true ); } +/** + * Check if the widget can be imported. + * + * @param array $widget One of the Jetpack Gallery widgets to be transformed into a new Core Media Gallery Widget. + */ function jetpack_migrate_gallery_widget_is_importable( $widget ) { // Can be caused by instantiating but not populating a widget in the Customizer. if ( empty( $widget ) ) { return false; } - // The array as stored in the option constains two keys and one - // is a string `_multiwidget` which does not represent a widget, so we skip it + + /* + * The array as stored in the option constains two keys and one + * is a string `_multiwidget` which does not represent a widget, so we skip it + */ if ( ! is_array( $widget ) ) { return false; } @@ -92,28 +107,28 @@ function jetpack_migrate_gallery_widget_is_importable( $widget ) { * Returns a transformed version of the Gallery Widget. * Will return null if the widget is either empty, is not an array or has more keys than expected * - * @param $widget One of the Jetpack Gallery widgets to be transformed into a new Core Media Gallery Widget + * @param array $widget One of the Jetpack Gallery widgets to be transformed into a new Core Media Gallery Widget. * * @return array|null */ function jetpack_migrate_gallery_widget_upgrade_widget( $widget ) { $allowed_keys = array( - 'ids' => '', - 'link' => '', - 'title' => '', - 'type' => '', - 'random' => '', + 'ids' => '', + 'link' => '', + 'title' => '', + 'type' => '', + 'random' => '', 'conditions' => '', ); $default_data = array( - 'columns' => 3, - 'ids' => array(), - 'link_type' => '', + 'columns' => 3, + 'ids' => array(), + 'link_type' => '', 'orderby_random' => false, - 'size' => 'thumbnail', - 'title' => '', - 'type' => '', + 'size' => 'thumbnail', + 'title' => '', + 'type' => '', ); if ( ! jetpack_migrate_gallery_widget_is_importable( $widget ) ) { @@ -121,30 +136,37 @@ function jetpack_migrate_gallery_widget_upgrade_widget( $widget ) { } // Ensure widget has no keys other than those expected. // Not all widgets have conditions, so lets add it in. - $widget_copy = array_merge( array( 'conditions' => null ), $widget ); + $widget_copy = array_merge( array( 'conditions' => null ), $widget ); $non_allowed_keys = array_diff_key( $widget_copy, $allowed_keys ); if ( count( $non_allowed_keys ) > 0 ) { jetpack_migrate_gallery_widget_bump_stats( 'extra-key' ); // Log the names of the keys not in our allowed list. foreach ( $non_allowed_keys as $key => $value ) { - jetpack_migrate_gallery_widget_bump_stats( "extra-key-$key", "migration-extra-key" ); + jetpack_migrate_gallery_widget_bump_stats( "extra-key-$key", 'migration-extra-key' ); } } - $widget_copy = array_merge( $default_data, $widget, array( - // ids in Jetpack's Gallery are a string of comma-separated values. - // Core's Media Gallery Widget stores ids in an array - 'ids' => explode( ',', $widget['ids'] ), - 'link_type' => $widget['link'], - 'orderby_random' => isset( $widget['random'] ) && $widget['random'] === 'on', - ) ); + $widget_copy = array_merge( + $default_data, + $widget, + array( + // ids in Jetpack's Gallery are a string of comma-separated values. + // Core's Media Gallery Widget stores ids in an array. + 'ids' => explode( ',', $widget['ids'] ), + 'link_type' => $widget['link'], + 'orderby_random' => isset( $widget['random'] ) && 'on' === $widget['random'], + ) + ); - // Unsetting old widget fields - $widget_copy = array_diff_key( $widget_copy, array( - 'link' => false, - 'random' => false, - ) ); + // Unsetting old widget fields. + $widget_copy = array_diff_key( + $widget_copy, + array( + 'link' => false, + 'random' => false, + ) + ); return $widget_copy; } @@ -152,21 +174,23 @@ function jetpack_migrate_gallery_widget_upgrade_widget( $widget ) { /** * Replaces the references to Jetpack Gallery Widget in the sidebars for references to the new version of the widget * - * @param $sidebars_widgets The sidebar widgets array to update - * @param $id Old id of the widget (basically its index in the array ) - * @param $new_id New id that will be using on the sidebar as a new widget + * @param array $sidebars_widgets The sidebar widgets array to update. + * @param string $id Old id of the widget (basically its index in the array ). + * @param string $new_id New id that will be using on the sidebar as a new widget. * - * @return mixed Updated sidebar widgets array + * @return mixed Updated sidebar widgets array */ function jetpack_migrate_gallery_widget_update_sidebars( $sidebars_widgets, $id, $new_id ) { foreach ( $sidebars_widgets as $sidebar => $widgets ) { - if ( - is_array( $widgets ) - && false !== ( $key = array_search( "gallery-{$id}", $widgets, true ) ) - ) { + $key = is_array( $widgets ) ? array_search( "gallery-{$id}", $widgets, true ) : false; + + if ( false !== $key ) { $sidebars_widgets[ $sidebar ][ $key ] = "media_gallery-{$new_id}"; - // Check if the inactive widgets sidebar exists - // Related: https://core.trac.wordpress.org/ticket/14893 + + /* + * Check if the inactive widgets sidebar exists + * Related: https://core.trac.wordpress.org/ticket/14893 + */ if ( ! isset( $sidebars_widgets['wp_inactive_widgets'] ) || ! is_array( $sidebars_widgets['wp_inactive_widgets'] ) ) { $sidebars_widgets['wp_inactive_widgets'] = array(); } @@ -190,9 +214,8 @@ function jetpack_migrate_gallery_widget_bump_stats( $bin, $group = 'widget-migra } else { // $group is prepended with 'jetpack-' $jetpack = Jetpack::init(); - $jetpack->stat( $group, $bin ) ; + $jetpack->stat( $group, $bin ); } } - add_action( 'widgets_init', 'jetpack_migrate_gallery_widget' ); diff --git a/projects/plugins/jetpack/modules/widgets/migrate-to-core/image-widget.php b/projects/plugins/jetpack/modules/widgets/migrate-to-core/image-widget.php index ee74745fe879c..06f317ff496d3 100644 --- a/projects/plugins/jetpack/modules/widgets/migrate-to-core/image-widget.php +++ b/projects/plugins/jetpack/modules/widgets/migrate-to-core/image-widget.php @@ -11,38 +11,38 @@ * Migrates all active instances of Jetpack's image widget to Core's media image widget. */ function jetpack_migrate_image_widget() { - // Only trigger the migration from wp-admin + // Only trigger the migration from wp-admin. if ( ! is_admin() ) { return; } - // Only migrate if the new widget is available and we haven't yet migrated + // Only migrate if the new widget is available and we haven't yet migrated. if ( ! class_exists( 'WP_Widget_Media_Image' ) || Jetpack_Options::get_option( 'image_widget_migration' ) ) { return; } $default_data = array( - 'attachment_id' => 0, - 'url' => '', - 'title' => '', - 'size' => 'custom', - 'width' => 0, - 'height' => 0, - 'align' => 'none', - 'caption' => '', - 'alt' => '', - 'link_type' => '', - 'link_url' => '', - 'image_classes' => '', - 'link_classes' => '', - 'link_rel' => '', - 'image_title' => '', + 'attachment_id' => 0, + 'url' => '', + 'title' => '', + 'size' => 'custom', + 'width' => 0, + 'height' => 0, + 'align' => 'none', + 'caption' => '', + 'alt' => '', + 'link_type' => '', + 'link_url' => '', + 'image_classes' => '', + 'link_classes' => '', + 'link_rel' => '', + 'image_title' => '', 'link_target_blank' => false, - 'conditions' => null, + 'conditions' => null, ); - $old_widgets = get_option( 'widget_image', array() ); - $media_image = get_option( 'widget_media_image', array() ); + $old_widgets = get_option( 'widget_image', array() ); + $media_image = get_option( 'widget_media_image', array() ); $sidebars_widgets = wp_get_sidebars_widgets(); // Persist old and current widgets in backup table. @@ -71,7 +71,7 @@ function jetpack_migrate_image_widget() { // Ensure widget has no keys other than those expected. // Not all widgets have conditions, so lets add it in. - $widget_copy = array_merge( array( 'conditions' => null ), $widget ); + $widget_copy = array_merge( array( 'conditions' => null ), $widget ); $non_allowed_keys = array_diff_key( $widget_copy, array( @@ -90,30 +90,37 @@ function jetpack_migrate_image_widget() { ); if ( count( $non_allowed_keys ) > 0 ) { - // skipping the widget in question + // skipping the widget in question. continue; } - $media_image[ $id ] = array_merge( $default_data, $widget, array( - 'alt' => $widget['alt_text'], - 'height' => $widget['img_height'], - 'image_classes' => ! empty( $widget['align'] ) ? 'align' . $widget['align'] : '', - 'image_title' => $widget['img_title'], - 'link_url' => $widget['link'], - 'url' => $widget['img_url'], - 'width' => $widget['img_width'], - ) ); - - // Unsetting old widget fields - $media_image[ $id ] = array_diff_key( $media_image[ $id ], array( - 'align' => false, - 'alt_text' => false, - 'img_height' => false, - 'img_title' => false, - 'img_url' => false, - 'img_width' => false, - 'link' => false, - ) ); + $media_image[ $id ] = array_merge( + $default_data, + $widget, + array( + 'alt' => $widget['alt_text'], + 'height' => $widget['img_height'], + 'image_classes' => ! empty( $widget['align'] ) ? 'align' . $widget['align'] : '', + 'image_title' => $widget['img_title'], + 'link_url' => $widget['link'], + 'url' => $widget['img_url'], + 'width' => $widget['img_width'], + ) + ); + + // Unsetting old widget fields. + $media_image[ $id ] = array_diff_key( + $media_image[ $id ], + array( + 'align' => false, + 'alt_text' => false, + 'img_height' => false, + 'img_title' => false, + 'img_url' => false, + 'img_width' => false, + 'link' => false, + ) + ); // Check if the image is in the media library. $image_basename = basename( $widget['img_url'] ); @@ -122,32 +129,34 @@ function jetpack_migrate_image_widget() { continue; } - $attachment_ids = get_posts( array( - 'fields' => 'ids', - 'meta_query' => array( - array( - 'value' => basename( $image_basename ), - 'compare' => 'LIKE', - 'key' => '_wp_attachment_metadata', + $attachment_ids = get_posts( + array( + 'fields' => 'ids', + 'meta_query' => array( + array( + 'value' => basename( $image_basename ), + 'compare' => 'LIKE', + 'key' => '_wp_attachment_metadata', + ), ), - ), - 'post_status' => 'inherit', - 'post_type' => 'attachment', - 'suppress_filters' => false, - ) ); + 'post_status' => 'inherit', + 'post_type' => 'attachment', + 'suppress_filters' => false, + ) + ); foreach ( $attachment_ids as $attachment_id ) { $image_meta = wp_get_attachment_metadata( $attachment_id ); // Is it a full size image? $image_path_pieces = explode( '/', $image_meta['file'] ); - if ( $image_basename === array_pop( $image_path_pieces ) ) { + if ( array_pop( $image_path_pieces ) === $image_basename ) { $media_image[ $id ]['attachment_id'] = $attachment_id; // Set correct size if dimensions fit. if ( - $media_image[ $id ]['width'] == $image_meta['width'] || - $media_image[ $id ]['height'] == $image_meta['height'] + $media_image[ $id ]['width'] == $image_meta['width'] || // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison + $media_image[ $id ]['height'] == $image_meta['height'] // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison ) { $media_image[ $id ]['size'] = 'full'; } @@ -156,13 +165,13 @@ function jetpack_migrate_image_widget() { // Is it a down-sized image? foreach ( $image_meta['sizes'] as $size => $image ) { - if ( false !== array_search( $image_basename, $image ) ) { + if ( false !== array_search( $image_basename, $image, true ) ) { $media_image[ $id ]['attachment_id'] = $attachment_id; // Set correct size if dimensions fit. if ( - $media_image[ $id ]['width'] == $image['width'] || - $media_image[ $id ]['height'] == $image['height'] + $media_image[ $id ]['width'] == $image['width'] || // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison + $media_image[ $id ]['height'] == $image['height'] // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison ) { $media_image[ $id ]['size'] = $size; } @@ -176,10 +185,9 @@ function jetpack_migrate_image_widget() { } foreach ( $sidebars_widgets as $sidebar => $widgets ) { - if ( - is_array( $widgets ) - && false !== ( $key = array_search( "image-{$id}", $widgets, true ) ) - ) { + $key = is_array( $widgets ) ? array_search( "image-{$id}", $widgets, true ) : false; + + if ( false !== $key ) { $sidebars_widgets[ $sidebar ][ $key ] = "media_image-{$id}"; } } @@ -215,6 +223,11 @@ function jetpack_migrate_image_widget() { } add_action( 'widgets_init', 'jetpack_migrate_image_widget' ); +/** + * Refresh the widgets page to save changes + * + * @param WP_Screen $current Current WP_Screen object. + */ function jetpack_refresh_on_widget_page( $current ) { if ( 'widgets' === $current->base ) { wp_safe_redirect( admin_url( 'widgets.php' ) ); diff --git a/projects/plugins/jetpack/modules/widgets/milestone/class-milestone-widget.php b/projects/plugins/jetpack/modules/widgets/milestone/class-milestone-widget.php index 3ce97d248ab4e..a28e85f57f9e5 100644 --- a/projects/plugins/jetpack/modules/widgets/milestone/class-milestone-widget.php +++ b/projects/plugins/jetpack/modules/widgets/milestone/class-milestone-widget.php @@ -212,7 +212,7 @@ public function widget( $args, $instance ) { /** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */ $title = apply_filters( 'widget_title', $instance['title'] ); if ( ! empty( $title ) ) { - echo $args['before_title'] . esc_html( $title ) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + echo $args['before_title'] . $title . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } $widget_id = ! empty( $args['widget_id'] ) ? $args['widget_id'] : 'milestone_widget'; diff --git a/projects/plugins/jetpack/modules/widgets/my-community.php b/projects/plugins/jetpack/modules/widgets/my-community.php index 13efc1d5e4a20..c16baf21ccdc4 100644 --- a/projects/plugins/jetpack/modules/widgets/my-community.php +++ b/projects/plugins/jetpack/modules/widgets/my-community.php @@ -1,4 +1,4 @@ -default_title; } - $number = isset( $instance['number'] ) ? $instance['number'] : 10; - if ( ! in_array( $number, array( 10, 50 ) ) ) { + $number = isset( $instance['number'] ) ? (int) $instance['number'] : 10; + if ( ! in_array( $number, array( 10, 50 ), true ) ) { $number = 10; } @@ -85,35 +85,35 @@ function form( $instance ) { ?>

      - - + +

        -
      • -
      • +
      • +

      -

      -

      -

      @@ -131,15 +131,15 @@ function form( $instance ) { * * @return array Updated safe values to be saved. */ - function update( $new_instance, $old_instance ) { + public function update( $new_instance, $old_instance ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable $instance = array(); $instance['title'] = wp_kses( $new_instance['title'], array() ); if ( $instance['title'] === $this->default_title ) { - $instance['title'] = false; // Store as false in case of language change + $instance['title'] = false; // Store as false in case of language change. } $instance['number'] = (int) $new_instance['number']; - if ( ! in_array( $instance['number'], array( 10, 50 ) ) ) { + if ( ! in_array( $instance['number'], array( 10, 50 ), true ) ) { $instance['number'] = 10; } @@ -160,9 +160,10 @@ function update( $new_instance, $old_instance ) { * @param array $args Widget arguments. * @param array $instance Saved values from database. */ - function widget( $args, $instance ) { + public function widget( $args, $instance ) { $instance = wp_parse_args( - $instance, array( + $instance, + array( 'title' => false, 'number' => true, 'include_likers' => true, @@ -177,13 +178,12 @@ function widget( $args, $instance ) { $title = $this->default_title; } + echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + /** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */ $title = apply_filters( 'widget_title', $title ); - - echo $args['before_widget']; - if ( ! empty( $title ) ) { - echo $args['before_title'] . $title . $args['after_title']; + echo $args['before_title'] . $title . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } $transient_name = "$this->id-v2-{$instance['number']}" . (int) $instance['include_likers'] . (int) $instance['include_followers'] . (int) $instance['include_commenters']; @@ -196,9 +196,9 @@ function widget( $args, $instance ) { set_transient( $transient_name, $my_community, self::$expiration ); } - echo $my_community; + echo $my_community; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped - echo $args['after_widget']; + echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped /** This action is documented in modules/widgets/gravatar-profile.php */ do_action( 'jetpack_stats_extra', 'widget_view', 'my_community' ); @@ -209,11 +209,11 @@ function widget( $args, $instance ) { * * @since 4.0 * - * @param array $query + * @param array $query Saved widget values from database. * * @return string */ - function get_community( $query ) { + private function get_community( $query ) { $members = $this->fetch_remote_community( $query ); if ( ! empty( $members ) ) { @@ -223,10 +223,10 @@ function get_community( $query ) { foreach ( $members as $member ) { $my_community .= sprintf( '
    • %s
    • ', - esc_url( $member->profile_URL ), + esc_url( $member->profile_URL ), // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase esc_attr( $member->name ), esc_attr( $member->name ), - esc_url( $member->avatar_URL ) + esc_url( $member->avatar_URL ) // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase ); } @@ -236,10 +236,12 @@ function get_community( $query ) { if ( current_user_can( 'edit_theme_options' ) ) { $my_community = '

      ' . wp_kses( sprintf( + /* Translators: 1. link to the widgets settings screen. 2. link to support document. */ __( 'There are no users to display in this My Community widget. Want more traffic?', 'jetpack' ), admin_url( 'widgets.php' ), esc_url( Redirect::get_url( 'jetpack-support-getting-more-views-and-traffic' ) ) - ), array( 'a' => array( 'href' => true ) ) + ), + array( 'a' => array( 'href' => true ) ) ) . '

      '; } else { $my_community = '

      ' . esc_html__( "I'm just starting out; leave me a comment or a like :)", 'jetpack' ) . '

      '; @@ -254,11 +256,11 @@ function get_community( $query ) { * * @since 4.0 * - * @param $query + * @param array $query Saved widget values from database. * * @return array */ - function fetch_remote_community( $query ) { + private function fetch_remote_community( $query ) { $jetpack_blog_id = Jetpack_Options::get_option( 'id' ); $url = add_query_arg( array( @@ -296,5 +298,4 @@ function jetpack_my_community_init() { register_widget( 'Jetpack_My_Community_Widget' ); } } - add_action( 'widgets_init', 'jetpack_my_community_init' ); diff --git a/projects/plugins/jetpack/modules/widgets/rsslinks-widget.php b/projects/plugins/jetpack/modules/widgets/rsslinks-widget.php index 60fb9e54796f1..303c0c6dfde0b 100644 --- a/projects/plugins/jetpack/modules/widgets/rsslinks-widget.php +++ b/projects/plugins/jetpack/modules/widgets/rsslinks-widget.php @@ -1,14 +1,26 @@ - 'widget_rss_links', 'description' => __( "Links to your blog's RSS feeds", 'jetpack' ), @@ -41,7 +53,7 @@ public function widget( $args, $instance ) { echo $before_widget; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped if ( $title ) { - echo $before_title . esc_html( $title ) . $after_title; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + echo $before_title . $title . $after_title; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } if ( 'text' === $instance['format'] ) { @@ -49,12 +61,12 @@ public function widget( $args, $instance ) { } if ( 'posts' === $instance['display'] ) { - $this->_rss_link( 'posts', $instance ); + $this->rss_link( 'posts', $instance ); } elseif ( 'comments' === $instance['display'] ) { - $this->_rss_link( 'comments', $instance ); + $this->rss_link( 'comments', $instance ); } elseif ( 'posts-comments' === $instance['display'] ) { - $this->_rss_link( 'posts', $instance ); - $this->_rss_link( 'comments', $instance ); + $this->rss_link( 'posts', $instance ); + $this->rss_link( 'comments', $instance ); } if ( 'text' === $instance['format'] ) { @@ -73,7 +85,7 @@ public function widget( $args, $instance ) { * * @return array Array of default values for the Widget's options */ - function defaults() { + public function defaults() { return array( 'title' => '', 'display' => 'posts-comments', @@ -81,7 +93,17 @@ function defaults() { ); } - function update( $new_instance, $old_instance ) { + /** + * Sanitize widget form values as they are saved. + * + * @see WP_Widget::update() + * + * @param array $new_instance Values just sent to be saved. + * @param array $old_instance Previously saved values from database. + * + * @return array Updated safe values to be saved. + */ + public function update( $new_instance, $old_instance ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable $instance = $old_instance; $instance['title'] = wp_filter_nohtml_kses( $new_instance['title'] ); @@ -93,7 +115,16 @@ function update( $new_instance, $old_instance ) { return $instance; } - function form( $instance ) { + /** + * Back end widget form. + * + * @see WP_Widget::form() + * + * @param array $instance Previously saved values from database. + * + * @return string|void + */ + public function form( $instance ) { $instance = wp_parse_args( (array) $instance, $this->defaults() ); $title = stripslashes( $instance['title'] ); @@ -102,8 +133,8 @@ function form( $instance ) { $image_size = isset( $instance['imagesize'] ) ? $instance['imagesize'] : 0; $image_color = isset( $instance['imagecolor'] ) ? $instance['imagecolor'] : 'red'; - echo '

      '; $displays = array( @@ -111,11 +142,11 @@ function form( $instance ) { 'comments' => __( 'Comments', 'jetpack' ), 'posts-comments' => __( 'Posts & Comments', 'jetpack' ), ); - echo '

      '; - echo ''; } - function _rss_link( $type, $args ) { - if ( 'posts' == $type ) { - $type_text = __( 'Posts', 'jetpack' ); - $rss_type = 'rss2_url'; - } elseif ( 'comments' == $type ) { - $type_text = __( 'Comments', 'jetpack' ); - $rss_type = 'comments_rss2_url'; + /** + * Output a link with a link to the feed. + * + * @param string $type Widget type (posts or comments). + * @param array $args Widget arguments. + */ + private function rss_link( $type, $args ) { + if ( 'posts' === $type ) { + $subscribe_to = esc_html__( 'Subscribe to posts', 'jetpack' ); + $link_text = esc_html__( 'RSS - Posts', 'jetpack' ); + $rss_type = 'rss2_url'; + } elseif ( 'comments' === $type ) { + $subscribe_to = esc_html__( 'Subscribe to comments', 'jetpack' ); + $link_text = esc_html__( 'RSS - Comments', 'jetpack' ); + $rss_type = 'comments_rss2_url'; } - $subscribe_to = sprintf( __( 'Subscribe to %s', 'jetpack' ), $type_text ); - - $link_item = ''; - $format = $args['format']; - /** * Filters the target link attribute for the RSS link in the RSS widget. * @@ -210,42 +244,60 @@ function _rss_link( $type, $args ) { $link_target = '_self'; } - if ( 'image' == $format || 'text-image' == $format ) { - /** - * Filters the image used as RSS icon in the RSS widget. - * - * @module widgets - * - * @since 3.6.0 - * - * @param string $var URL of RSS Widget icon. - */ - $link_image = apply_filters( 'jetpack_rss_widget_icon', plugins_url( 'images/rss/' . $args['imagecolor'] . '-' . $args['imagesize'] . '.png', dirname( dirname( __FILE__ ) ) ) ); - $link_item = 'RSS Feed'; - } - if ( 'text-image' == $format ) { - $link_item .= ' ' . esc_html__( 'RSS - ' . $type_text, 'jetpack' ) . ''; - } - if ( 'text' == $format ) { - $link_item = '' . esc_html__( 'RSS - ' . $type_text, 'jetpack' ) . ''; + $format = $args['format']; + if ( 'image' === $format ) { + $link_contents = $this->get_image_tag( $args ); + } elseif ( 'text-image' === $format ) { + $link_contents = sprintf( + '%1$s %2$s', + $this->get_image_tag( $args ), + $link_text + ); + } elseif ( 'text' === $format ) { + $link_contents = $link_text; } - if ( 'text' == $format ) { - echo '
    • '; - } else { - echo '

      '; - } - echo $link_item; - if ( 'text' == $format ) { - echo '

    • '; - } else { - echo '

      '; - } + printf( + '%1$s%6$s%2$s', + 'text' === $format ? '
    • ' : '

      ', // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + 'text' === $format ? '

    • ' : '

      ', // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + esc_attr( $link_target ), + esc_url( get_bloginfo( $rss_type ) ), + esc_attr( $subscribe_to ), + $link_contents // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- we are escaping this above. + ); + } + + /** + * Return an image tag for the RSS icon. + * + * @param array $args Widget arguments. + */ + private function get_image_tag( $args ) { + $image_path = sprintf( + 'images/rss/%1$s-%2$s.png', + $args['imagecolor'], + $args['imagesize'] + ); + /** + * Filters the image used as RSS icon in the RSS widget. + * + * @module widgets + * + * @since 3.6.0 + * + * @param string $var URL of RSS Widget icon. + */ + $image = apply_filters( + 'jetpack_rss_widget_icon', + plugins_url( $image_path, dirname( __DIR__ ) ) + ); + + return sprintf( + '%2$s', + esc_url( $image ), + esc_attr__( 'RSS feed', 'jetpack' ) + ); } } // Class Jetpack_RSS_Links_Widget - -function jetpack_rss_links_widget_init() { - register_widget( 'Jetpack_RSS_Links_Widget' ); -} -add_action( 'widgets_init', 'jetpack_rss_links_widget_init' ); diff --git a/projects/plugins/jetpack/modules/widgets/search.php b/projects/plugins/jetpack/modules/widgets/search.php index b6a63cf9285e1..291649f23ee31 100644 --- a/projects/plugins/jetpack/modules/widgets/search.php +++ b/projects/plugins/jetpack/modules/widgets/search.php @@ -15,6 +15,9 @@ add_action( 'widgets_init', 'jetpack_search_widget_init' ); +/** + * Register the widget if Jetpack Search is available and enabled. + */ function jetpack_search_widget_init() { if ( ! Jetpack::is_connection_ready() @@ -64,6 +67,8 @@ class Jetpack_Search_Widget extends WP_Widget { * Jetpack_Search_Widget constructor. * * @since 5.0.0 + * + * @param string $name Widget name. */ public function __construct( $name = null ) { if ( empty( $name ) ) { @@ -124,7 +129,12 @@ public function activate_search() { * @since 5.7.0 */ public function widget_admin_setup() { - wp_enqueue_style( 'widget-jetpack-search-filters', plugins_url( 'search/css/search-widget-admin-ui.css', __FILE__ ) ); + wp_enqueue_style( + 'widget-jetpack-search-filters', + plugins_url( 'search/css/search-widget-admin-ui.css', __FILE__ ), + array(), + JETPACK__VERSION + ); // Register jp-tracks and jp-tracks-functions. Tracking::register_tracks_functions_scripts(); @@ -133,7 +143,8 @@ public function widget_admin_setup() { 'jetpack-search-widget-admin', plugins_url( 'search/js/search-widget-admin.js', __FILE__ ), array( 'jquery', 'jquery-ui-sortable', 'jp-tracks-functions' ), - JETPACK__VERSION + JETPACK__VERSION, + false ); wp_localize_script( @@ -175,7 +186,12 @@ public function enqueue_frontend_scripts() { true ); - wp_enqueue_style( 'jetpack-search-widget', plugins_url( 'search/css/search-widget-frontend.css', __FILE__ ) ); + wp_enqueue_style( + 'jetpack-search-widget', + plugins_url( 'search/css/search-widget-frontend.css', __FILE__ ), + array(), + JETPACK__VERSION + ); } /** @@ -204,8 +220,8 @@ private function get_sort_types() { * * @return bool Whether the current filter item is for the current widget. */ - function is_for_current_widget( $item ) { - return isset( $item['widget_id'] ) && $this->id == $item['widget_id']; + public function is_for_current_widget( $item ) { + return isset( $item['widget_id'] ) && $this->id == $item['widget_id']; // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison } /** @@ -221,12 +237,12 @@ function is_for_current_widget( $item ) { public function should_display_sitewide_filters() { $filter_widgets = get_option( 'widget_jetpack-search-filters' ); - // This shouldn't be empty, but just for sanity + // This shouldn't be empty, but just for sanity. if ( empty( $filter_widgets ) ) { return false; } - // If any widget has any filters, return false + // If any widget has any filters, return false. foreach ( $filter_widgets as $number => $widget ) { $widget_id = sprintf( '%s-%d', $this->id_base, $number ); if ( ! empty( $widget['filters'] ) && is_active_widget( false, $widget_id, $this->id_base ) ) { @@ -237,6 +253,11 @@ public function should_display_sitewide_filters() { return true; } + /** + * Widget defaults. + * + * @param array $instance Previously saved values from database. + */ public function jetpack_search_populate_defaults( $instance ) { $instance = wp_parse_args( (array) $instance, @@ -601,6 +622,7 @@ private function maybe_render_sort_javascript( $instance, $order, $orderby ) { * @return array Order by and order. */ private function sorting_to_wp_query_param( $sort ) { + // phpcs:disable WordPress.Security.NonceVerification.Recommended $parts = explode( '|', $sort ); $orderby = isset( $_GET['orderby'] ) ? $_GET['orderby'] @@ -610,6 +632,8 @@ private function sorting_to_wp_query_param( $sort ) { ? strtoupper( $_GET['order'] ) : ( ( isset( $parts[1] ) && 'ASC' === strtoupper( $parts[1] ) ) ? 'ASC' : 'DESC' ); + // phpcs:enable WordPress.Security.NonceVerification.Recommended + return array( $orderby, $order ); } @@ -623,7 +647,7 @@ private function sorting_to_wp_query_param( $sort ) { * * @return array Settings to save. */ - public function update( $new_instance, $old_instance ) { + public function update( $new_instance, $old_instance ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable $new_instance = $this->maybe_reformat_widget( $new_instance ); $instance = array(); @@ -718,7 +742,7 @@ public function form( $instance ) { $instance = $this->jetpack_search_populate_defaults( $instance ); - $title = strip_tags( $instance['title'] ); + $title = wp_strip_all_tags( $instance['title'] ); $hide_filters = Helper::are_filters_by_widget_disabled(); @@ -776,7 +800,7 @@ class="jetpack-search-filters-widget__sort-controls-enabled" type="checkbox" value="name ); ?>" name="get_field_name( 'post_types' ) ); ?>[]" - name, $instance['post_types'] ) ); ?> + name, $instance['post_types'], true ) ); ?> />  label ); ?> @@ -800,7 +824,9 @@ class="widefat jetpack-search-filters-widget__sort-order">
      @@ -893,7 +919,7 @@ class="widefat" * @param bool $is_template Whether this is for an Underscore template or not. */ private function render_widget_attr( $name, $value, $is_template ) { - echo $is_template ? "<%= $name %>" : esc_attr( $value ); + echo $is_template ? "<%= $name %>" : esc_attr( $value ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } /** @@ -910,7 +936,7 @@ private function render_widget_attr( $name, $value, $is_template ) { */ private function render_widget_option_selected( $name, $value, $compare, $is_template ) { $compare_js = rawurlencode( $compare ); - echo $is_template ? "<%= decodeURIComponent( '$compare_js' ) === $name ? 'selected=\"selected\"' : '' %>" : selected( $value, $compare ); + echo $is_template ? "<%= decodeURIComponent( '$compare_js' ) === $name ? 'selected=\"selected\"' : '' %>" : selected( $value, $compare ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } /** @@ -968,7 +994,7 @@ public function render_widget_edit_filter( $filter, $is_template = false ) { true ), 'objects' ) as $taxonomy ) : ?>