| @@ -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,12 +451,9 @@ | ||
| 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 ); |
| 460 | 458 | |
| 461 | 459 | // 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). |
| @@ -477,8 +475,9 @@ | ||
| 477 | 475 | 'comment' => __( 'Comment', 'jetpack' ), |
| 478 | 476 | 'post_comment' => __( 'Post Comment', 'jetpack' ), |
| 479 | 477 | 'write_comment' => __( 'Write a Comment...', 'jetpack' ), |
| 480 | 478 | 'loading_comments' => __( 'Loading Comments...', 'jetpack' ), |
| 479 | + 'image_label' => __( 'Open image in full-screen.', 'jetpack' ), | |
| 481 | 480 | 'download_original' => sprintf( |
| 482 | 481 | /* translators: %1s is the full-size image width, and %2s is the height. */ |
| 483 | 482 | __( 'View full size <span class="photo-size">%1$s<span class="photo-size-times">×</span>%2$s</span>', 'jetpack' ), |
| 484 | 483 | '{0}', |
| @@ -542,12 +541,12 @@ | ||
| 542 | 541 | */ |
| 543 | 542 | $localize_strings = apply_filters( 'jp_carousel_localize_strings', $localize_strings ); |
| 544 | 543 | wp_localize_script( 'jetpack-carousel', 'jetpackCarouselStrings', $localize_strings ); |
| 545 | 544 | wp_enqueue_style( |
| 546 | - 'jetpack-carousel-swiper-css', | |
| 547 | - plugins_url( 'swiper-bundle.css', __FILE__ ), | |
| 545 | + 'jetpack-swiper-library', | |
| 546 | + plugins_url( '_inc/blocks/swiper.css', JETPACK__PLUGIN_FILE ), | |
| 548 | 547 | array(), |
| 549 | - $this->asset_version( JETPACK__VERSION ) | |
| 548 | + JETPACK__VERSION | |
| 550 | 549 | ); |
| 551 | 550 | wp_enqueue_style( 'jetpack-carousel', plugins_url( 'jetpack-carousel.css', __FILE__ ), array(), $this->asset_version( JETPACK__VERSION ) ); |
| 552 | 551 | wp_style_add_data( 'jetpack-carousel', 'rtl', 'replace' ); |
| 553 | 552 | |
| @@ -583,12 +582,13 @@ | ||
| 583 | 582 | $current_user = wp_get_current_user(); |
| 584 | 583 | $require_name_email = (int) get_option( 'require_name_email' ); |
| 585 | 584 | /* translators: %s is replaced with a field name in the form, e.g. "Email" */ |
| 586 | 585 | $required = ( $require_name_email ) ? __( '%s (Required)', 'jetpack' ) : '%s'; |
| 586 | + require_once JETPACK__PLUGIN_DIR . '_inc/lib/class-jetpack-spinner.php'; | |
| 587 | 587 | ?> |
| 588 | - <div id="jp-carousel-loading-overlay"> | |
| 588 | + <div id="jp-carousel-loading-overlay" style="display: none;"> | |
| 589 | 589 | <div id="jp-carousel-loading-wrapper"> |
| 590 | - <span id="jp-carousel-library-loading"> </span> | |
| 590 | + <span id="jp-carousel-library-loading"><?php echo Jetpack_Spinner::render( 40 ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- static SVG markup. ?></span> | |
| 591 | 591 | </div> |
| 592 | 592 | </div> |
| 593 | 593 | <div class="jp-carousel-overlay<?php echo( $is_light ? ' jp-carousel-light' : '' ); ?>" style="display: none;"> |
| 594 | 594 | |
| @@ -594,9 +594,9 @@ | ||
| 594 | 594 | |
| 595 | 595 | <div class="jp-carousel-container<?php echo( $is_light ? ' jp-carousel-light' : '' ); ?>"> |
| 596 | 596 | <!-- The Carousel Swiper --> |
| 597 | 597 | <div |
| 598 | - class="jp-carousel-wrap swiper-container jp-carousel-swiper-container jp-carousel-transitions" | |
| 598 | + class="jp-carousel-wrap swiper jp-carousel-swiper-container jp-carousel-transitions" | |
| 599 | 599 | itemscope |
| 600 | 600 | itemtype="https://schema.org/ImageGallery"> |
| 601 | 601 | <div class="jp-carousel swiper-wrapper"></div> |
| 602 | 602 | <div class="jp-swiper-button-prev swiper-button-prev"> |
| @@ -638,9 +638,9 @@ | ||
| 638 | 638 | <div class="jp-swiper-pagination swiper-pagination"></div> |
| 639 | 639 | <div class="jp-carousel-pagination"></div> |
| 640 | 640 | </div> |
| 641 | 641 | <div class="jp-carousel-photo-title-container"> |
| 642 | - <h2 class="jp-carousel-photo-caption"></h2> | |
| 642 | + <div class="jp-carousel-photo-caption"></div> | |
| 643 | 643 | </div> |
| 644 | 644 | <div class="jp-carousel-photo-icons-container"> |
| 645 | 645 | <a href="#" class="jp-carousel-icon-btn jp-carousel-icon-info" aria-label="<?php esc_attr_e( 'Toggle photo metadata visibility', 'jetpack' ); ?>"> |
| 646 | 646 | <span class="jp-carousel-icon"> |
| @@ -674,9 +674,9 @@ | ||
| 674 | 674 | </div> |
| 675 | 675 | <div class="jp-carousel-info-extra"> |
| 676 | 676 | <div class="jp-carousel-info-content-wrapper"> |
| 677 | 677 | <div class="jp-carousel-photo-title-container"> |
| 678 | - <h2 class="jp-carousel-photo-title"></h2> | |
| 678 | + <div class="jp-carousel-photo-title"></div> | |
| 679 | 679 | </div> |
| 680 | 680 | <div class="jp-carousel-comments-wrapper"> |
| 681 | 681 | <?php if ( $localize_strings['display_comments'] ) : ?> |
| 682 | 682 | <div id="jp-carousel-comments-loading"> |
| @@ -683,9 +683,9 @@ | ||
| 683 | 683 | <span><?php echo esc_html( $localize_strings['loading_comments'] ); ?></span> |
| 684 | 684 | </div> |
| 685 | 685 | <div class="jp-carousel-comments"></div> |
| 686 | 686 | <div id="jp-carousel-comment-form-container"> |
| 687 | - <span id="jp-carousel-comment-form-spinner"> </span> | |
| 687 | + <span id="jp-carousel-comment-form-spinner"><?php echo Jetpack_Spinner::render( 20 ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- static SVG markup. ?></span> | |
| 688 | 688 | <div id="jp-carousel-comment-post-results"></div> |
| 689 | 689 | <?php if ( $use_local_comments ) : ?> |
| 690 | 690 | <?php if ( ! $localize_strings['is_logged_in'] && $localize_strings['comment_registration'] ) : ?> |
| 691 | 691 | <div id="jp-carousel-comment-form-commenting-as"> |
| @@ -754,15 +754,14 @@ | ||
| 754 | 754 | </div> |
| 755 | 755 | <div class="jp-carousel-image-meta"> |
| 756 | 756 | <div class="jp-carousel-title-and-caption"> |
| 757 | 757 | <div class="jp-carousel-photo-info"> |
| 758 | - <h3 class="jp-carousel-caption" itemprop="caption description"></h3> | |
| 758 | + <div class="jp-carousel-caption" itemprop="caption description"></div> | |
| 759 | 759 | </div> |
| 760 | 760 | |
| 761 | 761 | <div class="jp-carousel-photo-description"></div> |
| 762 | 762 | </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;"> | |
| 763 | + <a class="jp-carousel-image-download" href="#" aria-label="<?php esc_attr_e( 'Download image', 'jetpack' ); ?>" target="_blank" style="display: none;"> | |
| 765 | 764 | <svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg"> |
| 766 | 765 | <mask id="mask0" mask-type="alpha" maskUnits="userSpaceOnUse" x="3" y="3" width="19" height="18"> |
| 767 | 766 | <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 | 767 | </mask> |
| @@ -813,8 +812,11 @@ | ||
| 813 | 812 | * @param string $content HTML content of the post. |
| 814 | 813 | * @return string |
| 815 | 814 | */ |
| 816 | 815 | public function add_data_img_tags_and_enqueue_assets( $content ) { |
| 816 | + if ( ! is_string( $content ) || $content === '' ) { | |
| 817 | + return ''; | |
| 818 | + } | |
| 817 | 819 | if ( |
| 818 | 820 | class_exists( 'Jetpack_AMP_Support' ) |
| 819 | 821 | && Jetpack_AMP_Support::is_amp_request() |
| 820 | 822 | ) { |
| @@ -825,11 +827,19 @@ | ||
| 825 | 827 | return $content; |
| 826 | 828 | } |
| 827 | 829 | $selected_images = array(); |
| 828 | 830 | foreach ( $matches[0] as $image_html ) { |
| 831 | + // This image already carries the attributes this method adds, so adding | |
| 832 | + // them again would emit every one of them twice. Tiled Gallery output | |
| 833 | + // reaches this filter twice: once as 'jetpack_tiled_galleries_block_content' | |
| 834 | + // from inside the block's render callback, and again as 'the_content' when | |
| 835 | + // single image galleries are enabled. See JETPACK-1990. | |
| 836 | + if ( str_contains( $image_html, 'data-attachment-id=' ) ) { | |
| 837 | + continue; | |
| 838 | + } | |
| 829 | 839 | if ( |
| 830 | 840 | preg_match( '/(wp-image-|data-id=)\"?([0-9]+)\"?/i', $image_html, $class_id ) |
| 831 | - && ! preg_match( '/wp-block-jetpack-slideshow_image/', $image_html ) | |
| 841 | + && ! str_contains( $image_html, 'wp-block-jetpack-slideshow_image' ) | |
| 832 | 842 | ) { |
| 833 | 843 | /** |
| 834 | 844 | * Allow filtering the attachment ID used to fetch and populate metadata about an image in a gallery. |
| 835 | 845 | * |
| @@ -881,9 +891,13 @@ | ||
| 881 | 891 | * |
| 882 | 892 | * This is meant as a relatively quick fix, as a better fix is likely to update the get_posts call above to only |
| 883 | 893 | * include attachments. |
| 884 | 894 | */ |
| 885 | - if ( ! isset( $attachment->ID ) || ! wp_attachment_is_image( $attachment->ID ) ) { | |
| 895 | + if ( | |
| 896 | + ! isset( $attachment->ID ) | |
| 897 | + || ! wp_attachment_is_image( $attachment->ID ) | |
| 898 | + || ! isset( $selected_images[ $attachment->ID ] ) | |
| 899 | + ) { | |
| 886 | 900 | continue; |
| 887 | 901 | } |
| 888 | 902 | $image_elements = $selected_images[ $attachment->ID ]; |
| 889 | 903 | |
| @@ -912,10 +926,10 @@ | ||
| 912 | 926 | * |
| 913 | 927 | * @see add_data_img_tags_and_enqueue_assets() |
| 914 | 928 | * @see https://developer.wordpress.org/reference/functions/wp_get_attachment_image/ Documentation about wp_get_attachment_image |
| 915 | 929 | * |
| 916 | - * @param string[] $attr Array of attribute values for the image markup, keyed by attribute name. | |
| 917 | - * @param WP_Post $attachment Image attachment post. | |
| 930 | + * @param string[] $attr Array of attribute values for the image markup, keyed by attribute name. | |
| 931 | + * @param null|WP_Post $attachment Image attachment post. | |
| 918 | 932 | * |
| 919 | 933 | * @return string[] Modified image attributes. |
| 920 | 934 | */ |
| 921 | 935 | public function add_data_to_images( $attr, $attachment = null ) { |
| @@ -925,19 +939,24 @@ | ||
| 925 | 939 | ) { |
| 926 | 940 | return $attr; |
| 927 | 941 | } |
| 928 | 942 | |
| 929 | - $attachment_id = (int) $attachment->ID; | |
| 930 | - if ( ! wp_attachment_is_image( $attachment_id ) ) { | |
| 943 | + if ( | |
| 944 | + ! $attachment instanceof WP_Post | |
| 945 | + || ! isset( $attachment->ID ) | |
| 946 | + || ! wp_attachment_is_image( $attachment ) | |
| 947 | + ) { | |
| 931 | 948 | return $attr; |
| 932 | 949 | } |
| 933 | 950 | |
| 951 | + $attachment_id = (int) $attachment->ID; | |
| 934 | 952 | $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 ); | |
| 953 | + $orig_file = $orig_file[0] ?? wp_get_attachment_url( $attachment_id ); | |
| 936 | 954 | $meta = wp_get_attachment_metadata( $attachment_id ); |
| 937 | 955 | $size = isset( $meta['width'] ) ? (int) $meta['width'] . ',' . (int) $meta['height'] : ''; |
| 938 | 956 | $img_meta = ( ! empty( $meta['image_meta'] ) ) ? (array) $meta['image_meta'] : array(); |
| 939 | 957 | $comments_opened = (int) comments_open( $attachment_id ); |
| 958 | + $display_exif = $this->test_1or0_option( Jetpack_Options::get_option_and_ensure_autoload( 'carousel_display_exif', true ) ); | |
| 940 | 959 | |
| 941 | 960 | /** |
| 942 | 961 | * Note: Cannot generate a filename from the width and height wp_get_attachment_image_src() returns because |
| 943 | 962 | * it takes the $content_width global variable themes can set in consideration, therefore returning sizes |
| @@ -952,39 +971,36 @@ | ||
| 952 | 971 | * EG with Twenty Ten activated: |
| 953 | 972 | * 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 | 973 | */ |
| 955 | 974 | |
| 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 | 975 | $large_file_info = wp_get_attachment_image_src( $attachment_id, 'large' ); |
| 960 | - $large_file = isset( $large_file_info[0] ) ? $large_file_info[0] : ''; | |
| 976 | + $large_file = $large_file_info[0] ?? ''; | |
| 961 | 977 | |
| 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 ) ) : ''; | |
| 978 | + $attachment_title = wptexturize( $attachment->post_title ); | |
| 979 | + $attachment_desc = wpautop( wptexturize( $attachment->post_content ) ); | |
| 980 | + $attachment_caption = wpautop( wptexturize( $attachment->post_excerpt ) ); | |
| 966 | 981 | |
| 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 | 982 | $attr['data-attachment-id'] = $attachment_id; |
| 975 | 983 | $attr['data-permalink'] = esc_attr( get_permalink( $attachment_id ) ); |
| 976 | 984 | $attr['data-orig-file'] = esc_attr( $orig_file ); |
| 977 | 985 | $attr['data-orig-size'] = $size; |
| 978 | 986 | $attr['data-comments-opened'] = $comments_opened; |
| 979 | - $attr['data-image-meta'] = esc_attr( $img_meta ); | |
| 987 | + | |
| 988 | + if ( $display_exif ) { | |
| 989 | + // See https://github.com/Automattic/jetpack/issues/2765. | |
| 990 | + if ( isset( $img_meta['keywords'] ) ) { | |
| 991 | + unset( $img_meta['keywords'] ); | |
| 992 | + } | |
| 993 | + | |
| 994 | + $img_meta = wp_json_encode( array_map( 'strval', array_filter( $img_meta, 'is_scalar' ) ), JSON_UNESCAPED_SLASHES | JSON_HEX_AMP ); | |
| 995 | + $attr['data-image-meta'] = esc_attr( $img_meta ); | |
| 996 | + } | |
| 997 | + | |
| 980 | 998 | // 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 | 999 | $attr['data-image-title'] = esc_attr( htmlspecialchars( $attachment_title, ENT_COMPAT ) ); |
| 982 | 1000 | $attr['data-image-description'] = esc_attr( htmlspecialchars( $attachment_desc, ENT_COMPAT ) ); |
| 983 | 1001 | $attr['data-image-caption'] = esc_attr( htmlspecialchars( $attachment_caption, ENT_COMPAT ) ); |
| 984 | - $attr['data-medium-file'] = esc_attr( $medium_file ); | |
| 985 | 1002 | $attr['data-large-file'] = esc_attr( $large_file ); |
| 986 | - | |
| 987 | 1003 | return $attr; |
| 988 | 1004 | } |
| 989 | 1005 | |
| 990 | 1006 | /** |
| @@ -1023,12 +1039,12 @@ | ||
| 1023 | 1039 | * @param array $extra_data Array of data about the site and the post. |
| 1024 | 1040 | */ |
| 1025 | 1041 | $extra_data = apply_filters( 'jp_carousel_add_data_to_container', $extra_data ); |
| 1026 | 1042 | 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 ); | |
| 1043 | + $html = str_replace( '<div ', '<div ' . esc_attr( $data_key ) . "='" . esc_attr( wp_json_encode( $data_values, JSON_HEX_AMP | JSON_UNESCAPED_SLASHES ) ) . "' ", $html ); | |
| 1044 | + $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 ); | |
| 1045 | + $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 ); | |
| 1046 | + $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 | 1047 | } |
| 1032 | 1048 | } |
| 1033 | 1049 | |
| 1034 | 1050 | return $html; |
| @@ -1073,9 +1089,9 @@ | ||
| 1073 | 1089 | |
| 1074 | 1090 | /** |
| 1075 | 1091 | * Retrieves comment information |
| 1076 | 1092 | * |
| 1077 | - * @return string | |
| 1093 | + * @return never | |
| 1078 | 1094 | */ |
| 1079 | 1095 | public function get_attachment_comments() { |
| 1080 | 1096 | if ( ! headers_sent() ) { |
| 1081 | 1097 | header( 'Content-type: text/javascript' ); |
| @@ -1099,11 +1115,11 @@ | ||
| 1099 | 1115 | |
| 1100 | 1116 | if ( ! $attachment_id ) { |
| 1101 | 1117 | wp_send_json_error( |
| 1102 | 1118 | __( 'Missing attachment ID.', 'jetpack' ), |
| 1103 | - 403 | |
| 1119 | + 403, | |
| 1120 | + JSON_UNESCAPED_SLASHES | |
| 1104 | 1121 | ); |
| 1105 | - return; | |
| 1106 | 1122 | } |
| 1107 | 1123 | |
| 1108 | 1124 | $attachment_post = get_post( $attachment_id ); |
| 1109 | 1125 | // If we have no info about that attachment, bail. |
| @@ -1109,11 +1125,11 @@ | ||
| 1109 | 1125 | // If we have no info about that attachment, bail. |
| 1110 | 1126 | if ( ! ( $attachment_post instanceof WP_Post ) ) { |
| 1111 | 1127 | wp_send_json_error( |
| 1112 | 1128 | __( 'Missing attachment info.', 'jetpack' ), |
| 1113 | - 403 | |
| 1129 | + 403, | |
| 1130 | + JSON_UNESCAPED_SLASHES | |
| 1114 | 1131 | ); |
| 1115 | - return; | |
| 1116 | 1132 | } |
| 1117 | 1133 | |
| 1118 | 1134 | // This AJAX call should only be used to fetch comments of attachments. |
| 1119 | 1135 | if ( 'attachment' !== $attachment_post->post_type ) { |
| @@ -1118,11 +1134,11 @@ | ||
| 1118 | 1134 | // This AJAX call should only be used to fetch comments of attachments. |
| 1119 | 1135 | if ( 'attachment' !== $attachment_post->post_type ) { |
| 1120 | 1136 | wp_send_json_error( |
| 1121 | 1137 | __( 'You aren’t authorized to do that.', 'jetpack' ), |
| 1122 | - 403 | |
| 1138 | + 403, | |
| 1139 | + JSON_UNESCAPED_SLASHES | |
| 1123 | 1140 | ); |
| 1124 | - return; | |
| 1125 | 1141 | } |
| 1126 | 1142 | |
| 1127 | 1143 | $parent_post = get_post_parent( $attachment_id ); |
| 1128 | 1144 | |
| @@ -1140,11 +1156,11 @@ | ||
| 1140 | 1156 | $current_user = wp_get_current_user(); |
| 1141 | 1157 | if ( ! ( $current_user instanceof WP_User ) ) { |
| 1142 | 1158 | wp_send_json_error( |
| 1143 | 1159 | __( 'Missing user info.', 'jetpack' ), |
| 1144 | - 403 | |
| 1160 | + 403, | |
| 1161 | + JSON_UNESCAPED_SLASHES | |
| 1145 | 1162 | ); |
| 1146 | - return; | |
| 1147 | 1163 | } |
| 1148 | 1164 | |
| 1149 | 1165 | /* |
| 1150 | 1166 | * If a post is private / draft |
| @@ -1156,11 +1172,11 @@ | ||
| 1156 | 1172 | && ! current_user_can( 'read_post', $parent_post->ID ) |
| 1157 | 1173 | ) { |
| 1158 | 1174 | wp_send_json_error( |
| 1159 | 1175 | __( 'You aren’t authorized to do that.', 'jetpack' ), |
| 1160 | - 403 | |
| 1176 | + 403, | |
| 1177 | + JSON_UNESCAPED_SLASHES | |
| 1161 | 1178 | ); |
| 1162 | - return; | |
| 1163 | 1179 | } |
| 1164 | 1180 | } |
| 1165 | 1181 | |
| 1166 | 1182 | if ( $offset < 1 ) { |
| @@ -1194,9 +1210,9 @@ | ||
| 1194 | 1210 | 'content' => wpautop( $comment->comment_content ), |
| 1195 | 1211 | ); |
| 1196 | 1212 | } |
| 1197 | 1213 | |
| 1198 | - die( wp_json_encode( $out ) ); | |
| 1214 | + wp_send_json( $out, null, JSON_UNESCAPED_SLASHES ); | |
| 1199 | 1215 | } |
| 1200 | 1216 | |
| 1201 | 1217 | /** |
| 1202 | 1218 | * Adds a new comment to the database |
| @@ -1208,9 +1224,9 @@ | ||
| 1208 | 1224 | header( 'Content-type: text/javascript' ); |
| 1209 | 1225 | } |
| 1210 | 1226 | |
| 1211 | 1227 | 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' ) ) ) ); | |
| 1228 | + die( wp_json_encode( array( 'error' => __( 'Nonce verification failed.', 'jetpack' ) ), JSON_UNESCAPED_SLASHES ) ); | |
| 1213 | 1229 | } |
| 1214 | 1230 | |
| 1215 | 1231 | $_blog_id = isset( $_POST['blog_id'] ) ? (int) $_POST['blog_id'] : 0; |
| 1216 | 1232 | $_post_id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0; |
| @@ -1216,17 +1232,17 @@ | ||
| 1216 | 1232 | $_post_id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0; |
| 1217 | 1233 | $comment = isset( $_POST['comment'] ) ? filter_var( wp_unslash( $_POST['comment'] ) ) : null; |
| 1218 | 1234 | |
| 1219 | 1235 | if ( empty( $_blog_id ) ) { |
| 1220 | - die( wp_json_encode( array( 'error' => __( 'Missing target blog ID.', 'jetpack' ) ) ) ); | |
| 1236 | + die( wp_json_encode( array( 'error' => __( 'Missing target blog ID.', 'jetpack' ) ), JSON_UNESCAPED_SLASHES ) ); | |
| 1221 | 1237 | } |
| 1222 | 1238 | |
| 1223 | 1239 | if ( empty( $_post_id ) ) { |
| 1224 | - die( wp_json_encode( array( 'error' => __( 'Missing target post ID.', 'jetpack' ) ) ) ); | |
| 1240 | + die( wp_json_encode( array( 'error' => __( 'Missing target post ID.', 'jetpack' ) ), JSON_UNESCAPED_SLASHES ) ); | |
| 1225 | 1241 | } |
| 1226 | 1242 | |
| 1227 | 1243 | if ( empty( $comment ) ) { |
| 1228 | - die( wp_json_encode( array( 'error' => __( 'No comment text was submitted.', 'jetpack' ) ) ) ); | |
| 1244 | + die( wp_json_encode( array( 'error' => __( 'No comment text was submitted.', 'jetpack' ) ), JSON_UNESCAPED_SLASHES ) ); | |
| 1229 | 1245 | } |
| 1230 | 1246 | |
| 1231 | 1247 | // Used in context like NewDash. |
| 1232 | 1248 | $switched = false; |
| @@ -1241,9 +1257,9 @@ | ||
| 1241 | 1257 | if ( ! comments_open( $_post_id ) ) { |
| 1242 | 1258 | if ( $switched ) { |
| 1243 | 1259 | restore_current_blog(); |
| 1244 | 1260 | } |
| 1245 | - die( wp_json_encode( array( 'error' => __( 'Comments on this post are closed.', 'jetpack' ) ) ) ); | |
| 1261 | + die( wp_json_encode( array( 'error' => __( 'Comments on this post are closed.', 'jetpack' ) ), JSON_UNESCAPED_SLASHES ) ); | |
| 1246 | 1262 | } |
| 1247 | 1263 | |
| 1248 | 1264 | if ( is_user_logged_in() ) { |
| 1249 | 1265 | $user = wp_get_current_user(); |
| @@ -1255,15 +1271,18 @@ | ||
| 1255 | 1271 | if ( empty( $user_id ) ) { |
| 1256 | 1272 | if ( $switched ) { |
| 1257 | 1273 | restore_current_blog(); |
| 1258 | 1274 | } |
| 1259 | - die( wp_json_encode( array( 'error' => __( 'Sorry, but we could not authenticate your request.', 'jetpack' ) ) ) ); | |
| 1275 | + die( wp_json_encode( array( 'error' => __( 'Sorry, but we could not authenticate your request.', 'jetpack' ) ), JSON_UNESCAPED_SLASHES ) ); | |
| 1260 | 1276 | } |
| 1261 | 1277 | } else { |
| 1262 | 1278 | $user_id = 0; |
| 1263 | 1279 | $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; | |
| 1280 | + $email = null; | |
| 1281 | + if ( isset( $_POST['email'] ) && is_string( $_POST['email'] ) ) { | |
| 1282 | + $email = wp_unslash( $_POST['email'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Checked or sanitized below. | |
| 1283 | + } | |
| 1284 | + $url = isset( $_POST['url'] ) && is_string( $_POST['url'] ) ? esc_url_raw( wp_unslash( $_POST['url'] ) ) : null; | |
| 1266 | 1285 | |
| 1267 | 1286 | if ( get_option( 'require_name_email' ) ) { |
| 1268 | 1287 | if ( empty( $display_name ) ) { |
| 1269 | 1288 | if ( $switched ) { |
| @@ -1268,9 +1287,9 @@ | ||
| 1268 | 1287 | if ( empty( $display_name ) ) { |
| 1269 | 1288 | if ( $switched ) { |
| 1270 | 1289 | restore_current_blog(); |
| 1271 | 1290 | } |
| 1272 | - die( wp_json_encode( array( 'error' => __( 'Please provide your name.', 'jetpack' ) ) ) ); | |
| 1291 | + die( wp_json_encode( array( 'error' => __( 'Please provide your name.', 'jetpack' ) ), JSON_UNESCAPED_SLASHES ) ); | |
| 1273 | 1292 | } |
| 1274 | 1293 | |
| 1275 | 1294 | if ( empty( $email ) ) { |
| 1276 | 1295 | if ( $switched ) { |
| @@ -1275,9 +1294,9 @@ | ||
| 1275 | 1294 | if ( empty( $email ) ) { |
| 1276 | 1295 | if ( $switched ) { |
| 1277 | 1296 | restore_current_blog(); |
| 1278 | 1297 | } |
| 1279 | - die( wp_json_encode( array( 'error' => __( 'Please provide an email address.', 'jetpack' ) ) ) ); | |
| 1298 | + die( wp_json_encode( array( 'error' => __( 'Please provide an email address.', 'jetpack' ) ), JSON_UNESCAPED_SLASHES ) ); | |
| 1280 | 1299 | } |
| 1281 | 1300 | |
| 1282 | 1301 | if ( ! is_email( $email ) ) { |
| 1283 | 1302 | if ( $switched ) { |
| @@ -1282,9 +1301,9 @@ | ||
| 1282 | 1301 | if ( ! is_email( $email ) ) { |
| 1283 | 1302 | if ( $switched ) { |
| 1284 | 1303 | restore_current_blog(); |
| 1285 | 1304 | } |
| 1286 | - die( wp_json_encode( array( 'error' => __( 'Please provide a valid email address.', 'jetpack' ) ) ) ); | |
| 1305 | + die( wp_json_encode( array( 'error' => __( 'Please provide a valid email address.', 'jetpack' ) ), JSON_UNESCAPED_SLASHES ) ); | |
| 1287 | 1306 | } |
| 1288 | 1307 | } else { |
| 1289 | 1308 | $email = $email !== null ? sanitize_email( $email ) : null; |
| 1290 | 1309 | } |
| @@ -1325,9 +1344,10 @@ | ||
| 1325 | 1344 | wp_json_encode( |
| 1326 | 1345 | array( |
| 1327 | 1346 | 'comment_id' => $comment_id, |
| 1328 | 1347 | 'comment_status' => $comment_status, |
| 1329 | - ) | |
| 1348 | + ), | |
| 1349 | + JSON_UNESCAPED_SLASHES | |
| 1330 | 1350 | ) |
| 1331 | 1351 | ); |
| 1332 | 1352 | } |
| 1333 | 1353 | |
| @@ -1455,9 +1475,9 @@ | ||
| 1455 | 1475 | * Sanitize input for the `carousel_display_exif` setting. |
| 1456 | 1476 | * |
| 1457 | 1477 | * @param mixed $value User input setting value. |
| 1458 | 1478 | * |
| 1459 | - * @return number Sanitized value, only 1 or 0. | |
| 1479 | + * @return int Sanitized value, only 1 or 0. | |
| 1460 | 1480 | */ |
| 1461 | 1481 | public function carousel_display_exif_sanitize( $value ) { |
| 1462 | 1482 | return $this->sanitize_1or0_option( $value ); |
| 1463 | 1483 | } |
| @@ -1464,11 +1484,11 @@ | ||
| 1464 | 1484 | |
| 1465 | 1485 | /** |
| 1466 | 1486 | * Return sanitized option for value that controls whether comments will be hidden or not. |
| 1467 | 1487 | * |
| 1468 | - * @param number $value Value to sanitize. | |
| 1488 | + * @param mixed $value Value to sanitize. | |
| 1469 | 1489 | * |
| 1470 | - * @return number Sanitized value, only 1 or 0. | |
| 1490 | + * @return int Sanitized value, only 1 or 0. | |
| 1471 | 1491 | */ |
| 1472 | 1492 | public function carousel_display_comments_sanitize( $value ) { |
| 1473 | 1493 | return $this->sanitize_1or0_option( $value ); |
| 1474 | 1494 | } |
| @@ -1508,9 +1528,9 @@ | ||
| 1508 | 1528 | * Sanitize input for the `carousel_enable_it` setting. |
| 1509 | 1529 | * |
| 1510 | 1530 | * @param mixed $value User input. |
| 1511 | 1531 | * |
| 1512 | - * @return number Sanitized value, only 1 or 0. | |
| 1532 | + * @return int Sanitized value, only 1 or 0. | |
| 1513 | 1533 | */ |
| 1514 | 1534 | public function carousel_enable_it_sanitize( $value ) { |
| 1515 | 1535 | return $this->sanitize_1or0_option( $value ); |
| 1516 | 1536 | } |