| @@ -2,10 +2,13 @@ | ||
| 2 | 2 | |
| 3 | 3 | // phpcs:disable Universal.Files.SeparateFunctionsFromOO.Mixed -- TODO: Move classes to appropriately-named class files. |
| 4 | 4 | |
| 5 | 5 | use Automattic\Jetpack\Assets; |
| 6 | -use Automattic\Jetpack\Redirect; | |
| 7 | 6 | |
| 7 | +if ( ! defined( 'ABSPATH' ) ) { | |
| 8 | + exit( 0 ); | |
| 9 | +} | |
| 10 | + | |
| 8 | 11 | /* |
| 9 | 12 | Plugin Name: The Neverending Home Page. |
| 10 | 13 | Plugin URI: https://automattic.com/ |
| 11 | 14 | Description: Adds infinite scrolling support to the front-end blog post view for themes, pulling the next set of posts automatically into view when the reader approaches the bottom of the page. |
| @@ -19,8 +22,10 @@ | ||
| 19 | 22 | |
| 20 | 23 | /** |
| 21 | 24 | * Class: The_Neverending_Home_Page relies on add_theme_support, expects specific |
| 22 | 25 | * styling from each theme; including fixed footer. |
| 26 | + * | |
| 27 | + * @phan-constructor-used-for-side-effects | |
| 23 | 28 | */ |
| 24 | 29 | class The_Neverending_Home_Page { |
| 25 | 30 | /** |
| 26 | 31 | * Maximum allowed number of posts per page in $_REQUEST. |
| @@ -89,23 +94,24 @@ | ||
| 89 | 94 | * @uses get_theme_support, infinite_scroll_has_footer_widgets, sanitize_title, add_action, get_option, wp_parse_args, is_active_sidebar |
| 90 | 95 | * @return object |
| 91 | 96 | */ |
| 92 | 97 | public static function get_settings() { |
| 98 | + $defaults = array( | |
| 99 | + 'type' => 'scroll', // scroll | click | |
| 100 | + 'requested_type' => 'scroll', // store the original type for use when logic overrides it | |
| 101 | + 'footer_widgets' => false, // true | false | sidebar_id | array of sidebar_ids -- last two are checked with is_active_sidebar | |
| 102 | + 'container' => 'content', // container html id | |
| 103 | + 'wrapper' => true, // true | false | html class -- the html class. | |
| 104 | + 'render' => false, // optional function, otherwise the `content` template part will be used | |
| 105 | + 'footer' => true, // boolean to enable or disable the infinite footer | string to provide an html id to derive footer width from | |
| 106 | + 'footer_callback' => false, // function to be called to render the IS footer, in place of the default | |
| 107 | + 'posts_per_page' => false, | |
| 108 | + 'click_handle' => true, // boolean to enable or disable rendering the click handler div. If type is click and this is false, page must include its own trigger with the HTML ID `infinite-handle`. | |
| 109 | + ); | |
| 110 | + | |
| 93 | 111 | if ( self::$settings === null ) { |
| 94 | 112 | $css_pattern = '#[^A-Z\d\-_]#i'; |
| 95 | 113 | |
| 96 | - $defaults = array( | |
| 97 | - 'type' => 'scroll', // scroll | click | |
| 98 | - 'requested_type' => 'scroll', // store the original type for use when logic overrides it | |
| 99 | - 'footer_widgets' => false, // true | false | sidebar_id | array of sidebar_ids -- last two are checked with is_active_sidebar | |
| 100 | - 'container' => 'content', // container html id | |
| 101 | - 'wrapper' => true, // true | false | html class -- the html class. | |
| 102 | - 'render' => false, // optional function, otherwise the `content` template part will be used | |
| 103 | - 'footer' => true, // boolean to enable or disable the infinite footer | string to provide an html id to derive footer width from | |
| 104 | - 'footer_callback' => false, // function to be called to render the IS footer, in place of the default | |
| 105 | - 'posts_per_page' => false, // phpcs:ignore WordPress.WP.PostsPerPage.posts_per_page_posts_per_page -- int | false to set based on IS type | |
| 106 | - 'click_handle' => true, // boolean to enable or disable rendering the click handler div. If type is click and this is false, page must include its own trigger with the HTML ID `infinite-handle`. | |
| 107 | - ); | |
| 108 | 114 | $settings = $defaults; |
| 109 | 115 | // Validate settings passed through add_theme_support() |
| 110 | 116 | $_settings = get_theme_support( 'infinite-scroll' ); |
| 111 | 117 | |
| @@ -251,9 +257,9 @@ | ||
| 251 | 257 | |
| 252 | 258 | // Ensure that IS is enabled and no footer widgets exist if the IS type isn't already "click". |
| 253 | 259 | if ( 'click' !== $settings['type'] ) { |
| 254 | 260 | // Check the setting status |
| 255 | - $disabled = '' === get_option( self::$option_name_enabled ) ? true : false; | |
| 261 | + $disabled = '' === get_option( self::$option_name_enabled ); | |
| 256 | 262 | |
| 257 | 263 | // Footer content or Reading option check |
| 258 | 264 | if ( $settings['footer_widgets'] || $disabled ) { |
| 259 | 265 | $settings['type'] = 'click'; |
| @@ -265,22 +271,24 @@ | ||
| 265 | 271 | $settings['click_handle'] = true; |
| 266 | 272 | } |
| 267 | 273 | |
| 268 | 274 | // Store final settings in a class static to avoid reparsing |
| 269 | - /** | |
| 270 | - * Filter the array of Infinite Scroll settings. | |
| 271 | - * | |
| 272 | - * @module infinite-scroll | |
| 273 | - * | |
| 274 | - * @since 2.0.0 | |
| 275 | - * | |
| 276 | - * @param array $settings Array of Infinite Scroll settings. | |
| 277 | - */ | |
| 278 | - self::$settings = apply_filters( 'infinite_scroll_settings', $settings ); | |
| 275 | + self::$settings = $settings; | |
| 279 | 276 | } |
| 280 | 277 | |
| 281 | - /** This filter is already documented in modules/infinite-scroll/infinity.php */ | |
| 282 | - return (object) apply_filters( 'infinite_scroll_settings', self::$settings ); | |
| 278 | + /** | |
| 279 | + * Filter the array of Infinite Scroll settings. | |
| 280 | + * | |
| 281 | + * @module infinite-scroll | |
| 282 | + * | |
| 283 | + * @since 2.0.0 | |
| 284 | + * | |
| 285 | + * @param array $settings Array of Infinite Scroll settings. | |
| 286 | + */ | |
| 287 | + $filtered_settings = apply_filters( 'infinite_scroll_settings', self::$settings ); | |
| 288 | + | |
| 289 | + // Ensure all properties are still set. | |
| 290 | + return (object) wp_parse_args( $filtered_settings, $defaults ); | |
| 283 | 291 | } |
| 284 | 292 | |
| 285 | 293 | /** |
| 286 | 294 | * Number of posts per page. |
| @@ -288,14 +296,15 @@ | ||
| 288 | 296 | * @uses self::wp_query, self::get_settings, apply_filters |
| 289 | 297 | * @return int |
| 290 | 298 | */ |
| 291 | 299 | public static function posts_per_page() { |
| 292 | - $posts_per_page = self::get_settings()->posts_per_page ? self::get_settings()->posts_per_page : self::wp_query()->get( 'posts_per_page' ); | |
| 300 | + $settings = self::get_settings(); | |
| 301 | + $posts_per_page = $settings->posts_per_page ? $settings->posts_per_page : self::wp_query()->get( 'posts_per_page' ); | |
| 293 | 302 | $posts_per_page_core_option = get_option( 'posts_per_page' ); |
| 294 | 303 | |
| 295 | 304 | // If Infinite Scroll is set to click, and if the site owner changed posts_per_page, let's use that. |
| 296 | 305 | if ( |
| 297 | - 'click' === self::get_settings()->type | |
| 306 | + 'click' === $settings->type | |
| 298 | 307 | && ( '10' !== $posts_per_page_core_option ) |
| 299 | 308 | ) { |
| 300 | 309 | $posts_per_page = $posts_per_page_core_option; |
| 301 | 310 | } |
| @@ -371,9 +380,9 @@ | ||
| 371 | 380 | * @param bool|null null Bool if value should be overridden, null to determine from query |
| 372 | 381 | * @param object self::wp_query() WP_Query object for current request |
| 373 | 382 | * @param object self::get_settings() Infinite Scroll settings |
| 374 | 383 | */ |
| 375 | - $override = apply_filters( 'infinite_scroll_is_last_batch', null, self::wp_query(), self::get_settings() ); // phpcs:ignore WordPress.WP.ClassNameCase.Incorrect -- False positive. | |
| 384 | + $override = apply_filters( 'infinite_scroll_is_last_batch', null, self::wp_query(), self::get_settings() ); | |
| 376 | 385 | if ( is_bool( $override ) ) { |
| 377 | 386 | return $override; |
| 378 | 387 | } |
| 379 | 388 | |
| @@ -381,11 +390,11 @@ | ||
| 381 | 390 | $posts_per_page = self::posts_per_page(); |
| 382 | 391 | |
| 383 | 392 | // This is to cope with an issue in certain themes or setups where posts are returned but found_posts is 0. |
| 384 | 393 | if ( 0 === $entries ) { |
| 385 | - return (bool) ( ! is_countable( self::wp_query()->posts ) || ( count( self::wp_query()->posts ) < $posts_per_page ) ); | |
| 394 | + return ( ! is_countable( self::wp_query()->posts ) || ( count( self::wp_query()->posts ) < $posts_per_page ) ); | |
| 386 | 395 | } |
| 387 | - $paged = max( 1, self::wp_query()->get( 'paged' ) ); | |
| 396 | + $paged = max( 1, (int) self::wp_query()->get( 'paged' ) ); | |
| 388 | 397 | |
| 389 | 398 | // Are there enough posts for more than the first page? |
| 390 | 399 | if ( $entries <= $posts_per_page ) { |
| 391 | 400 | return true; |
| @@ -431,19 +440,8 @@ | ||
| 431 | 440 | if ( ! current_theme_supports( 'infinite-scroll' ) ) { |
| 432 | 441 | return; |
| 433 | 442 | } |
| 434 | 443 | |
| 435 | - if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) { | |
| 436 | - // This setting is no longer configurable in wp-admin on WordPress.com -- leave a pointer | |
| 437 | - add_settings_field( | |
| 438 | - self::$option_name_enabled, | |
| 439 | - '<span id="infinite-scroll-options">' . esc_html__( 'Infinite Scroll Behavior', 'jetpack' ) . '</span>', | |
| 440 | - array( $this, 'infinite_setting_html_calypso_placeholder' ), | |
| 441 | - 'reading' | |
| 442 | - ); | |
| 443 | - return; | |
| 444 | - } | |
| 445 | - | |
| 446 | 444 | // Add the setting field [infinite_scroll] and place it in Settings > Reading |
| 447 | 445 | add_settings_field( self::$option_name_enabled, '<span id="infinite-scroll-options">' . esc_html__( 'Infinite Scroll Behavior', 'jetpack' ) . '</span>', array( $this, 'infinite_setting_html' ), 'reading' ); |
| 448 | 446 | register_setting( 'reading', self::$option_name_enabled, 'esc_attr' ); |
| 449 | 447 | } |
| @@ -448,29 +446,16 @@ | ||
| 448 | 446 | register_setting( 'reading', self::$option_name_enabled, 'esc_attr' ); |
| 449 | 447 | } |
| 450 | 448 | |
| 451 | 449 | /** |
| 452 | - * Render the redirect link to the infinite scroll settings in Calypso. | |
| 453 | - */ | |
| 454 | - public function infinite_setting_html_calypso_placeholder() { | |
| 455 | - $details = get_blog_details(); | |
| 456 | - $writing_url = Redirect::get_url( 'calypso-settings-writing', array( 'site' => $details->domain ) ); | |
| 457 | - echo '<span>' . sprintf( | |
| 458 | - /* translators: Variables are the enclosing link to the settings page */ | |
| 459 | - esc_html__( 'This option has moved. You can now manage it %1$shere%2$s.', 'jetpack' ), | |
| 460 | - '<a href="' . esc_url( $writing_url ) . '">', | |
| 461 | - '</a>' | |
| 462 | - ) . '</span>'; | |
| 463 | - } | |
| 464 | - | |
| 465 | - /** | |
| 466 | 450 | * HTML code to display a checkbox true/false option |
| 467 | 451 | * for the infinite_scroll setting. |
| 468 | 452 | */ |
| 469 | 453 | public function infinite_setting_html() { |
| 454 | + $settings = self::get_settings(); | |
| 470 | 455 | |
| 471 | 456 | // If the blog has footer widgets, show a notice instead of the checkbox |
| 472 | - if ( self::get_settings()->footer_widgets || 'click' === self::get_settings()->requested_type ) { | |
| 457 | + if ( $settings->footer_widgets || 'click' === $settings->requested_type ) { | |
| 473 | 458 | echo '<label><em>' . esc_html__( 'We’ve changed this option to a click-to-scroll version for you since you have footer widgets in Appearance → Widgets, or your theme uses click-to-scroll as the default behavior.', 'jetpack' ) . '</em></label>'; |
| 474 | 459 | } else { |
| 475 | 460 | echo '<label><input name="infinite_scroll" type="checkbox" value="1" ' . checked( 1, '' !== get_option( self::$option_name_enabled ), false ) . ' /> ' . esc_html__( 'Check to load posts as you scroll. Uncheck to show clickable button to load posts', 'jetpack' ) . '</label>'; |
| 476 | 461 | // translators: the number of posts to show on each page load. |
| @@ -564,15 +549,16 @@ | ||
| 564 | 549 | * |
| 565 | 550 | * @return string |
| 566 | 551 | */ |
| 567 | 552 | public function body_class() { |
| 568 | - $classes = ''; | |
| 553 | + $settings = self::get_settings(); | |
| 554 | + $classes = ''; | |
| 569 | 555 | // Do not add infinity-scroll class if disabled through the Reading page |
| 570 | - $disabled = '' === get_option( self::$option_name_enabled ) ? true : false; | |
| 571 | - if ( ! $disabled || 'click' === self::get_settings()->type ) { | |
| 556 | + $disabled = '' === get_option( self::$option_name_enabled ); | |
| 557 | + if ( ! $disabled || 'click' === $settings->type ) { | |
| 572 | 558 | $classes = 'infinite-scroll'; |
| 573 | 559 | |
| 574 | - if ( 'scroll' === self::get_settings()->type ) { | |
| 560 | + if ( 'scroll' === $settings->type ) { | |
| 575 | 561 | $classes .= ' neverending'; |
| 576 | 562 | } |
| 577 | 563 | } |
| 578 | 564 | |
| @@ -591,9 +577,13 @@ | ||
| 591 | 577 | |
| 592 | 578 | $excluded_posts = array(); |
| 593 | 579 | // loop through posts returned by wp_query call |
| 594 | 580 | foreach ( self::wp_query()->get_posts() as $post ) { |
| 581 | + if ( ! $post instanceof \WP_Post ) { | |
| 582 | + continue; | |
| 583 | + } | |
| 595 | 584 | |
| 585 | + // @phan-suppress-next-line PhanPluginDuplicateConditionalNullCoalescing -- probably would be safe to collapse, but not changing just in case. | |
| 596 | 586 | $orderby = isset( self::wp_query()->query_vars['orderby'] ) ? self::wp_query()->query_vars['orderby'] : ''; |
| 597 | 587 | $post_date = ( ! empty( $post->post_date ) ? $post->post_date : false ); |
| 598 | 588 | if ( 'modified' === $orderby || false === $post_date ) { |
| 599 | 589 | $post_date = $post->post_modified; |
| @@ -620,9 +610,9 @@ | ||
| 620 | 610 | $query_vars = self::wp_query()->query_vars; |
| 621 | 611 | // applies to search page only |
| 622 | 612 | if ( true === self::wp_query()->is_search() ) { |
| 623 | 613 | // set post__not_in array in query_vars in case it does not exists |
| 624 | - if ( false === isset( $query_vars['post__not_in'] ) ) { | |
| 614 | + if ( ! isset( $query_vars['post__not_in'] ) ) { | |
| 625 | 615 | $query_vars['post__not_in'] = array(); |
| 626 | 616 | } |
| 627 | 617 | // get excluded posts |
| 628 | 618 | $excluded = self::get_excluded_posts(); |
| @@ -647,12 +637,15 @@ | ||
| 647 | 637 | } |
| 648 | 638 | |
| 649 | 639 | // grab the last posts in the stack as if the last one is title-matching the rest is title-matching as well |
| 650 | 640 | $post = end( self::wp_query()->posts ); |
| 641 | + if ( ! $post instanceof WP_Post ) { | |
| 642 | + return false; | |
| 643 | + } | |
| 651 | 644 | |
| 652 | 645 | // code inspired by WP_Query class |
| 653 | 646 | if ( preg_match_all( '/".*?("|$)|((?<=[\t ",+])|^)[^\t ",+]+/', self::wp_query()->get( 's' ), $matches ) ) { |
| 654 | - $search_terms = self::wp_query()->query_vars['search_terms']; | |
| 647 | + $search_terms = self::wp_query()->query_vars['search_terms'] ?? null; | |
| 655 | 648 | // if the search string has only short terms or stopwords, or is 10+ terms long, match it as sentence |
| 656 | 649 | if ( empty( $search_terms ) || ! is_countable( $search_terms ) || count( $search_terms ) > 9 ) { |
| 657 | 650 | $search_terms = array( self::wp_query()->get( 's' ) ); |
| 658 | 651 | } |
| @@ -693,11 +686,11 @@ | ||
| 693 | 686 | if ( true === self::has_only_title_matching_posts() ) { |
| 694 | 687 | return false; |
| 695 | 688 | } |
| 696 | 689 | |
| 697 | - $post = end( self::wp_query()->posts ); | |
| 698 | - $orderby = isset( self::wp_query()->query_vars['orderby'] ) ? | |
| 699 | - self::wp_query()->query_vars['orderby'] : ''; | |
| 690 | + $post = end( self::wp_query()->posts ); | |
| 691 | + // @phan-suppress-next-line PhanPluginDuplicateConditionalNullCoalescing -- probably would be safe to collapse, but not changing just in case. | |
| 692 | + $orderby = isset( self::wp_query()->query_vars['orderby'] ) ? self::wp_query()->query_vars['orderby'] : ''; | |
| 700 | 693 | $post_date = ( ! empty( $post->post_date ) ? $post->post_date : false ); |
| 701 | 694 | switch ( $orderby ) { |
| 702 | 695 | case 'modified': |
| 703 | 696 | return $post->post_modified; |
| @@ -721,9 +714,9 @@ | ||
| 721 | 714 | if ( empty( $query ) ) { |
| 722 | 715 | $query = self::wp_query(); |
| 723 | 716 | } |
| 724 | 717 | |
| 725 | - $orderby = isset( $query->query_vars['orderby'] ) ? $query->query_vars['orderby'] : ''; | |
| 718 | + $orderby = $query->query_vars['orderby'] ?? ''; | |
| 726 | 719 | |
| 727 | 720 | switch ( $orderby ) { |
| 728 | 721 | case 'modified': |
| 729 | 722 | return 'post_modified'; |
| @@ -752,13 +745,14 @@ | ||
| 752 | 745 | global $wpdb; |
| 753 | 746 | |
| 754 | 747 | $sort_field = self::get_query_sort_field( $query ); |
| 755 | 748 | |
| 756 | - if ( 'post_date' !== $sort_field || 'DESC' !== $_REQUEST['query_args']['order'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotValidated -- no changes made to the site. | |
| 749 | + if ( 'post_date' !== $sort_field || | |
| 750 | + ! isset( $_REQUEST['query_args']['order'] ) || 'DESC' !== $_REQUEST['query_args']['order'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- no changes made to the site. | |
| 757 | 751 | return $where; |
| 758 | 752 | } |
| 759 | 753 | |
| 760 | - $query_before = sanitize_text_field( wp_unslash( $_REQUEST['query_before'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotValidated -- no changes made to the site. | |
| 754 | + $query_before = isset( $_REQUEST['query_before'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['query_before'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- no changes made to the site. | |
| 761 | 755 | |
| 762 | 756 | if ( empty( $query_before ) ) { |
| 763 | 757 | return $where; |
| 764 | 758 | } |
| @@ -770,8 +764,10 @@ | ||
| 770 | 764 | * Filter Infinite Scroll's SQL date query making sure post queries |
| 771 | 765 | * will always return results prior to (descending sort) |
| 772 | 766 | * or before (ascending sort) the last post date. |
| 773 | 767 | * |
| 768 | + * @deprecated 14.0 | |
| 769 | + * | |
| 774 | 770 | * @module infinite-scroll |
| 775 | 771 | * |
| 776 | 772 | * @param string $clause SQL Date query. |
| 777 | 773 | * @param object $query Query. |
| @@ -777,11 +773,11 @@ | ||
| 777 | 773 | * @param object $query Query. |
| 778 | 774 | * @param string $operator @deprecated Query operator. |
| 779 | 775 | * @param string $last_post_date @deprecated Last Post Date timestamp. |
| 780 | 776 | */ |
| 781 | - $operator = 'ASC' === $_REQUEST['query_args']['order'] ? '>' : '<'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotValidated -- no changes to the site. | |
| 782 | - $last_post_date = sanitize_text_field( wp_unslash( $_REQUEST['last_post_date'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotValidated -- no changes to the site. | |
| 783 | - $where .= apply_filters( 'infinite_scroll_posts_where', $clause, $query, $operator, $last_post_date ); | |
| 777 | + $operator = '<'; | |
| 778 | + $last_post_date = isset( $_REQUEST['last_post_date'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['last_post_date'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- no changes to the site | |
| 779 | + $where .= apply_filters_deprecated( 'infinite_scroll_posts_where', array( $clause, $query, $operator, $last_post_date ), '14.0', '' ); | |
| 784 | 780 | } |
| 785 | 781 | |
| 786 | 782 | return $where; |
| 787 | 783 | } |
| @@ -881,8 +877,10 @@ | ||
| 881 | 877 | public function action_wp_footer_settings() { |
| 882 | 878 | global $wp_rewrite; |
| 883 | 879 | global $currentday; |
| 884 | 880 | |
| 881 | + $settings = self::get_settings(); | |
| 882 | + | |
| 885 | 883 | // Default click handle text |
| 886 | 884 | $click_handle_text = __( 'Older posts', 'jetpack' ); |
| 887 | 885 | |
| 888 | 886 | // If a single CPT is displayed, use its plural name instead of "posts" |
| @@ -902,11 +900,13 @@ | ||
| 902 | 900 | // If not, use "Posts" without confusing the users. |
| 903 | 901 | if ( |
| 904 | 902 | is_a( $taxonomy, 'WP_Taxonomy' ) |
| 905 | 903 | && is_countable( $taxonomy->object_type ) |
| 904 | + && ! empty( $taxonomy->object_type ) | |
| 906 | 905 | && count( $taxonomy->object_type ) < 2 |
| 907 | 906 | ) { |
| 908 | - $post_type = $taxonomy->object_type[0]; | |
| 907 | + // It seems [0] doesn't work, as sometimes plugins can deregister a taxonomy but not reindex. | |
| 908 | + $post_type = reset( $taxonomy->object_type ); | |
| 909 | 909 | } |
| 910 | 910 | } |
| 911 | 911 | |
| 912 | 912 | if ( is_string( $post_type ) && ! empty( $post_type ) ) { |
| @@ -930,17 +930,17 @@ | ||
| 930 | 930 | unset( $post_type ); |
| 931 | 931 | |
| 932 | 932 | // Base JS settings |
| 933 | 933 | $js_settings = array( |
| 934 | - 'id' => self::get_settings()->container, | |
| 934 | + 'id' => $settings->container, | |
| 935 | 935 | 'ajaxurl' => esc_url_raw( self::ajax_url() ), |
| 936 | - 'type' => esc_js( self::get_settings()->type ), | |
| 936 | + 'type' => esc_js( $settings->type ), | |
| 937 | 937 | 'wrapper' => self::has_wrapper(), |
| 938 | - 'wrapper_class' => is_string( self::get_settings()->wrapper ) ? esc_js( self::get_settings()->wrapper ) : 'infinite-wrap', | |
| 939 | - 'footer' => is_string( self::get_settings()->footer ) ? esc_js( self::get_settings()->footer ) : self::get_settings()->footer, | |
| 940 | - 'click_handle' => esc_js( self::get_settings()->click_handle ), | |
| 941 | - 'text' => esc_js( $click_handle_text ), | |
| 942 | - 'totop' => esc_js( __( 'Scroll back to top', 'jetpack' ) ), | |
| 938 | + 'wrapper_class' => is_string( $settings->wrapper ) ? esc_js( $settings->wrapper ) : 'infinite-wrap', | |
| 939 | + 'footer' => is_string( $settings->footer ) ? esc_js( $settings->footer ) : $settings->footer, | |
| 940 | + 'click_handle' => esc_js( $settings->click_handle ), | |
| 941 | + 'text' => $click_handle_text, | |
| 942 | + 'totop' => __( 'Scroll back to top', 'jetpack' ), | |
| 943 | 943 | 'currentday' => $currentday, |
| 944 | 944 | 'order' => 'DESC', |
| 945 | 945 | 'scripts' => array(), |
| 946 | 946 | 'styles' => array(), |
| @@ -955,9 +955,9 @@ | ||
| 955 | 955 | 'query_args' => self::get_query_vars(), |
| 956 | 956 | 'query_before' => current_time( 'mysql' ), |
| 957 | 957 | 'last_post_date' => self::get_last_post_date(), |
| 958 | 958 | 'body_class' => self::body_class(), |
| 959 | - 'loading_text' => esc_js( __( 'Loading new page', 'jetpack' ) ), | |
| 959 | + 'loading_text' => __( 'Loading new page', 'jetpack' ), | |
| 960 | 960 | ); |
| 961 | 961 | |
| 962 | 962 | // Optional order param |
| 963 | 963 | if ( isset( $_REQUEST['order'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- no changes made to the site. |
| @@ -989,9 +989,9 @@ | ||
| 989 | 989 | do_action( 'infinite_scroll_wp_head' ); |
| 990 | 990 | |
| 991 | 991 | ?> |
| 992 | 992 | <script type="text/javascript"> |
| 993 | - var infiniteScroll = <?php echo wp_json_encode( array( 'settings' => $js_settings ), JSON_HEX_TAG ); ?>; | |
| 993 | + var infiniteScroll = <?php echo wp_json_encode( array( 'settings' => $js_settings ), JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ); ?>; | |
| 994 | 994 | </script> |
| 995 | 995 | <?php |
| 996 | 996 | } |
| 997 | 997 | |
| @@ -1108,10 +1108,10 @@ | ||
| 1108 | 1108 | } |
| 1109 | 1109 | |
| 1110 | 1110 | return out; |
| 1111 | 1111 | }; |
| 1112 | - extend( window.infiniteScroll.settings.scripts, <?php echo wp_json_encode( $scripts ); ?> ); | |
| 1113 | - extend( window.infiniteScroll.settings.styles, <?php echo wp_json_encode( $styles ); ?> ); | |
| 1112 | + extend( window.infiniteScroll.settings.scripts, <?php echo wp_json_encode( $scripts, JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ); ?> ); | |
| 1113 | + extend( window.infiniteScroll.settings.styles, <?php echo wp_json_encode( $styles, JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ); ?> ); | |
| 1114 | 1114 | })(); |
| 1115 | 1115 | </script> |
| 1116 | 1116 | <?php |
| 1117 | 1117 | $aria_live = 'assertive'; |
| @@ -1373,9 +1373,9 @@ | ||
| 1373 | 1373 | * @uses current_theme_supports, get_option, self::wp_query, current_user_can, apply_filters, self::get_settings, add_filter, WP_Query, remove_filter, have_posts, wp_head, do_action, add_action, this::render, this::has_wrapper, esc_attr, wp_footer, sharing_register_post_for_share_counts, get_the_id |
| 1374 | 1374 | */ |
| 1375 | 1375 | public function query() { |
| 1376 | 1376 | if ( ! isset( $_REQUEST['page'] ) || ! current_theme_supports( 'infinite-scroll' ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- no changes to the site. |
| 1377 | - die; | |
| 1377 | + die( 0 ); | |
| 1378 | 1378 | } |
| 1379 | 1379 | |
| 1380 | 1380 | // @todo see if we should validate this nonce since we use it to form a query. |
| 1381 | 1381 | $page = (int) $_REQUEST['page']; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- we're casting this to an int and not making changes to the site. |
| @@ -1380,9 +1380,9 @@ | ||
| 1380 | 1380 | // @todo see if we should validate this nonce since we use it to form a query. |
| 1381 | 1381 | $page = (int) $_REQUEST['page']; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- we're casting this to an int and not making changes to the site. |
| 1382 | 1382 | |
| 1383 | 1383 | // Sanitize and set $previousday. Expected format: dd.mm.yy |
| 1384 | - if ( isset( $_REQUEST['currentday'] ) && preg_match( '/^\d{2}\.\d{2}\.\d{2}$/', $_REQUEST['currentday'] ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput, WordPress.Security.NonceVerification.Recommended -- manually validating, no changes to site | |
| 1384 | + if ( isset( $_REQUEST['currentday'] ) && is_string( $_REQUEST['currentday'] ) && preg_match( '/^\d{2}\.\d{2}\.\d{2}$/', $_REQUEST['currentday'] ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput, WordPress.Security.NonceVerification.Recommended -- manually validating, no changes to site | |
| 1385 | 1385 | global $previousday; |
| 1386 | 1386 | $previousday = $_REQUEST['currentday']; // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput |
| 1387 | 1387 | } |
| 1388 | 1388 | |
| @@ -1397,9 +1397,9 @@ | ||
| 1397 | 1397 | self::wp_query()->query_vars, |
| 1398 | 1398 | array( |
| 1399 | 1399 | 'paged' => $page, |
| 1400 | 1400 | 'post_status' => $post_status, |
| 1401 | - 'posts_per_page' => self::posts_per_page(), // phpcs:ignore WordPress.WP.PostsPerPage.posts_per_page_posts_per_page | |
| 1401 | + 'posts_per_page' => self::posts_per_page(), | |
| 1402 | 1402 | 'order' => $order, |
| 1403 | 1403 | ) |
| 1404 | 1404 | ); |
| 1405 | 1405 | |
| @@ -1431,9 +1431,9 @@ | ||
| 1431 | 1431 | $GLOBALS['wp_query'] = $infinite_scroll_query; |
| 1432 | 1432 | |
| 1433 | 1433 | $infinite_scroll_query->query( $query_args ); |
| 1434 | 1434 | |
| 1435 | - remove_filter( 'posts_where', array( $this, 'query_time_filter' ), 10, 2 ); | |
| 1435 | + remove_filter( 'posts_where', array( $this, 'query_time_filter' ), 10 ); | |
| 1436 | 1436 | |
| 1437 | 1437 | $results = array(); |
| 1438 | 1438 | |
| 1439 | 1439 | if ( have_posts() ) { |
| @@ -1474,8 +1474,9 @@ | ||
| 1474 | 1474 | foreach ( $callbacks as $callback ) { |
| 1475 | 1475 | if ( false !== $callback && is_callable( $callback ) ) { |
| 1476 | 1476 | rewind_posts(); |
| 1477 | 1477 | ob_start(); |
| 1478 | + | |
| 1478 | 1479 | add_action( 'infinite_scroll_render', $callback ); |
| 1479 | 1480 | |
| 1480 | 1481 | /** |
| 1481 | 1482 | * This action is already documented above. |
| @@ -1483,8 +1484,11 @@ | ||
| 1483 | 1484 | * for more details as to why it was introduced. |
| 1484 | 1485 | */ |
| 1485 | 1486 | do_action( 'infinite_scroll_render' ); |
| 1486 | 1487 | |
| 1488 | + // Fire wp_head to ensure that all necessary scripts are enqueued. Output isn't used, but scripts are extracted in self::action_wp_footer. | |
| 1489 | + wp_head(); | |
| 1490 | + | |
| 1487 | 1491 | $results['html'] = ob_get_clean(); |
| 1488 | 1492 | remove_action( 'infinite_scroll_render', $callback ); |
| 1489 | 1493 | } |
| 1490 | 1494 | if ( ! empty( $results['html'] ) ) { |
| @@ -1540,9 +1544,22 @@ | ||
| 1540 | 1544 | |
| 1541 | 1545 | sharing_register_post_for_share_counts( get_the_ID() ); |
| 1542 | 1546 | } |
| 1543 | 1547 | |
| 1544 | - $results['postflair'] = array_flip( $jetpack_sharing_counts ); | |
| 1548 | + // If sharing counts are not initialized for any reason, we initialize them here. | |
| 1549 | + if ( ! is_array( $jetpack_sharing_counts ) ) { | |
| 1550 | + $jetpack_sharing_counts = array(); | |
| 1551 | + } else { | |
| 1552 | + // Filter out non-string and non-integer values to avoid warnings with array_flip. | |
| 1553 | + $flippable_jetpack_sharing_counts = array_filter( | |
| 1554 | + $jetpack_sharing_counts, | |
| 1555 | + function ( $value ) { | |
| 1556 | + return is_string( $value ) || is_int( $value ); | |
| 1557 | + } | |
| 1558 | + ); | |
| 1559 | + } | |
| 1560 | + | |
| 1561 | + $results['postflair'] = array_flip( $flippable_jetpack_sharing_counts ?? array() ); | |
| 1545 | 1562 | } |
| 1546 | 1563 | } else { |
| 1547 | 1564 | /** This action is already documented in modules/infinite-scroll/infinity.php */ |
| 1548 | 1565 | do_action( 'infinite_scroll_empty' ); |
| @@ -1560,9 +1577,11 @@ | ||
| 1560 | 1577 | * @param array $results Array of Infinite Scroll results. |
| 1561 | 1578 | * @param array $query_args Array of main query arguments. |
| 1562 | 1579 | * @param WP_Query $wp_query WP Query. |
| 1563 | 1580 | */ |
| 1564 | - apply_filters( 'infinite_scroll_results', $results, $query_args, self::wp_query() ) | |
| 1581 | + apply_filters( 'infinite_scroll_results', $results, $query_args, self::wp_query() ), | |
| 1582 | + null, // @phan-suppress-current-line PhanTypeMismatchArgumentProbablyReal -- It takes null, but its phpdoc only says int. | |
| 1583 | + JSON_UNESCAPED_SLASHES | |
| 1565 | 1584 | ); |
| 1566 | 1585 | } |
| 1567 | 1586 | |
| 1568 | 1587 | /** |
| @@ -1699,15 +1718,17 @@ | ||
| 1699 | 1718 | if ( class_exists( 'Jetpack_AMP_Support' ) && Jetpack_AMP_Support::is_amp_request() ) { |
| 1700 | 1719 | return; |
| 1701 | 1720 | } |
| 1702 | 1721 | |
| 1722 | + $settings = self::get_settings(); | |
| 1723 | + | |
| 1703 | 1724 | // Bail if theme requested footer not show |
| 1704 | - if ( false === self::get_settings()->footer ) { | |
| 1725 | + if ( false === $settings->footer ) { | |
| 1705 | 1726 | return; |
| 1706 | 1727 | } |
| 1707 | 1728 | |
| 1708 | 1729 | // We only need the new footer for the 'scroll' type |
| 1709 | - if ( 'scroll' !== self::get_settings()->type || ! self::archive_supports_infinity() ) { | |
| 1730 | + if ( 'scroll' !== $settings->type || ! self::archive_supports_infinity() ) { | |
| 1710 | 1731 | return; |
| 1711 | 1732 | } |
| 1712 | 1733 | |
| 1713 | 1734 | if ( self::is_last_batch() ) { |
| @@ -1714,10 +1735,10 @@ | ||
| 1714 | 1735 | return; |
| 1715 | 1736 | } |
| 1716 | 1737 | |
| 1717 | 1738 | // Display a footer, either user-specified or a default |
| 1718 | - if ( false !== self::get_settings()->footer_callback && is_callable( self::get_settings()->footer_callback ) ) { | |
| 1719 | - call_user_func( self::get_settings()->footer_callback, self::get_settings() ); | |
| 1739 | + if ( false !== $settings->footer_callback && is_callable( $settings->footer_callback ) ) { | |
| 1740 | + call_user_func( $settings->footer_callback, $settings ); | |
| 1720 | 1741 | } else { |
| 1721 | 1742 | self::default_footer(); |
| 1722 | 1743 | } |
| 1723 | 1744 | } |
| @@ -1816,9 +1837,9 @@ | ||
| 1816 | 1837 | |
| 1817 | 1838 | $scripts_data[ $key ]['extra_data'] = sprintf( |
| 1818 | 1839 | 'window.%s = %s', |
| 1819 | 1840 | '_wpmejsSettings', |
| 1820 | - wp_json_encode( apply_filters( 'mejs_settings', $mejs_settings ) ) | |
| 1841 | + wp_json_encode( apply_filters( 'mejs_settings', $mejs_settings ), JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ) | |
| 1821 | 1842 | ); |
| 1822 | 1843 | } |
| 1823 | 1844 | } |
| 1824 | 1845 | return $scripts_data; |
| @@ -1977,9 +1998,9 @@ | ||
| 1977 | 1998 | ?> |
| 1978 | 1999 | <amp-next-page max-pages="<?php echo esc_attr( static::amp_get_max_pages() ); ?>"> |
| 1979 | 2000 | <script type="application/json"> |
| 1980 | 2001 | [ |
| 1981 | - <?php echo wp_json_encode( $this->amp_next_page() ); ?> | |
| 2002 | + <?php echo wp_json_encode( $this->amp_next_page(), JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ); ?> | |
| 1982 | 2003 | ] |
| 1983 | 2004 | </script> |
| 1984 | 2005 | <div separator> |
| 1985 | 2006 | <?php |
| @@ -2075,9 +2096,9 @@ | ||
| 2075 | 2096 | */ |
| 2076 | 2097 | protected static function amp_get_max_pages() { |
| 2077 | 2098 | global $wp_query; |
| 2078 | 2099 | |
| 2079 | - return (int) $wp_query->max_num_pages - $wp_query->query_vars['paged']; | |
| 2100 | + return (int) $wp_query->max_num_pages - (int) $wp_query->query_vars['paged']; | |
| 2080 | 2101 | } |
| 2081 | 2102 | |
| 2082 | 2103 | /** |
| 2083 | 2104 | * Is the last page. |