PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-a.7
Jetpack – WP Security, Backup, Speed, & Growth v16.3-a.7
16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 All 506 releases
← All changes | modules/carousel/jetpack-carousel.php +169 -106 13.3.3 → 16.3-a.7 View file →
@@ -7,10 +7,18 @@
7 7
8 8 use Automattic\Jetpack\Assets;
9 9 use Automattic\Jetpack\Stats\Options as Stats_Options;
10 10 use Automattic\Jetpack\Status;
11 +use Automattic\Jetpack\Status\Host;
12 +
13 +if ( ! defined( 'ABSPATH' ) ) {
14 + exit( 0 );
15 +}
16 +
11 17 /**
12 18 * Jetpack_Carousel class.
19 + *
20 + * @phan-constructor-used-for-side-effects
13 21 */
14 22 class Jetpack_Carousel {
15 23 /**
16 24 * Defines Carousel pre-built widths
@@ -42,9 +50,9 @@
42 50 */
43 51 public $in_gallery = false;
44 52
45 53 /**
46 - * Determines whether the Jetpack class and method exists. Default is true.
54 + * Determines whether the module runs in the Jetpack plugin, as opposed to WP.com Simple site environment
47 55 *
48 56 * @var bool
49 57 */
50 58 public $in_jetpack = true;
@@ -77,16 +85,13 @@
77 85 if ( $this->maybe_disable_jp_carousel() ) {
78 86 return;
79 87 }
80 88
81 - $this->in_jetpack = ( class_exists( 'Jetpack' ) && method_exists( 'Jetpack', 'enable_module_configurable' ) ) ? true : false;
89 + $this->in_jetpack = ! ( new Host() )->is_wpcom_simple();
82 90
83 91 $this->single_image_gallery_enabled = ! $this->maybe_disable_jp_carousel_single_images();
84 92 $this->single_image_gallery_enabled_media_file = $this->maybe_enable_jp_carousel_single_images_media_file();
85 93
86 - // Disable core lightbox when Carousel is enabled.
87 - add_action( 'wp_theme_json_data_theme', array( $this, 'disable_core_lightbox' ) );
88 -
89 94 if ( is_admin() ) {
90 95 // Register the Carousel-related related settings.
91 96 add_action( 'admin_init', array( $this, 'register_settings' ), 5 );
92 97 if ( ! $this->in_jetpack ) {
@@ -125,8 +130,10 @@
125 130 if ( $this->single_image_gallery_enabled ) {
126 131 add_filter( 'the_content', array( $this, 'add_data_img_tags_and_enqueue_assets' ) );
127 132 }
128 133
134 + add_filter( 'render_block_data', array( $this, 'remove_core_lightbox_in_gallery' ), 10, 3 );
135 +
129 136 // `is_amp_request()` can't be called until the 'wp' filter.
130 137 add_action( 'wp', array( $this, 'check_amp_support' ) );
131 138 }
132 139
@@ -209,34 +216,8 @@
209 216 return apply_filters( 'jp_carousel_load_for_images_linked_to_file', false );
210 217 }
211 218
212 219 /**
213 - * Disable the "Lightbox" option offered in WordPress core
214 - * whenever Jetpack's Carousel feature is enabled.
215 - *
216 - * @since 13.3
217 - *
218 - * @param WP_Theme_JSON_Data $theme_json Class to access and update theme.json data.
219 - */
220 - public function disable_core_lightbox( $theme_json ) {
221 - return $theme_json->update_with(
222 - array(
223 - 'version' => 2,
224 - 'settings' => array(
225 - 'blocks' => array(
226 - 'core/image' => array(
227 - 'lightbox' => array(
228 - 'allowEditing' => false,
229 - 'enabled' => false,
230 - ),
231 - ),
232 - ),
233 - ),
234 - )
235 - );
236 - }
237 -
238 - /**
239 220 * Returns the value of the applied jp_carousel_asset_version filter
240 221 *
241 222 * @since 1.6.0
242 223 *
@@ -363,8 +344,28 @@
363 344 return $content;
364 345 }
365 346
366 347 /**
348 + * Remove core lightbox settings from images in a gallery, if Carousel is enabled.
349 + *
350 + * @param array $parsed_block An associative array of the block being rendered.
351 + * @param array $source_block An un-modified copy of `$parsed_block`, as it appeared in the source content.
352 + * @param WP_Block|null $parent_block If this is a nested block, a reference to the parent block.
353 + * @return array The modified block data.
354 + */
355 + public function remove_core_lightbox_in_gallery( $parsed_block, $source_block, $parent_block ) {
356 + if (
357 + ! empty( $parsed_block['blockName'] ) &&
358 + 'core/image' === $parsed_block['blockName'] &&
359 + ! empty( $parent_block->name ) &&
360 + 'core/gallery' === $parent_block->name
361 + ) {
362 + unset( $parsed_block['attrs']['lightbox'] );
363 + }
364 + return $parsed_block;
365 + }
366 +
367 + /**
367 368 * Enrich the gallery block content using the render_block_{$this->name} filter.
368 369 * This function is triggered after block render to make sure we track galleries within
369 370 * reusable blocks.
370 371 *
@@ -384,9 +385,9 @@
384 385 }
385 386
386 387 $this->enqueue_assets();
387 388
388 - if ( ! isset( $post ) ) {
389 + if ( ! $post instanceof WP_Post ) {
389 390 return $block_content;
390 391 }
391 392
392 393 $blog_id = (int) get_current_blog_id();
@@ -417,9 +418,9 @@
417 418 $extra_attributes = implode(
418 419 ' ',
419 420 array_map(
420 421 function ( $data_key, $data_values ) {
421 - return esc_attr( $data_key ) . "='" . wp_json_encode( $data_values ) . "'";
422 + return esc_attr( $data_key ) . "='" . esc_attr( wp_json_encode( $data_values, JSON_UNESCAPED_SLASHES | JSON_HEX_AMP ) ) . "'";
422 423 },
423 424 array_keys( $extra_data ),
424 425 array_values( $extra_data )
425 426 )
@@ -450,14 +451,12 @@
450 451 true
451 452 );
452 453
453 454 $swiper_library_path = array(
454 - 'url' => Assets::get_file_url_for_environment(
455 - '_inc/build/carousel/swiper-bundle.min.js',
456 - 'modules/carousel/swiper-bundle.js'
457 - ),
455 + 'url' => plugins_url( '_inc/blocks/swiper.js', JETPACK__PLUGIN_FILE ),
458 456 );
459 457 wp_localize_script( 'jetpack-carousel', 'jetpackSwiperLibraryPath', $swiper_library_path );
458 + add_action( 'wp_footer', array( $this, 'prefetch_swiper_library' ) );
460 459
461 460 // Note: using home_url() instead of admin_url() for ajaxurl to be sure to get same domain on wpcom when using mapped domains (also works on self-hosted).
462 461 // Also: not hardcoding path since there is no guarantee site is running on site root in self-hosted context.
463 462 $is_logged_in = is_user_logged_in();
@@ -477,8 +476,9 @@
477 476 'comment' => __( 'Comment', 'jetpack' ),
478 477 'post_comment' => __( 'Post Comment', 'jetpack' ),
479 478 'write_comment' => __( 'Write a Comment...', 'jetpack' ),
480 479 'loading_comments' => __( 'Loading Comments...', 'jetpack' ),
480 + 'image_label' => __( 'Open image in full-screen.', 'jetpack' ),
481 481 'download_original' => sprintf(
482 482 /* translators: %1s is the full-size image width, and %2s is the height. */
483 483 __( 'View full size <span class="photo-size">%1$s<span class="photo-size-times">&times;</span>%2$s</span>', 'jetpack' ),
484 484 '{0}',
@@ -542,12 +542,12 @@
542 542 */
543 543 $localize_strings = apply_filters( 'jp_carousel_localize_strings', $localize_strings );
544 544 wp_localize_script( 'jetpack-carousel', 'jetpackCarouselStrings', $localize_strings );
545 545 wp_enqueue_style(
546 - 'jetpack-carousel-swiper-css',
547 - plugins_url( 'swiper-bundle.css', __FILE__ ),
546 + 'jetpack-swiper-library',
547 + plugins_url( '_inc/blocks/swiper.css', JETPACK__PLUGIN_FILE ),
548 548 array(),
549 - $this->asset_version( JETPACK__VERSION )
549 + JETPACK__VERSION
550 550 );
551 551 wp_enqueue_style( 'jetpack-carousel', plugins_url( 'jetpack-carousel.css', __FILE__ ), array(), $this->asset_version( JETPACK__VERSION ) );
552 552 wp_style_add_data( 'jetpack-carousel', 'rtl', 'replace' );
553 553
@@ -572,8 +572,24 @@
572 572 }
573 573 }
574 574
575 575 /**
576 + * Hint the browser to fetch the Swiper library while it is idle.
577 + *
578 + * Swiper is only requested when the lightbox is first opened, which puts a network
579 + * round trip in front of that first click. This is deliberately `prefetch` rather than
580 + * `preload`: most visitors never open the lightbox, so the fetch must stay at low
581 + * priority and out of the way of the page's own images. `loadSwiper()` still loads the
582 + * library on demand, since a prefetch is a hint the browser is free to ignore.
583 + */
584 + public function prefetch_swiper_library() {
585 + printf(
586 + '<link rel="prefetch" href="%s" as="script" />' . "\n",
587 + esc_url( plugins_url( '_inc/blocks/swiper.js', JETPACK__PLUGIN_FILE ) )
588 + );
589 + }
590 +
591 + /**
576 592 * Generate the HTML skeleton that will be picked up by the Carousel JS and used for showing the carousel.
577 593 */
578 594 public function add_carousel_skeleton() {
579 595 $localize_strings = $this->localize_strings;
@@ -583,12 +599,13 @@
583 599 $current_user = wp_get_current_user();
584 600 $require_name_email = (int) get_option( 'require_name_email' );
585 601 /* translators: %s is replaced with a field name in the form, e.g. "Email" */
586 602 $required = ( $require_name_email ) ? __( '%s (Required)', 'jetpack' ) : '%s';
603 + require_once JETPACK__PLUGIN_DIR . '_inc/lib/class-jetpack-spinner.php';
587 604 ?>
588 - <div id="jp-carousel-loading-overlay">
605 + <div id="jp-carousel-loading-overlay" style="display: none;">
589 606 <div id="jp-carousel-loading-wrapper">
590 - <span id="jp-carousel-library-loading">&nbsp;</span>
607 + <span id="jp-carousel-library-loading"><?php echo Jetpack_Spinner::render( 40 ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- static SVG markup. ?></span>
591 608 </div>
592 609 </div>
593 610 <div class="jp-carousel-overlay<?php echo( $is_light ? ' jp-carousel-light' : '' ); ?>" style="display: none;">
594 611
@@ -594,9 +611,9 @@
594 611
595 612 <div class="jp-carousel-container<?php echo( $is_light ? ' jp-carousel-light' : '' ); ?>">
596 613 <!-- The Carousel Swiper -->
597 614 <div
598 - class="jp-carousel-wrap swiper-container jp-carousel-swiper-container jp-carousel-transitions"
615 + class="jp-carousel-wrap swiper jp-carousel-swiper-container jp-carousel-transitions"
599 616 itemscope
600 617 itemtype="https://schema.org/ImageGallery">
601 618 <div class="jp-carousel swiper-wrapper"></div>
602 619 <div class="jp-swiper-button-prev swiper-button-prev">
@@ -638,9 +655,9 @@
638 655 <div class="jp-swiper-pagination swiper-pagination"></div>
639 656 <div class="jp-carousel-pagination"></div>
640 657 </div>
641 658 <div class="jp-carousel-photo-title-container">
642 - <h2 class="jp-carousel-photo-caption"></h2>
659 + <div class="jp-carousel-photo-caption"></div>
643 660 </div>
644 661 <div class="jp-carousel-photo-icons-container">
645 662 <a href="#" class="jp-carousel-icon-btn jp-carousel-icon-info" aria-label="<?php esc_attr_e( 'Toggle photo metadata visibility', 'jetpack' ); ?>">
646 663 <span class="jp-carousel-icon">
@@ -674,9 +691,9 @@
674 691 </div>
675 692 <div class="jp-carousel-info-extra">
676 693 <div class="jp-carousel-info-content-wrapper">
677 694 <div class="jp-carousel-photo-title-container">
678 - <h2 class="jp-carousel-photo-title"></h2>
695 + <div class="jp-carousel-photo-title"></div>
679 696 </div>
680 697 <div class="jp-carousel-comments-wrapper">
681 698 <?php if ( $localize_strings['display_comments'] ) : ?>
682 699 <div id="jp-carousel-comments-loading">
@@ -683,9 +700,9 @@
683 700 <span><?php echo esc_html( $localize_strings['loading_comments'] ); ?></span>
684 701 </div>
685 702 <div class="jp-carousel-comments"></div>
686 703 <div id="jp-carousel-comment-form-container">
687 - <span id="jp-carousel-comment-form-spinner">&nbsp;</span>
704 + <span id="jp-carousel-comment-form-spinner"><?php echo Jetpack_Spinner::render( 20 ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- static SVG markup. ?></span>
688 705 <div id="jp-carousel-comment-post-results"></div>
689 706 <?php if ( $use_local_comments ) : ?>
690 707 <?php if ( ! $localize_strings['is_logged_in'] && $localize_strings['comment_registration'] ) : ?>
691 708 <div id="jp-carousel-comment-form-commenting-as">
@@ -754,15 +771,14 @@
754 771 </div>
755 772 <div class="jp-carousel-image-meta">
756 773 <div class="jp-carousel-title-and-caption">
757 774 <div class="jp-carousel-photo-info">
758 - <h3 class="jp-carousel-caption" itemprop="caption description"></h3>
775 + <div class="jp-carousel-caption" itemprop="caption description"></div>
759 776 </div>
760 777
761 778 <div class="jp-carousel-photo-description"></div>
762 779 </div>
763 - <ul class="jp-carousel-image-exif" style="display: none;"></ul>
764 - <a class="jp-carousel-image-download" href="#" target="_blank" style="display: none;">
780 + <a class="jp-carousel-image-download" href="#" aria-label="<?php esc_attr_e( 'Download image', 'jetpack' ); ?>" target="_blank" style="display: none;">
765 781 <svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
766 782 <mask id="mask0" mask-type="alpha" maskUnits="userSpaceOnUse" x="3" y="3" width="19" height="18">
767 783 <path fill-rule="evenodd" clip-rule="evenodd" d="M5.84615 5V19H19.7775V12H21.7677V19C21.7677 20.1 20.8721 21 19.7775 21H5.84615C4.74159 21 3.85596 20.1 3.85596 19V5C3.85596 3.9 4.74159 3 5.84615 3H12.8118V5H5.84615ZM14.802 5V3H21.7677V10H19.7775V6.41L9.99569 16.24L8.59261 14.83L18.3744 5H14.802Z" fill="white"/>
768 784 </mask>
@@ -813,8 +829,11 @@
813 829 * @param string $content HTML content of the post.
814 830 * @return string
815 831 */
816 832 public function add_data_img_tags_and_enqueue_assets( $content ) {
833 + if ( ! is_string( $content ) || $content === '' ) {
834 + return '';
835 + }
817 836 if (
818 837 class_exists( 'Jetpack_AMP_Support' )
819 838 && Jetpack_AMP_Support::is_amp_request()
820 839 ) {
@@ -825,11 +844,19 @@
825 844 return $content;
826 845 }
827 846 $selected_images = array();
828 847 foreach ( $matches[0] as $image_html ) {
848 + // This image already carries the attributes this method adds, so adding
849 + // them again would emit every one of them twice. Tiled Gallery output
850 + // reaches this filter twice: once as 'jetpack_tiled_galleries_block_content'
851 + // from inside the block's render callback, and again as 'the_content' when
852 + // single image galleries are enabled. See JETPACK-1990.
853 + if ( str_contains( $image_html, 'data-attachment-id=' ) ) {
854 + continue;
855 + }
829 856 if (
830 857 preg_match( '/(wp-image-|data-id=)\"?([0-9]+)\"?/i', $image_html, $class_id )
831 - && ! preg_match( '/wp-block-jetpack-slideshow_image/', $image_html )
858 + && ! str_contains( $image_html, 'wp-block-jetpack-slideshow_image' )
832 859 ) {
833 860 /**
834 861 * Allow filtering the attachment ID used to fetch and populate metadata about an image in a gallery.
835 862 *
@@ -881,9 +908,13 @@
881 908 *
882 909 * This is meant as a relatively quick fix, as a better fix is likely to update the get_posts call above to only
883 910 * include attachments.
884 911 */
885 - if ( ! isset( $attachment->ID ) || ! wp_attachment_is_image( $attachment->ID ) ) {
912 + if (
913 + ! isset( $attachment->ID )
914 + || ! wp_attachment_is_image( $attachment->ID )
915 + || ! isset( $selected_images[ $attachment->ID ] )
916 + ) {
886 917 continue;
887 918 }
888 919 $image_elements = $selected_images[ $attachment->ID ];
889 920
@@ -912,10 +943,10 @@
912 943 *
913 944 * @see add_data_img_tags_and_enqueue_assets()
914 945 * @see https://developer.wordpress.org/reference/functions/wp_get_attachment_image/ Documentation about wp_get_attachment_image
915 946 *
916 - * @param string[] $attr Array of attribute values for the image markup, keyed by attribute name.
917 - * @param WP_Post $attachment Image attachment post.
947 + * @param string[] $attr Array of attribute values for the image markup, keyed by attribute name.
948 + * @param null|WP_Post $attachment Image attachment post.
918 949 *
919 950 * @return string[] Modified image attributes.
920 951 */
921 952 public function add_data_to_images( $attr, $attachment = null ) {
@@ -925,19 +956,24 @@
925 956 ) {
926 957 return $attr;
927 958 }
928 959
929 - $attachment_id = (int) $attachment->ID;
930 - if ( ! wp_attachment_is_image( $attachment_id ) ) {
960 + if (
961 + ! $attachment instanceof WP_Post
962 + || ! isset( $attachment->ID )
963 + || ! wp_attachment_is_image( $attachment )
964 + ) {
931 965 return $attr;
932 966 }
933 967
968 + $attachment_id = (int) $attachment->ID;
934 969 $orig_file = wp_get_attachment_image_src( $attachment_id, 'full' );
935 - $orig_file = isset( $orig_file[0] ) ? $orig_file[0] : wp_get_attachment_url( $attachment_id );
970 + $orig_file = $orig_file[0] ?? wp_get_attachment_url( $attachment_id );
936 971 $meta = wp_get_attachment_metadata( $attachment_id );
937 972 $size = isset( $meta['width'] ) ? (int) $meta['width'] . ',' . (int) $meta['height'] : '';
938 973 $img_meta = ( ! empty( $meta['image_meta'] ) ) ? (array) $meta['image_meta'] : array();
939 974 $comments_opened = (int) comments_open( $attachment_id );
975 + $display_exif = $this->test_1or0_option( Jetpack_Options::get_option_and_ensure_autoload( 'carousel_display_exif', true ) );
940 976
941 977 /**
942 978 * Note: Cannot generate a filename from the width and height wp_get_attachment_image_src() returns because
943 979 * it takes the $content_width global variable themes can set in consideration, therefore returning sizes
@@ -952,39 +988,62 @@
952 988 * EG with Twenty Ten activated:
953 989 * array(4) { [0]=> string(82) "http://vanillawpinstall.blah/wp-content/uploads/2012/06/IMG_3534-1024x764.jpg" [1]=> int(640) [2]=> int(477) [3]=> bool(true) }
954 990 */
955 991
956 - $medium_file_info = wp_get_attachment_image_src( $attachment_id, 'medium' );
957 - $medium_file = isset( $medium_file_info[0] ) ? $medium_file_info[0] : '';
958 -
959 992 $large_file_info = wp_get_attachment_image_src( $attachment_id, 'large' );
960 - $large_file = isset( $large_file_info[0] ) ? $large_file_info[0] : '';
993 + $large_file = $large_file_info[0] ?? '';
961 994
962 - $attachment = get_post( $attachment_id );
963 - $attachment_title = ! empty( $attachment ) ? wptexturize( $attachment->post_title ) : '';
964 - $attachment_desc = ! empty( $attachment ) ? wpautop( wptexturize( $attachment->post_content ) ) : '';
965 - $attachment_caption = ! empty( $attachment ) ? wpautop( wptexturize( $attachment->post_excerpt ) ) : '';
995 + $attachment_title = wptexturize( $attachment->post_title );
996 + $attachment_desc = wpautop( wptexturize( $attachment->post_content ) );
997 + $attachment_caption = wpautop( wptexturize( $attachment->post_excerpt ) );
966 998
967 - // See https://github.com/Automattic/jetpack/issues/2765.
968 - if ( isset( $img_meta['keywords'] ) ) {
969 - unset( $img_meta['keywords'] );
970 - }
971 -
972 - $img_meta = wp_json_encode( array_map( 'strval', array_filter( $img_meta, 'is_scalar' ) ) );
973 -
974 999 $attr['data-attachment-id'] = $attachment_id;
975 1000 $attr['data-permalink'] = esc_attr( get_permalink( $attachment_id ) );
976 1001 $attr['data-orig-file'] = esc_attr( $orig_file );
977 1002 $attr['data-orig-size'] = $size;
978 1003 $attr['data-comments-opened'] = $comments_opened;
979 - $attr['data-image-meta'] = esc_attr( $img_meta );
1004 +
1005 + /*
1006 + Lets the Carousel show its "has comments" badge without fetching the comments
1007 + themselves. Omitted when there are none, which is the common case, so galleries
1008 + without comments pay nothing for it.
1009 + */
1010 + $comments_count = (int) $attachment->comment_count;
1011 + if ( $comments_count > 0 ) {
1012 + $attr['data-comments-count'] = $comments_count;
1013 + }
1014 +
1015 + if ( $display_exif ) {
1016 + // See https://github.com/Automattic/jetpack/issues/2765.
1017 + if ( isset( $img_meta['keywords'] ) ) {
1018 + unset( $img_meta['keywords'] );
1019 + }
1020 +
1021 + /*
1022 + Filtering on `is_scalar` alone kept every "" and "0" in the metadata array, which
1023 + on a typical photo is most of it. The carousel skips those values when it renders
1024 + the EXIF panel anyway, so serialising them only inflates the page. Mirror that
1025 + check here: drop empties and numeric zeroes, but keep text that merely casts to
1026 + zero, such as a camera name.
1027 + */
1028 + $img_meta = array_filter(
1029 + array_map( 'strval', array_filter( $img_meta, 'is_scalar' ) ),
1030 + function ( $value ) {
1031 + return '' !== $value && ! ( is_numeric( $value ) && 0.0 === (float) $value );
1032 + }
1033 + );
1034 +
1035 + // With nothing left to show, the attribute itself is dead weight.
1036 + if ( ! empty( $img_meta ) ) {
1037 + $attr['data-image-meta'] = esc_attr( wp_json_encode( $img_meta, JSON_UNESCAPED_SLASHES | JSON_HEX_AMP ) );
1038 + }
1039 + }
1040 +
980 1041 // The lines below use `esc_attr( htmlspecialchars( ) )` because esc_attr tries to be too smart and won't double-encode, and we need that here.
981 1042 $attr['data-image-title'] = esc_attr( htmlspecialchars( $attachment_title, ENT_COMPAT ) );
982 1043 $attr['data-image-description'] = esc_attr( htmlspecialchars( $attachment_desc, ENT_COMPAT ) );
983 1044 $attr['data-image-caption'] = esc_attr( htmlspecialchars( $attachment_caption, ENT_COMPAT ) );
984 - $attr['data-medium-file'] = esc_attr( $medium_file );
985 1045 $attr['data-large-file'] = esc_attr( $large_file );
986 -
987 1046 return $attr;
988 1047 }
989 1048
990 1049 /**
@@ -1023,12 +1082,12 @@
1023 1082 * @param array $extra_data Array of data about the site and the post.
1024 1083 */
1025 1084 $extra_data = apply_filters( 'jp_carousel_add_data_to_container', $extra_data );
1026 1085 foreach ( (array) $extra_data as $data_key => $data_values ) {
1027 - $html = str_replace( '<div ', '<div ' . esc_attr( $data_key ) . "='" . wp_json_encode( $data_values ) . "' ", $html );
1028 - $html = str_replace( '<ul class="wp-block-gallery', '<ul ' . esc_attr( $data_key ) . "='" . wp_json_encode( $data_values ) . "' class=\"wp-block-gallery", $html );
1029 - $html = str_replace( '<ul class="blocks-gallery-grid', '<ul ' . esc_attr( $data_key ) . "='" . wp_json_encode( $data_values ) . "' class=\"blocks-gallery-grid", $html );
1030 - $html = preg_replace( '/\<figure([^>]*)class="(wp-block-gallery[^"]*?has-nested-images.*?)"/', '<figure ' . esc_attr( $data_key ) . "='" . wp_json_encode( $data_values ) . "' $1 class=\"$2\"", $html );
1086 + $html = str_replace( '<div ', '<div ' . esc_attr( $data_key ) . "='" . esc_attr( wp_json_encode( $data_values, JSON_HEX_AMP | JSON_UNESCAPED_SLASHES ) ) . "' ", $html );
1087 + $html = str_replace( '<ul class="wp-block-gallery', '<ul ' . esc_attr( $data_key ) . "='" . esc_attr( wp_json_encode( $data_values, JSON_HEX_AMP | JSON_UNESCAPED_SLASHES ) ) . "' class=\"wp-block-gallery", $html );
1088 + $html = str_replace( '<ul class="blocks-gallery-grid', '<ul ' . esc_attr( $data_key ) . "='" . esc_attr( wp_json_encode( $data_values, JSON_HEX_AMP | JSON_UNESCAPED_SLASHES ) ) . "' class=\"blocks-gallery-grid", $html );
1089 + $html = preg_replace( '/\<figure([^>]*)class="(wp-block-gallery[^"]*?has-nested-images.*?)"/', '<figure ' . esc_attr( $data_key ) . "='" . esc_attr( wp_json_encode( $data_values, JSON_HEX_AMP | JSON_UNESCAPED_SLASHES ) ) . "' $1 class=\"$2\"", $html );
1031 1090 }
1032 1091 }
1033 1092
1034 1093 return $html;
@@ -1073,9 +1132,9 @@
1073 1132
1074 1133 /**
1075 1134 * Retrieves comment information
1076 1135 *
1077 - * @return string
1136 + * @return never
1078 1137 */
1079 1138 public function get_attachment_comments() {
1080 1139 if ( ! headers_sent() ) {
1081 1140 header( 'Content-type: text/javascript' );
@@ -1099,11 +1158,11 @@
1099 1158
1100 1159 if ( ! $attachment_id ) {
1101 1160 wp_send_json_error(
1102 1161 __( 'Missing attachment ID.', 'jetpack' ),
1103 - 403
1162 + 403,
1163 + JSON_UNESCAPED_SLASHES
1104 1164 );
1105 - return;
1106 1165 }
1107 1166
1108 1167 $attachment_post = get_post( $attachment_id );
1109 1168 // If we have no info about that attachment, bail.
@@ -1109,11 +1168,11 @@
1109 1168 // If we have no info about that attachment, bail.
1110 1169 if ( ! ( $attachment_post instanceof WP_Post ) ) {
1111 1170 wp_send_json_error(
1112 1171 __( 'Missing attachment info.', 'jetpack' ),
1113 - 403
1172 + 403,
1173 + JSON_UNESCAPED_SLASHES
1114 1174 );
1115 - return;
1116 1175 }
1117 1176
1118 1177 // This AJAX call should only be used to fetch comments of attachments.
1119 1178 if ( 'attachment' !== $attachment_post->post_type ) {
@@ -1118,11 +1177,11 @@
1118 1177 // This AJAX call should only be used to fetch comments of attachments.
1119 1178 if ( 'attachment' !== $attachment_post->post_type ) {
1120 1179 wp_send_json_error(
1121 1180 __( 'You aren’t authorized to do that.', 'jetpack' ),
1122 - 403
1181 + 403,
1182 + JSON_UNESCAPED_SLASHES
1123 1183 );
1124 - return;
1125 1184 }
1126 1185
1127 1186 $parent_post = get_post_parent( $attachment_id );
1128 1187
@@ -1140,11 +1199,11 @@
1140 1199 $current_user = wp_get_current_user();
1141 1200 if ( ! ( $current_user instanceof WP_User ) ) {
1142 1201 wp_send_json_error(
1143 1202 __( 'Missing user info.', 'jetpack' ),
1144 - 403
1203 + 403,
1204 + JSON_UNESCAPED_SLASHES
1145 1205 );
1146 - return;
1147 1206 }
1148 1207
1149 1208 /*
1150 1209 * If a post is private / draft
@@ -1156,11 +1215,11 @@
1156 1215 && ! current_user_can( 'read_post', $parent_post->ID )
1157 1216 ) {
1158 1217 wp_send_json_error(
1159 1218 __( 'You aren’t authorized to do that.', 'jetpack' ),
1160 - 403
1219 + 403,
1220 + JSON_UNESCAPED_SLASHES
1161 1221 );
1162 - return;
1163 1222 }
1164 1223 }
1165 1224
1166 1225 if ( $offset < 1 ) {
@@ -1194,9 +1253,9 @@
1194 1253 'content' => wpautop( $comment->comment_content ),
1195 1254 );
1196 1255 }
1197 1256
1198 - die( wp_json_encode( $out ) );
1257 + wp_send_json( $out, null, JSON_UNESCAPED_SLASHES );
1199 1258 }
1200 1259
1201 1260 /**
1202 1261 * Adds a new comment to the database
@@ -1208,9 +1267,9 @@
1208 1267 header( 'Content-type: text/javascript' );
1209 1268 }
1210 1269
1211 1270 if ( empty( $_POST['nonce'] ) || ! wp_verify_nonce( $_POST['nonce'], 'carousel_nonce' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- WP Core doesn't unslash or sanitize nonces either
1212 - die( wp_json_encode( array( 'error' => __( 'Nonce verification failed.', 'jetpack' ) ) ) );
1271 + die( wp_json_encode( array( 'error' => __( 'Nonce verification failed.', 'jetpack' ) ), JSON_UNESCAPED_SLASHES ) );
1213 1272 }
1214 1273
1215 1274 $_blog_id = isset( $_POST['blog_id'] ) ? (int) $_POST['blog_id'] : 0;
1216 1275 $_post_id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
@@ -1216,17 +1275,17 @@
1216 1275 $_post_id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
1217 1276 $comment = isset( $_POST['comment'] ) ? filter_var( wp_unslash( $_POST['comment'] ) ) : null;
1218 1277
1219 1278 if ( empty( $_blog_id ) ) {
1220 - die( wp_json_encode( array( 'error' => __( 'Missing target blog ID.', 'jetpack' ) ) ) );
1279 + die( wp_json_encode( array( 'error' => __( 'Missing target blog ID.', 'jetpack' ) ), JSON_UNESCAPED_SLASHES ) );
1221 1280 }
1222 1281
1223 1282 if ( empty( $_post_id ) ) {
1224 - die( wp_json_encode( array( 'error' => __( 'Missing target post ID.', 'jetpack' ) ) ) );
1283 + die( wp_json_encode( array( 'error' => __( 'Missing target post ID.', 'jetpack' ) ), JSON_UNESCAPED_SLASHES ) );
1225 1284 }
1226 1285
1227 1286 if ( empty( $comment ) ) {
1228 - die( wp_json_encode( array( 'error' => __( 'No comment text was submitted.', 'jetpack' ) ) ) );
1287 + die( wp_json_encode( array( 'error' => __( 'No comment text was submitted.', 'jetpack' ) ), JSON_UNESCAPED_SLASHES ) );
1229 1288 }
1230 1289
1231 1290 // Used in context like NewDash.
1232 1291 $switched = false;
@@ -1241,9 +1300,9 @@
1241 1300 if ( ! comments_open( $_post_id ) ) {
1242 1301 if ( $switched ) {
1243 1302 restore_current_blog();
1244 1303 }
1245 - die( wp_json_encode( array( 'error' => __( 'Comments on this post are closed.', 'jetpack' ) ) ) );
1304 + die( wp_json_encode( array( 'error' => __( 'Comments on this post are closed.', 'jetpack' ) ), JSON_UNESCAPED_SLASHES ) );
1246 1305 }
1247 1306
1248 1307 if ( is_user_logged_in() ) {
1249 1308 $user = wp_get_current_user();
@@ -1255,15 +1314,18 @@
1255 1314 if ( empty( $user_id ) ) {
1256 1315 if ( $switched ) {
1257 1316 restore_current_blog();
1258 1317 }
1259 - die( wp_json_encode( array( 'error' => __( 'Sorry, but we could not authenticate your request.', 'jetpack' ) ) ) );
1318 + die( wp_json_encode( array( 'error' => __( 'Sorry, but we could not authenticate your request.', 'jetpack' ) ), JSON_UNESCAPED_SLASHES ) );
1260 1319 }
1261 1320 } else {
1262 1321 $user_id = 0;
1263 1322 $display_name = isset( $_POST['author'] ) ? sanitize_text_field( wp_unslash( $_POST['author'] ) ) : null;
1264 - $email = isset( $_POST['email'] ) ? wp_unslash( $_POST['email'] ) : null; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Checked or sanitized below.
1265 - $url = isset( $_POST['url'] ) ? esc_url_raw( wp_unslash( $_POST['url'] ) ) : null;
1323 + $email = null;
1324 + if ( isset( $_POST['email'] ) && is_string( $_POST['email'] ) ) {
1325 + $email = wp_unslash( $_POST['email'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Checked or sanitized below.
1326 + }
1327 + $url = isset( $_POST['url'] ) && is_string( $_POST['url'] ) ? esc_url_raw( wp_unslash( $_POST['url'] ) ) : null;
1266 1328
1267 1329 if ( get_option( 'require_name_email' ) ) {
1268 1330 if ( empty( $display_name ) ) {
1269 1331 if ( $switched ) {
@@ -1268,9 +1330,9 @@
1268 1330 if ( empty( $display_name ) ) {
1269 1331 if ( $switched ) {
1270 1332 restore_current_blog();
1271 1333 }
1272 - die( wp_json_encode( array( 'error' => __( 'Please provide your name.', 'jetpack' ) ) ) );
1334 + die( wp_json_encode( array( 'error' => __( 'Please provide your name.', 'jetpack' ) ), JSON_UNESCAPED_SLASHES ) );
1273 1335 }
1274 1336
1275 1337 if ( empty( $email ) ) {
1276 1338 if ( $switched ) {
@@ -1275,9 +1337,9 @@
1275 1337 if ( empty( $email ) ) {
1276 1338 if ( $switched ) {
1277 1339 restore_current_blog();
1278 1340 }
1279 - die( wp_json_encode( array( 'error' => __( 'Please provide an email address.', 'jetpack' ) ) ) );
1341 + die( wp_json_encode( array( 'error' => __( 'Please provide an email address.', 'jetpack' ) ), JSON_UNESCAPED_SLASHES ) );
1280 1342 }
1281 1343
1282 1344 if ( ! is_email( $email ) ) {
1283 1345 if ( $switched ) {
@@ -1282,9 +1344,9 @@
1282 1344 if ( ! is_email( $email ) ) {
1283 1345 if ( $switched ) {
1284 1346 restore_current_blog();
1285 1347 }
1286 - die( wp_json_encode( array( 'error' => __( 'Please provide a valid email address.', 'jetpack' ) ) ) );
1348 + die( wp_json_encode( array( 'error' => __( 'Please provide a valid email address.', 'jetpack' ) ), JSON_UNESCAPED_SLASHES ) );
1287 1349 }
1288 1350 } else {
1289 1351 $email = $email !== null ? sanitize_email( $email ) : null;
1290 1352 }
@@ -1325,9 +1387,10 @@
1325 1387 wp_json_encode(
1326 1388 array(
1327 1389 'comment_id' => $comment_id,
1328 1390 'comment_status' => $comment_status,
1329 - )
1391 + ),
1392 + JSON_UNESCAPED_SLASHES
1330 1393 )
1331 1394 );
1332 1395 }
1333 1396
@@ -1455,9 +1518,9 @@
1455 1518 * Sanitize input for the `carousel_display_exif` setting.
1456 1519 *
1457 1520 * @param mixed $value User input setting value.
1458 1521 *
1459 - * @return number Sanitized value, only 1 or 0.
1522 + * @return int Sanitized value, only 1 or 0.
1460 1523 */
1461 1524 public function carousel_display_exif_sanitize( $value ) {
1462 1525 return $this->sanitize_1or0_option( $value );
1463 1526 }
@@ -1464,11 +1527,11 @@
1464 1527
1465 1528 /**
1466 1529 * Return sanitized option for value that controls whether comments will be hidden or not.
1467 1530 *
1468 - * @param number $value Value to sanitize.
1531 + * @param mixed $value Value to sanitize.
1469 1532 *
1470 - * @return number Sanitized value, only 1 or 0.
1533 + * @return int Sanitized value, only 1 or 0.
1471 1534 */
1472 1535 public function carousel_display_comments_sanitize( $value ) {
1473 1536 return $this->sanitize_1or0_option( $value );
1474 1537 }
@@ -1508,9 +1571,9 @@
1508 1571 * Sanitize input for the `carousel_enable_it` setting.
1509 1572 *
1510 1573 * @param mixed $value User input.
1511 1574 *
1512 - * @return number Sanitized value, only 1 or 0.
1575 + * @return int Sanitized value, only 1 or 0.
1513 1576 */
1514 1577 public function carousel_enable_it_sanitize( $value ) {
1515 1578 return $this->sanitize_1or0_option( $value );
1516 1579 }