| @@ -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,9 +85,9 @@ | ||
| 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 | |
| @@ -122,15 +130,12 @@ | ||
| 122 | 130 | if ( $this->single_image_gallery_enabled ) { |
| 123 | 131 | add_filter( 'the_content', array( $this, 'add_data_img_tags_and_enqueue_assets' ) ); |
| 124 | 132 | } |
| 125 | 133 | |
| 126 | - if ( | |
| 127 | - ! class_exists( 'Jetpack_AMP_Support' ) | |
| 128 | - || ! Jetpack_AMP_Support::is_amp_request() | |
| 129 | - ) { | |
| 130 | - add_filter( 'render_block_core/gallery', array( $this, 'filter_gallery_block_render' ), 10, 2 ); | |
| 131 | - add_filter( 'render_block_jetpack/tiled-gallery', array( $this, 'filter_gallery_block_render' ), 10, 2 ); | |
| 132 | - } | |
| 134 | + add_filter( 'render_block_data', array( $this, 'remove_core_lightbox_in_gallery' ), 10, 3 ); | |
| 135 | + | |
| 136 | + // `is_amp_request()` can't be called until the 'wp' filter. | |
| 137 | + add_action( 'wp', array( $this, 'check_amp_support' ) ); | |
| 133 | 138 | } |
| 134 | 139 | |
| 135 | 140 | if ( $this->in_jetpack ) { |
| 136 | 141 | Jetpack::enable_module_configurable( dirname( __DIR__ ) . '/carousel.php' ); |
| @@ -137,8 +142,21 @@ | ||
| 137 | 142 | } |
| 138 | 143 | } |
| 139 | 144 | |
| 140 | 145 | /** |
| 146 | + * Check AMP and add filters. | |
| 147 | + */ | |
| 148 | + public function check_amp_support() { | |
| 149 | + if ( | |
| 150 | + ! class_exists( 'Jetpack_AMP_Support' ) | |
| 151 | + || ! Jetpack_AMP_Support::is_amp_request() | |
| 152 | + ) { | |
| 153 | + add_filter( 'render_block_core/gallery', array( $this, 'filter_gallery_block_render' ), 10, 2 ); | |
| 154 | + add_filter( 'render_block_jetpack/tiled-gallery', array( $this, 'filter_gallery_block_render' ), 10, 2 ); | |
| 155 | + } | |
| 156 | + } | |
| 157 | + | |
| 158 | + /** | |
| 141 | 159 | * Returns the value of the applied jp_carousel_maybe_disable filter |
| 142 | 160 | * |
| 143 | 161 | * @since 1.6.0 |
| 144 | 162 | * |
| @@ -326,8 +344,28 @@ | ||
| 326 | 344 | return $content; |
| 327 | 345 | } |
| 328 | 346 | |
| 329 | 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 | + /** | |
| 330 | 368 | * Enrich the gallery block content using the render_block_{$this->name} filter. |
| 331 | 369 | * This function is triggered after block render to make sure we track galleries within |
| 332 | 370 | * reusable blocks. |
| 333 | 371 | * |
| @@ -347,9 +385,9 @@ | ||
| 347 | 385 | } |
| 348 | 386 | |
| 349 | 387 | $this->enqueue_assets(); |
| 350 | 388 | |
| 351 | - if ( ! isset( $post ) ) { | |
| 389 | + if ( ! $post instanceof WP_Post ) { | |
| 352 | 390 | return $block_content; |
| 353 | 391 | } |
| 354 | 392 | |
| 355 | 393 | $blog_id = (int) get_current_blog_id(); |
| @@ -380,9 +418,9 @@ | ||
| 380 | 418 | $extra_attributes = implode( |
| 381 | 419 | ' ', |
| 382 | 420 | array_map( |
| 383 | 421 | function ( $data_key, $data_values ) { |
| 384 | - 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 ) ) . "'"; | |
| 385 | 423 | }, |
| 386 | 424 | array_keys( $extra_data ), |
| 387 | 425 | array_values( $extra_data ) |
| 388 | 426 | ) |
| @@ -413,12 +451,9 @@ | ||
| 413 | 451 | true |
| 414 | 452 | ); |
| 415 | 453 | |
| 416 | 454 | $swiper_library_path = array( |
| 417 | - 'url' => Assets::get_file_url_for_environment( | |
| 418 | - '_inc/build/carousel/swiper-bundle.min.js', | |
| 419 | - 'modules/carousel/swiper-bundle.js' | |
| 420 | - ), | |
| 455 | + 'url' => plugins_url( '_inc/blocks/swiper.js', JETPACK__PLUGIN_FILE ), | |
| 421 | 456 | ); |
| 422 | 457 | wp_localize_script( 'jetpack-carousel', 'jetpackSwiperLibraryPath', $swiper_library_path ); |
| 423 | 458 | |
| 424 | 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). |
| @@ -440,8 +475,9 @@ | ||
| 440 | 475 | 'comment' => __( 'Comment', 'jetpack' ), |
| 441 | 476 | 'post_comment' => __( 'Post Comment', 'jetpack' ), |
| 442 | 477 | 'write_comment' => __( 'Write a Comment...', 'jetpack' ), |
| 443 | 478 | 'loading_comments' => __( 'Loading Comments...', 'jetpack' ), |
| 479 | + 'image_label' => __( 'Open image in full-screen.', 'jetpack' ), | |
| 444 | 480 | 'download_original' => sprintf( |
| 445 | 481 | /* translators: %1s is the full-size image width, and %2s is the height. */ |
| 446 | 482 | __( 'View full size <span class="photo-size">%1$s<span class="photo-size-times">×</span>%2$s</span>', 'jetpack' ), |
| 447 | 483 | '{0}', |
| @@ -505,12 +541,12 @@ | ||
| 505 | 541 | */ |
| 506 | 542 | $localize_strings = apply_filters( 'jp_carousel_localize_strings', $localize_strings ); |
| 507 | 543 | wp_localize_script( 'jetpack-carousel', 'jetpackCarouselStrings', $localize_strings ); |
| 508 | 544 | wp_enqueue_style( |
| 509 | - 'jetpack-carousel-swiper-css', | |
| 510 | - plugins_url( 'swiper-bundle.css', __FILE__ ), | |
| 545 | + 'jetpack-swiper-library', | |
| 546 | + plugins_url( '_inc/blocks/swiper.css', JETPACK__PLUGIN_FILE ), | |
| 511 | 547 | array(), |
| 512 | - $this->asset_version( JETPACK__VERSION ) | |
| 548 | + JETPACK__VERSION | |
| 513 | 549 | ); |
| 514 | 550 | wp_enqueue_style( 'jetpack-carousel', plugins_url( 'jetpack-carousel.css', __FILE__ ), array(), $this->asset_version( JETPACK__VERSION ) ); |
| 515 | 551 | wp_style_add_data( 'jetpack-carousel', 'rtl', 'replace' ); |
| 516 | 552 | |
| @@ -546,12 +582,13 @@ | ||
| 546 | 582 | $current_user = wp_get_current_user(); |
| 547 | 583 | $require_name_email = (int) get_option( 'require_name_email' ); |
| 548 | 584 | /* translators: %s is replaced with a field name in the form, e.g. "Email" */ |
| 549 | 585 | $required = ( $require_name_email ) ? __( '%s (Required)', 'jetpack' ) : '%s'; |
| 586 | + require_once JETPACK__PLUGIN_DIR . '_inc/lib/class-jetpack-spinner.php'; | |
| 550 | 587 | ?> |
| 551 | - <div id="jp-carousel-loading-overlay"> | |
| 588 | + <div id="jp-carousel-loading-overlay" style="display: none;"> | |
| 552 | 589 | <div id="jp-carousel-loading-wrapper"> |
| 553 | - <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> | |
| 554 | 591 | </div> |
| 555 | 592 | </div> |
| 556 | 593 | <div class="jp-carousel-overlay<?php echo( $is_light ? ' jp-carousel-light' : '' ); ?>" style="display: none;"> |
| 557 | 594 | |
| @@ -557,9 +594,9 @@ | ||
| 557 | 594 | |
| 558 | 595 | <div class="jp-carousel-container<?php echo( $is_light ? ' jp-carousel-light' : '' ); ?>"> |
| 559 | 596 | <!-- The Carousel Swiper --> |
| 560 | 597 | <div |
| 561 | - 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" | |
| 562 | 599 | itemscope |
| 563 | 600 | itemtype="https://schema.org/ImageGallery"> |
| 564 | 601 | <div class="jp-carousel swiper-wrapper"></div> |
| 565 | 602 | <div class="jp-swiper-button-prev swiper-button-prev"> |
| @@ -601,9 +638,9 @@ | ||
| 601 | 638 | <div class="jp-swiper-pagination swiper-pagination"></div> |
| 602 | 639 | <div class="jp-carousel-pagination"></div> |
| 603 | 640 | </div> |
| 604 | 641 | <div class="jp-carousel-photo-title-container"> |
| 605 | - <h2 class="jp-carousel-photo-caption"></h2> | |
| 642 | + <div class="jp-carousel-photo-caption"></div> | |
| 606 | 643 | </div> |
| 607 | 644 | <div class="jp-carousel-photo-icons-container"> |
| 608 | 645 | <a href="#" class="jp-carousel-icon-btn jp-carousel-icon-info" aria-label="<?php esc_attr_e( 'Toggle photo metadata visibility', 'jetpack' ); ?>"> |
| 609 | 646 | <span class="jp-carousel-icon"> |
| @@ -637,9 +674,9 @@ | ||
| 637 | 674 | </div> |
| 638 | 675 | <div class="jp-carousel-info-extra"> |
| 639 | 676 | <div class="jp-carousel-info-content-wrapper"> |
| 640 | 677 | <div class="jp-carousel-photo-title-container"> |
| 641 | - <h2 class="jp-carousel-photo-title"></h2> | |
| 678 | + <div class="jp-carousel-photo-title"></div> | |
| 642 | 679 | </div> |
| 643 | 680 | <div class="jp-carousel-comments-wrapper"> |
| 644 | 681 | <?php if ( $localize_strings['display_comments'] ) : ?> |
| 645 | 682 | <div id="jp-carousel-comments-loading"> |
| @@ -646,9 +683,9 @@ | ||
| 646 | 683 | <span><?php echo esc_html( $localize_strings['loading_comments'] ); ?></span> |
| 647 | 684 | </div> |
| 648 | 685 | <div class="jp-carousel-comments"></div> |
| 649 | 686 | <div id="jp-carousel-comment-form-container"> |
| 650 | - <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> | |
| 651 | 688 | <div id="jp-carousel-comment-post-results"></div> |
| 652 | 689 | <?php if ( $use_local_comments ) : ?> |
| 653 | 690 | <?php if ( ! $localize_strings['is_logged_in'] && $localize_strings['comment_registration'] ) : ?> |
| 654 | 691 | <div id="jp-carousel-comment-form-commenting-as"> |
| @@ -717,15 +754,14 @@ | ||
| 717 | 754 | </div> |
| 718 | 755 | <div class="jp-carousel-image-meta"> |
| 719 | 756 | <div class="jp-carousel-title-and-caption"> |
| 720 | 757 | <div class="jp-carousel-photo-info"> |
| 721 | - <h3 class="jp-carousel-caption" itemprop="caption description"></h3> | |
| 758 | + <div class="jp-carousel-caption" itemprop="caption description"></div> | |
| 722 | 759 | </div> |
| 723 | 760 | |
| 724 | 761 | <div class="jp-carousel-photo-description"></div> |
| 725 | 762 | </div> |
| 726 | - <ul class="jp-carousel-image-exif" style="display: none;"></ul> | |
| 727 | - <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;"> | |
| 728 | 764 | <svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg"> |
| 729 | 765 | <mask id="mask0" mask-type="alpha" maskUnits="userSpaceOnUse" x="3" y="3" width="19" height="18"> |
| 730 | 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"/> |
| 731 | 767 | </mask> |
| @@ -776,8 +812,11 @@ | ||
| 776 | 812 | * @param string $content HTML content of the post. |
| 777 | 813 | * @return string |
| 778 | 814 | */ |
| 779 | 815 | public function add_data_img_tags_and_enqueue_assets( $content ) { |
| 816 | + if ( ! is_string( $content ) || $content === '' ) { | |
| 817 | + return ''; | |
| 818 | + } | |
| 780 | 819 | if ( |
| 781 | 820 | class_exists( 'Jetpack_AMP_Support' ) |
| 782 | 821 | && Jetpack_AMP_Support::is_amp_request() |
| 783 | 822 | ) { |
| @@ -788,11 +827,19 @@ | ||
| 788 | 827 | return $content; |
| 789 | 828 | } |
| 790 | 829 | $selected_images = array(); |
| 791 | 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 | + } | |
| 792 | 839 | if ( |
| 793 | 840 | preg_match( '/(wp-image-|data-id=)\"?([0-9]+)\"?/i', $image_html, $class_id ) |
| 794 | - && ! preg_match( '/wp-block-jetpack-slideshow_image/', $image_html ) | |
| 841 | + && ! str_contains( $image_html, 'wp-block-jetpack-slideshow_image' ) | |
| 795 | 842 | ) { |
| 796 | 843 | /** |
| 797 | 844 | * Allow filtering the attachment ID used to fetch and populate metadata about an image in a gallery. |
| 798 | 845 | * |
| @@ -844,9 +891,13 @@ | ||
| 844 | 891 | * |
| 845 | 892 | * This is meant as a relatively quick fix, as a better fix is likely to update the get_posts call above to only |
| 846 | 893 | * include attachments. |
| 847 | 894 | */ |
| 848 | - 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 | + ) { | |
| 849 | 900 | continue; |
| 850 | 901 | } |
| 851 | 902 | $image_elements = $selected_images[ $attachment->ID ]; |
| 852 | 903 | |
| @@ -875,10 +926,10 @@ | ||
| 875 | 926 | * |
| 876 | 927 | * @see add_data_img_tags_and_enqueue_assets() |
| 877 | 928 | * @see https://developer.wordpress.org/reference/functions/wp_get_attachment_image/ Documentation about wp_get_attachment_image |
| 878 | 929 | * |
| 879 | - * @param string[] $attr Array of attribute values for the image markup, keyed by attribute name. | |
| 880 | - * @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. | |
| 881 | 932 | * |
| 882 | 933 | * @return string[] Modified image attributes. |
| 883 | 934 | */ |
| 884 | 935 | public function add_data_to_images( $attr, $attachment = null ) { |
| @@ -888,19 +939,24 @@ | ||
| 888 | 939 | ) { |
| 889 | 940 | return $attr; |
| 890 | 941 | } |
| 891 | 942 | |
| 892 | - $attachment_id = (int) $attachment->ID; | |
| 893 | - 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 | + ) { | |
| 894 | 948 | return $attr; |
| 895 | 949 | } |
| 896 | 950 | |
| 951 | + $attachment_id = (int) $attachment->ID; | |
| 897 | 952 | $orig_file = wp_get_attachment_image_src( $attachment_id, 'full' ); |
| 898 | - $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 ); | |
| 899 | 954 | $meta = wp_get_attachment_metadata( $attachment_id ); |
| 900 | 955 | $size = isset( $meta['width'] ) ? (int) $meta['width'] . ',' . (int) $meta['height'] : ''; |
| 901 | 956 | $img_meta = ( ! empty( $meta['image_meta'] ) ) ? (array) $meta['image_meta'] : array(); |
| 902 | 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 ) ); | |
| 903 | 959 | |
| 904 | 960 | /** |
| 905 | 961 | * Note: Cannot generate a filename from the width and height wp_get_attachment_image_src() returns because |
| 906 | 962 | * it takes the $content_width global variable themes can set in consideration, therefore returning sizes |
| @@ -915,39 +971,36 @@ | ||
| 915 | 971 | * EG with Twenty Ten activated: |
| 916 | 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) } |
| 917 | 973 | */ |
| 918 | 974 | |
| 919 | - $medium_file_info = wp_get_attachment_image_src( $attachment_id, 'medium' ); | |
| 920 | - $medium_file = isset( $medium_file_info[0] ) ? $medium_file_info[0] : ''; | |
| 921 | - | |
| 922 | 975 | $large_file_info = wp_get_attachment_image_src( $attachment_id, 'large' ); |
| 923 | - $large_file = isset( $large_file_info[0] ) ? $large_file_info[0] : ''; | |
| 976 | + $large_file = $large_file_info[0] ?? ''; | |
| 924 | 977 | |
| 925 | - $attachment = get_post( $attachment_id ); | |
| 926 | - $attachment_title = ! empty( $attachment ) ? wptexturize( $attachment->post_title ) : ''; | |
| 927 | - $attachment_desc = ! empty( $attachment ) ? wpautop( wptexturize( $attachment->post_content ) ) : ''; | |
| 928 | - $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 ) ); | |
| 929 | 981 | |
| 930 | - // See https://github.com/Automattic/jetpack/issues/2765. | |
| 931 | - if ( isset( $img_meta['keywords'] ) ) { | |
| 932 | - unset( $img_meta['keywords'] ); | |
| 933 | - } | |
| 934 | - | |
| 935 | - $img_meta = wp_json_encode( array_map( 'strval', array_filter( $img_meta, 'is_scalar' ) ) ); | |
| 936 | - | |
| 937 | 982 | $attr['data-attachment-id'] = $attachment_id; |
| 938 | 983 | $attr['data-permalink'] = esc_attr( get_permalink( $attachment_id ) ); |
| 939 | 984 | $attr['data-orig-file'] = esc_attr( $orig_file ); |
| 940 | 985 | $attr['data-orig-size'] = $size; |
| 941 | 986 | $attr['data-comments-opened'] = $comments_opened; |
| 942 | - $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 | + | |
| 943 | 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. |
| 944 | 999 | $attr['data-image-title'] = esc_attr( htmlspecialchars( $attachment_title, ENT_COMPAT ) ); |
| 945 | 1000 | $attr['data-image-description'] = esc_attr( htmlspecialchars( $attachment_desc, ENT_COMPAT ) ); |
| 946 | 1001 | $attr['data-image-caption'] = esc_attr( htmlspecialchars( $attachment_caption, ENT_COMPAT ) ); |
| 947 | - $attr['data-medium-file'] = esc_attr( $medium_file ); | |
| 948 | 1002 | $attr['data-large-file'] = esc_attr( $large_file ); |
| 949 | - | |
| 950 | 1003 | return $attr; |
| 951 | 1004 | } |
| 952 | 1005 | |
| 953 | 1006 | /** |
| @@ -986,12 +1039,12 @@ | ||
| 986 | 1039 | * @param array $extra_data Array of data about the site and the post. |
| 987 | 1040 | */ |
| 988 | 1041 | $extra_data = apply_filters( 'jp_carousel_add_data_to_container', $extra_data ); |
| 989 | 1042 | foreach ( (array) $extra_data as $data_key => $data_values ) { |
| 990 | - $html = str_replace( '<div ', '<div ' . esc_attr( $data_key ) . "='" . wp_json_encode( $data_values ) . "' ", $html ); | |
| 991 | - $html = str_replace( '<ul class="wp-block-gallery', '<ul ' . esc_attr( $data_key ) . "='" . wp_json_encode( $data_values ) . "' class=\"wp-block-gallery", $html ); | |
| 992 | - $html = str_replace( '<ul class="blocks-gallery-grid', '<ul ' . esc_attr( $data_key ) . "='" . wp_json_encode( $data_values ) . "' class=\"blocks-gallery-grid", $html ); | |
| 993 | - $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 ); | |
| 994 | 1047 | } |
| 995 | 1048 | } |
| 996 | 1049 | |
| 997 | 1050 | return $html; |
| @@ -1036,9 +1089,9 @@ | ||
| 1036 | 1089 | |
| 1037 | 1090 | /** |
| 1038 | 1091 | * Retrieves comment information |
| 1039 | 1092 | * |
| 1040 | - * @return string | |
| 1093 | + * @return never | |
| 1041 | 1094 | */ |
| 1042 | 1095 | public function get_attachment_comments() { |
| 1043 | 1096 | if ( ! headers_sent() ) { |
| 1044 | 1097 | header( 'Content-type: text/javascript' ); |
| @@ -1062,11 +1115,11 @@ | ||
| 1062 | 1115 | |
| 1063 | 1116 | if ( ! $attachment_id ) { |
| 1064 | 1117 | wp_send_json_error( |
| 1065 | 1118 | __( 'Missing attachment ID.', 'jetpack' ), |
| 1066 | - 403 | |
| 1119 | + 403, | |
| 1120 | + JSON_UNESCAPED_SLASHES | |
| 1067 | 1121 | ); |
| 1068 | - return; | |
| 1069 | 1122 | } |
| 1070 | 1123 | |
| 1071 | 1124 | $attachment_post = get_post( $attachment_id ); |
| 1072 | 1125 | // If we have no info about that attachment, bail. |
| @@ -1072,11 +1125,11 @@ | ||
| 1072 | 1125 | // If we have no info about that attachment, bail. |
| 1073 | 1126 | if ( ! ( $attachment_post instanceof WP_Post ) ) { |
| 1074 | 1127 | wp_send_json_error( |
| 1075 | 1128 | __( 'Missing attachment info.', 'jetpack' ), |
| 1076 | - 403 | |
| 1129 | + 403, | |
| 1130 | + JSON_UNESCAPED_SLASHES | |
| 1077 | 1131 | ); |
| 1078 | - return; | |
| 1079 | 1132 | } |
| 1080 | 1133 | |
| 1081 | 1134 | // This AJAX call should only be used to fetch comments of attachments. |
| 1082 | 1135 | if ( 'attachment' !== $attachment_post->post_type ) { |
| @@ -1081,11 +1134,11 @@ | ||
| 1081 | 1134 | // This AJAX call should only be used to fetch comments of attachments. |
| 1082 | 1135 | if ( 'attachment' !== $attachment_post->post_type ) { |
| 1083 | 1136 | wp_send_json_error( |
| 1084 | 1137 | __( 'You aren’t authorized to do that.', 'jetpack' ), |
| 1085 | - 403 | |
| 1138 | + 403, | |
| 1139 | + JSON_UNESCAPED_SLASHES | |
| 1086 | 1140 | ); |
| 1087 | - return; | |
| 1088 | 1141 | } |
| 1089 | 1142 | |
| 1090 | 1143 | $parent_post = get_post_parent( $attachment_id ); |
| 1091 | 1144 | |
| @@ -1103,11 +1156,11 @@ | ||
| 1103 | 1156 | $current_user = wp_get_current_user(); |
| 1104 | 1157 | if ( ! ( $current_user instanceof WP_User ) ) { |
| 1105 | 1158 | wp_send_json_error( |
| 1106 | 1159 | __( 'Missing user info.', 'jetpack' ), |
| 1107 | - 403 | |
| 1160 | + 403, | |
| 1161 | + JSON_UNESCAPED_SLASHES | |
| 1108 | 1162 | ); |
| 1109 | - return; | |
| 1110 | 1163 | } |
| 1111 | 1164 | |
| 1112 | 1165 | /* |
| 1113 | 1166 | * If a post is private / draft |
| @@ -1119,11 +1172,11 @@ | ||
| 1119 | 1172 | && ! current_user_can( 'read_post', $parent_post->ID ) |
| 1120 | 1173 | ) { |
| 1121 | 1174 | wp_send_json_error( |
| 1122 | 1175 | __( 'You aren’t authorized to do that.', 'jetpack' ), |
| 1123 | - 403 | |
| 1176 | + 403, | |
| 1177 | + JSON_UNESCAPED_SLASHES | |
| 1124 | 1178 | ); |
| 1125 | - return; | |
| 1126 | 1179 | } |
| 1127 | 1180 | } |
| 1128 | 1181 | |
| 1129 | 1182 | if ( $offset < 1 ) { |
| @@ -1157,13 +1210,15 @@ | ||
| 1157 | 1210 | 'content' => wpautop( $comment->comment_content ), |
| 1158 | 1211 | ); |
| 1159 | 1212 | } |
| 1160 | 1213 | |
| 1161 | - die( wp_json_encode( $out ) ); | |
| 1214 | + wp_send_json( $out, null, JSON_UNESCAPED_SLASHES ); | |
| 1162 | 1215 | } |
| 1163 | 1216 | |
| 1164 | 1217 | /** |
| 1165 | 1218 | * Adds a new comment to the database |
| 1219 | + * | |
| 1220 | + * @return never | |
| 1166 | 1221 | */ |
| 1167 | 1222 | public function post_attachment_comment() { |
| 1168 | 1223 | if ( ! headers_sent() ) { |
| 1169 | 1224 | header( 'Content-type: text/javascript' ); |
| @@ -1169,9 +1224,9 @@ | ||
| 1169 | 1224 | header( 'Content-type: text/javascript' ); |
| 1170 | 1225 | } |
| 1171 | 1226 | |
| 1172 | 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 |
| 1173 | - die( wp_json_encode( array( 'error' => __( 'Nonce verification failed.', 'jetpack' ) ) ) ); | |
| 1228 | + die( wp_json_encode( array( 'error' => __( 'Nonce verification failed.', 'jetpack' ) ), JSON_UNESCAPED_SLASHES ) ); | |
| 1174 | 1229 | } |
| 1175 | 1230 | |
| 1176 | 1231 | $_blog_id = isset( $_POST['blog_id'] ) ? (int) $_POST['blog_id'] : 0; |
| 1177 | 1232 | $_post_id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0; |
| @@ -1177,17 +1232,17 @@ | ||
| 1177 | 1232 | $_post_id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0; |
| 1178 | 1233 | $comment = isset( $_POST['comment'] ) ? filter_var( wp_unslash( $_POST['comment'] ) ) : null; |
| 1179 | 1234 | |
| 1180 | 1235 | if ( empty( $_blog_id ) ) { |
| 1181 | - 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 ) ); | |
| 1182 | 1237 | } |
| 1183 | 1238 | |
| 1184 | 1239 | if ( empty( $_post_id ) ) { |
| 1185 | - 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 ) ); | |
| 1186 | 1241 | } |
| 1187 | 1242 | |
| 1188 | 1243 | if ( empty( $comment ) ) { |
| 1189 | - 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 ) ); | |
| 1190 | 1245 | } |
| 1191 | 1246 | |
| 1192 | 1247 | // Used in context like NewDash. |
| 1193 | 1248 | $switched = false; |
| @@ -1202,9 +1257,9 @@ | ||
| 1202 | 1257 | if ( ! comments_open( $_post_id ) ) { |
| 1203 | 1258 | if ( $switched ) { |
| 1204 | 1259 | restore_current_blog(); |
| 1205 | 1260 | } |
| 1206 | - 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 ) ); | |
| 1207 | 1262 | } |
| 1208 | 1263 | |
| 1209 | 1264 | if ( is_user_logged_in() ) { |
| 1210 | 1265 | $user = wp_get_current_user(); |
| @@ -1216,15 +1271,18 @@ | ||
| 1216 | 1271 | if ( empty( $user_id ) ) { |
| 1217 | 1272 | if ( $switched ) { |
| 1218 | 1273 | restore_current_blog(); |
| 1219 | 1274 | } |
| 1220 | - 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 ) ); | |
| 1221 | 1276 | } |
| 1222 | 1277 | } else { |
| 1223 | 1278 | $user_id = 0; |
| 1224 | 1279 | $display_name = isset( $_POST['author'] ) ? sanitize_text_field( wp_unslash( $_POST['author'] ) ) : null; |
| 1225 | - $email = isset( $_POST['email'] ) ? wp_unslash( $_POST['email'] ) : null; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Checked or sanitized below. | |
| 1226 | - $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; | |
| 1227 | 1285 | |
| 1228 | 1286 | if ( get_option( 'require_name_email' ) ) { |
| 1229 | 1287 | if ( empty( $display_name ) ) { |
| 1230 | 1288 | if ( $switched ) { |
| @@ -1229,9 +1287,9 @@ | ||
| 1229 | 1287 | if ( empty( $display_name ) ) { |
| 1230 | 1288 | if ( $switched ) { |
| 1231 | 1289 | restore_current_blog(); |
| 1232 | 1290 | } |
| 1233 | - 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 ) ); | |
| 1234 | 1292 | } |
| 1235 | 1293 | |
| 1236 | 1294 | if ( empty( $email ) ) { |
| 1237 | 1295 | if ( $switched ) { |
| @@ -1236,9 +1294,9 @@ | ||
| 1236 | 1294 | if ( empty( $email ) ) { |
| 1237 | 1295 | if ( $switched ) { |
| 1238 | 1296 | restore_current_blog(); |
| 1239 | 1297 | } |
| 1240 | - 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 ) ); | |
| 1241 | 1299 | } |
| 1242 | 1300 | |
| 1243 | 1301 | if ( ! is_email( $email ) ) { |
| 1244 | 1302 | if ( $switched ) { |
| @@ -1243,9 +1301,9 @@ | ||
| 1243 | 1301 | if ( ! is_email( $email ) ) { |
| 1244 | 1302 | if ( $switched ) { |
| 1245 | 1303 | restore_current_blog(); |
| 1246 | 1304 | } |
| 1247 | - 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 ) ); | |
| 1248 | 1306 | } |
| 1249 | 1307 | } else { |
| 1250 | 1308 | $email = $email !== null ? sanitize_email( $email ) : null; |
| 1251 | 1309 | } |
| @@ -1286,9 +1344,10 @@ | ||
| 1286 | 1344 | wp_json_encode( |
| 1287 | 1345 | array( |
| 1288 | 1346 | 'comment_id' => $comment_id, |
| 1289 | 1347 | 'comment_status' => $comment_status, |
| 1290 | - ) | |
| 1348 | + ), | |
| 1349 | + JSON_UNESCAPED_SLASHES | |
| 1291 | 1350 | ) |
| 1292 | 1351 | ); |
| 1293 | 1352 | } |
| 1294 | 1353 | |
| @@ -1416,9 +1475,9 @@ | ||
| 1416 | 1475 | * Sanitize input for the `carousel_display_exif` setting. |
| 1417 | 1476 | * |
| 1418 | 1477 | * @param mixed $value User input setting value. |
| 1419 | 1478 | * |
| 1420 | - * @return number Sanitized value, only 1 or 0. | |
| 1479 | + * @return int Sanitized value, only 1 or 0. | |
| 1421 | 1480 | */ |
| 1422 | 1481 | public function carousel_display_exif_sanitize( $value ) { |
| 1423 | 1482 | return $this->sanitize_1or0_option( $value ); |
| 1424 | 1483 | } |
| @@ -1425,11 +1484,11 @@ | ||
| 1425 | 1484 | |
| 1426 | 1485 | /** |
| 1427 | 1486 | * Return sanitized option for value that controls whether comments will be hidden or not. |
| 1428 | 1487 | * |
| 1429 | - * @param number $value Value to sanitize. | |
| 1488 | + * @param mixed $value Value to sanitize. | |
| 1430 | 1489 | * |
| 1431 | - * @return number Sanitized value, only 1 or 0. | |
| 1490 | + * @return int Sanitized value, only 1 or 0. | |
| 1432 | 1491 | */ |
| 1433 | 1492 | public function carousel_display_comments_sanitize( $value ) { |
| 1434 | 1493 | return $this->sanitize_1or0_option( $value ); |
| 1435 | 1494 | } |
| @@ -1469,9 +1528,9 @@ | ||
| 1469 | 1528 | * Sanitize input for the `carousel_enable_it` setting. |
| 1470 | 1529 | * |
| 1471 | 1530 | * @param mixed $value User input. |
| 1472 | 1531 | * |
| 1473 | - * @return number Sanitized value, only 1 or 0. | |
| 1532 | + * @return int Sanitized value, only 1 or 0. | |
| 1474 | 1533 | */ |
| 1475 | 1534 | public function carousel_enable_it_sanitize( $value ) { |
| 1476 | 1535 | return $this->sanitize_1or0_option( $value ); |
| 1477 | 1536 | } |