Completed
Push — add/secondary-user-in-place-co... ( 792b2c...105dce )
by
unknown
09:07
created

The_Neverending_Home_Page::action_wp_footer()   B

Complexity

Conditions 4
Paths 8

Size

Total Lines 57

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
nc 8
nop 0
dl 0
loc 57
rs 8.9381
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
use Automattic\Jetpack\Assets;
4
use Automattic\Jetpack\Redirect;
5
6
/*
7
Plugin Name: The Neverending Home Page.
8
Plugin URI: https://automattic.com/
9
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.
10
Version: 1.1
11
Author: Automattic
12
Author URI: https://automattic.com/
13
License: GNU General Public License v2 or later
14
License URI: https://www.gnu.org/licenses/gpl-2.0.html
15
*/
16
17
/**
18
 * Class: The_Neverending_Home_Page relies on add_theme_support, expects specific
19
 * styling from each theme; including fixed footer.
20
 */
21
class The_Neverending_Home_Page {
22
23
	/**
24
	 * Register actions and filters, plus parse IS settings
25
	 *
26
	 * @uses add_action, add_filter, self::get_settings
27
	 * @return null
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
28
	 */
29
	function __construct() {
30
		add_action( 'pre_get_posts', array( $this, 'posts_per_page_query' ) );
31
		add_action( 'admin_init', array( $this, 'settings_api_init' ) );
32
		add_action( 'template_redirect', array( $this, 'action_template_redirect' ) );
33
		add_action( 'customize_preview_init', array( $this, 'init_customizer_assets' ) );
34
		add_action( 'template_redirect', array( $this, 'ajax_response' ) );
35
		add_action( 'custom_ajax_infinite_scroll', array( $this, 'query' ) );
36
		add_filter( 'infinite_scroll_query_args', array( $this, 'inject_query_args' ) );
37
		add_filter( 'infinite_scroll_allowed_vars', array( $this, 'allowed_query_vars' ) );
38
		add_action( 'the_post', array( $this, 'preserve_more_tag' ) );
39
		add_action( 'wp_footer', array( $this, 'footer' ) );
40
		add_filter( 'infinite_scroll_additional_scripts', array( $this, 'add_mejs_config' ) );
41
42
		// Plugin compatibility
43
		add_filter( 'grunion_contact_form_redirect_url', array( $this, 'filter_grunion_redirect_url' ) );
44
45
		// Parse IS settings from theme
46
		self::get_settings();
47
	}
48
49
	/**
50
	 * Initialize our static variables
51
	 */
52
	static $the_time = null;
53
	static $settings = null; // Don't access directly, instead use self::get_settings().
54
55
	static $option_name_enabled = 'infinite_scroll';
56
57
	/**
58
	 * Parse IS settings provided by theme
59
	 *
60
	 * @uses get_theme_support, infinite_scroll_has_footer_widgets, sanitize_title, add_action, get_option, wp_parse_args, is_active_sidebar
61
	 * @return object
62
	 */
63
	static function get_settings() {
64
		if ( is_null( self::$settings ) ) {
65
			$css_pattern = '#[^A-Z\d\-_]#i';
66
67
			$settings = $defaults = array(
68
				'type'            => 'scroll', // scroll | click
69
				'requested_type'  => 'scroll', // store the original type for use when logic overrides it
70
				'footer_widgets'  => false, // true | false | sidebar_id | array of sidebar_ids -- last two are checked with is_active_sidebar
71
				'container'       => 'content', // container html id
72
				'wrapper'         => true, // true | false | html class
73
				'render'          => false, // optional function, otherwise the `content` template part will be used
74
				'footer'          => true, // boolean to enable or disable the infinite footer | string to provide an html id to derive footer width from
75
				'footer_callback' => false, // function to be called to render the IS footer, in place of the default
76
				'posts_per_page'  => false, // int | false to set based on IS type
77
				'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`.
78
			);
79
80
			// Validate settings passed through add_theme_support()
81
			$_settings = get_theme_support( 'infinite-scroll' );
82
83
			if ( is_array( $_settings ) ) {
84
				// Preferred implementation, where theme provides an array of options
85
				if ( isset( $_settings[0] ) && is_array( $_settings[0] ) ) {
86
					foreach ( $_settings[0] as $key => $value ) {
87
						switch ( $key ) {
88
							case 'type' :
89
								if ( in_array( $value, array( 'scroll', 'click' ) ) )
90
									$settings[ $key ] = $settings['requested_type'] = $value;
91
92
								break;
93
94
							case 'footer_widgets' :
95
								if ( is_string( $value ) )
96
									$settings[ $key ] = sanitize_title( $value );
97
								elseif ( is_array( $value ) )
98
									$settings[ $key ] = array_map( 'sanitize_title', $value );
99
								elseif ( is_bool( $value ) )
100
									$settings[ $key ] = $value;
101
102
								break;
103
104
							case 'container' :
105 View Code Duplication
							case 'wrapper' :
106
								if ( 'wrapper' == $key && is_bool( $value ) ) {
107
									$settings[ $key ] = $value;
108
								} else {
109
									$value = preg_replace( $css_pattern, '', $value );
110
111
									if ( ! empty( $value ) )
112
										$settings[ $key ] = $value;
113
								}
114
115
								break;
116
117
							case 'render' :
118
								if ( false !== $value && is_callable( $value ) ) {
119
									$settings[ $key ] = $value;
120
								}
121
122
								break;
123
124 View Code Duplication
							case 'footer' :
125
								if ( is_bool( $value ) ) {
126
									$settings[ $key ] = $value;
127
								} elseif ( is_string( $value ) ) {
128
									$value = preg_replace( $css_pattern, '', $value );
129
130
									if ( ! empty( $value ) )
131
										$settings[ $key ] = $value;
132
								}
133
134
								break;
135
136
							case 'footer_callback' :
137
								if ( is_callable( $value ) )
138
									$settings[ $key ] = $value;
139
								else
140
									$settings[ $key ] = false;
141
142
								break;
143
144
							case 'posts_per_page' :
145
								if ( is_numeric( $value ) )
146
									$settings[ $key ] = (int) $value;
147
148
								break;
149
150
							case 'click_handle' :
151
								if ( is_bool( $value ) ) {
152
									$settings[ $key ] = $value;
153
								}
154
155
								break;
156
157
							default:
158
								break;
159
						}
160
					}
161
				} elseif ( is_string( $_settings[0] ) ) {
162
					// Checks below are for backwards compatibility
163
164
					// Container to append new posts to
165
					$settings['container'] = preg_replace( $css_pattern, '', $_settings[0] );
166
167
					// Wrap IS elements?
168
					if ( isset( $_settings[1] ) )
169
						$settings['wrapper'] = (bool) $_settings[1];
170
				}
171
			}
172
173
			// Always ensure all values are present in the final array
174
			$settings = wp_parse_args( $settings, $defaults );
0 ignored issues
show
Documentation introduced by
$defaults is of type array<string,string|bool...ick_handle":"boolean"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
175
176
			// Check if a legacy `infinite_scroll_has_footer_widgets()` function is defined and override the footer_widgets parameter's value.
177
			// Otherwise, if a widget area ID or array of IDs was provided in the footer_widgets parameter, check if any contains any widgets.
178
			// It is safe to use `is_active_sidebar()` before the sidebar is registered as this function doesn't check for a sidebar's existence when determining if it contains any widgets.
179
			if ( function_exists( 'infinite_scroll_has_footer_widgets' ) ) {
180
				$settings['footer_widgets'] = (bool) infinite_scroll_has_footer_widgets();
181
			} elseif ( is_array( $settings['footer_widgets'] ) ) {
182
				$sidebar_ids = $settings['footer_widgets'];
183
				$settings['footer_widgets'] = false;
184
185
				foreach ( $sidebar_ids as $sidebar_id ) {
186
					if ( is_active_sidebar( $sidebar_id ) ) {
187
						$settings['footer_widgets'] = true;
188
						break;
189
					}
190
				}
191
192
				unset( $sidebar_ids );
193
				unset( $sidebar_id );
194
			} elseif ( is_string( $settings['footer_widgets'] ) ) {
195
				$settings['footer_widgets'] = (bool) is_active_sidebar( $settings['footer_widgets'] );
196
			}
197
198
			/**
199
			 * Filter Infinite Scroll's `footer_widgets` parameter.
200
			 *
201
			 * @module infinite-scroll
202
			 *
203
			 * @since 2.0.0
204
			 *
205
			 * @param bool $settings['footer_widgets'] Does the current theme have Footer Widgets.
206
			 */
207
			$settings['footer_widgets'] = apply_filters( 'infinite_scroll_has_footer_widgets', $settings['footer_widgets'] );
208
209
			// Finally, after all of the sidebar checks and filtering, ensure that a boolean value is present, otherwise set to default of `false`.
210
			if ( ! is_bool( $settings['footer_widgets'] ) )
211
				$settings['footer_widgets'] = false;
212
213
			// Ensure that IS is enabled and no footer widgets exist if the IS type isn't already "click".
214
			if ( 'click' != $settings['type'] ) {
215
				// Check the setting status
216
				$disabled = '' === get_option( self::$option_name_enabled ) ? true : false;
217
218
				// Footer content or Reading option check
219
				if ( $settings['footer_widgets'] || $disabled )
220
					$settings['type'] = 'click';
221
			}
222
223
			// Force display of the click handler and attendant bits when the type isn't `click`
224
			if ( 'click' !== $settings['type'] ) {
225
				$settings['click_handle'] = true;
226
			}
227
228
			// Store final settings in a class static to avoid reparsing
229
			/**
230
			 * Filter the array of Infinite Scroll settings.
231
			 *
232
			 * @module infinite-scroll
233
			 *
234
			 * @since 2.0.0
235
			 *
236
			 * @param array $settings Array of Infinite Scroll settings.
237
			 */
238
			self::$settings = apply_filters( 'infinite_scroll_settings', $settings );
239
		}
240
241
		/** This filter is already documented in modules/infinite-scroll/infinity.php */
242
		return (object) apply_filters( 'infinite_scroll_settings', self::$settings );
243
	}
244
245
	/**
246
	 * Number of posts per page.
247
	 *
248
	 * @uses self::wp_query, self::get_settings, apply_filters
249
	 * @return int
250
	 */
251
	static function posts_per_page() {
252
		$posts_per_page = self::get_settings()->posts_per_page ? self::get_settings()->posts_per_page : self::wp_query()->get( 'posts_per_page' );
253
254
		// Take JS query into consideration here
255
		if ( true === isset( $_REQUEST['query_args']['posts_per_page'] ) ) {
256
			$posts_per_page = $_REQUEST['query_args']['posts_per_page'];
257
		}
258
259
		/**
260
		 * Filter the number of posts per page.
261
		 *
262
		 * @module infinite-scroll
263
		 *
264
		 * @since 6.0.0
265
		 *
266
		 * @param int $posts_per_page The number of posts to display per page.
267
		 */
268
		return (int) apply_filters( 'infinite_scroll_posts_per_page', $posts_per_page );
269
	}
270
271
	/**
272
	 * Retrieve the query used with Infinite Scroll
273
	 *
274
	 * @global $wp_the_query
275
	 * @uses apply_filters
276
	 * @return object
277
	 */
278
	static function wp_query() {
279
		global $wp_the_query;
280
		/**
281
		 * Filter the Infinite Scroll query object.
282
		 *
283
		 * @module infinite-scroll
284
		 *
285
		 * @since 2.2.1
286
		 *
287
		 * @param WP_Query $wp_the_query WP Query.
288
		 */
289
		return apply_filters( 'infinite_scroll_query_object', $wp_the_query );
290
	}
291
292
	/**
293
	 * Has infinite scroll been triggered?
294
	 */
295
	static function got_infinity() {
296
		/**
297
		 * Filter the parameter used to check if Infinite Scroll has been triggered.
298
		 *
299
		 * @module infinite-scroll
300
		 *
301
		 * @since 3.9.0
302
		 *
303
		 * @param bool isset( $_GET[ 'infinity' ] ) Return true if the "infinity" parameter is set.
304
		 */
305
		return apply_filters( 'infinite_scroll_got_infinity', isset( $_GET[ 'infinity' ] ) );
306
	}
307
308
	/**
309
	 * Is this guaranteed to be the last batch of posts?
310
	 */
311
	static function is_last_batch() {
312
		/**
313
		 * Override whether or not this is the last batch for a request
314
		 *
315
		 * @module infinite-scroll
316
		 *
317
		 * @since 4.8.0
318
		 *
319
		 * @param bool|null null                 Bool if value should be overridden, null to determine from query
320
		 * @param object    self::wp_query()     WP_Query object for current request
321
		 * @param object    self::get_settings() Infinite Scroll settings
322
		 */
323
		$override = apply_filters( 'infinite_scroll_is_last_batch', null, self::wp_query(), self::get_settings() );
0 ignored issues
show
Unused Code introduced by
The call to apply_filters() has too many arguments starting with self::wp_query().

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
324
		if ( is_bool( $override ) ) {
325
			return $override;
326
		}
327
328
		$entries = (int) self::wp_query()->found_posts;
329
		$posts_per_page = self::posts_per_page();
330
331
		// This is to cope with an issue in certain themes or setups where posts are returned but found_posts is 0.
332
		if ( 0 == $entries ) {
333
			return (bool) ( count( self::wp_query()->posts ) < $posts_per_page );
334
		}
335
		$paged = max( 1, self::wp_query()->get( 'paged' ) );
336
337
		// Are there enough posts for more than the first page?
338
		if ( $entries <= $posts_per_page ) {
339
			return true;
340
		}
341
342
		// Calculate entries left after a certain number of pages
343
		if ( $paged && $paged > 1 ) {
344
			$entries -= $posts_per_page * $paged;
345
		}
346
347
		// Are there some entries left to display?
348
		return $entries <= 0;
349
	}
350
351
	/**
352
	 * The more tag will be ignored by default if the blog page isn't our homepage.
353
	 * Let's force the $more global to false.
354
	 */
355
	function preserve_more_tag( $array ) {
356
		global $more;
357
358
		if ( self::got_infinity() )
359
			$more = 0; //0 = show content up to the more tag. Add more link.
360
361
		return $array;
362
	}
363
364
	/**
365
	 * Add a checkbox field to Settings > Reading
366
	 * for enabling infinite scroll.
367
	 *
368
	 * Only show if the current theme supports infinity.
369
	 *
370
	 * @uses current_theme_supports, add_settings_field, __, register_setting
371
	 * @action admin_init
372
	 * @return null
373
	 */
374
	function settings_api_init() {
375
		if ( ! current_theme_supports( 'infinite-scroll' ) )
376
			return;
377
378
		if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
379
			// This setting is no longer configurable in wp-admin on WordPress.com -- leave a pointer
380
			add_settings_field( self::$option_name_enabled,
381
				'<span id="infinite-scroll-options">' . esc_html__( 'Infinite Scroll Behavior', 'jetpack' ) . '</span>',
382
				array( $this, 'infinite_setting_html_calypso_placeholder' ),
383
				'reading'
384
			);
385
			return;
386
		}
387
388
		// Add the setting field [infinite_scroll] and place it in Settings > Reading
389
		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' );
390
		register_setting( 'reading', self::$option_name_enabled, 'esc_attr' );
391
	}
392
393
	function infinite_setting_html_calypso_placeholder() {
394
		$details     = get_blog_details();
395
		$writing_url = Redirect::get_url( 'calypso-settings-writing', array( 'site' => $details->domain ) );
396
		echo '<span>' . sprintf(
397
			/* translators: Variables are the enclosing link to the settings page */
398
			esc_html__( 'This option has moved. You can now manage it %1$shere%2$s.', 'jetpack' ),
399
			'<a href="' . esc_url( $writing_url ) . '">',
400
			'</a>'
401
		) . '</span>';
402
	}
403
404
	/**
405
	 * HTML code to display a checkbox true/false option
406
	 * for the infinite_scroll setting.
407
	 */
408
	function infinite_setting_html() {
409
		$notice = '<em>' . __( 'We&rsquo;ve changed this option to a click-to-scroll version for you since you have footer widgets in Appearance &rarr; Widgets, or your theme uses click-to-scroll as the default behavior.', 'jetpack' ) . '</em>';
410
411
		// If the blog has footer widgets, show a notice instead of the checkbox
412
		if ( self::get_settings()->footer_widgets || 'click' == self::get_settings()->requested_type ) {
413
			echo '<label>' . $notice . '</label>';
414
		} else {
415
			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>';
416
			echo '<p class="description">' . esc_html( sprintf( _n( 'Shows %s post on each load.', 'Shows %s posts on each load.', self::posts_per_page(), 'jetpack' ), number_format_i18n( self::posts_per_page() ) ) ) . '</p>';
417
		}
418
	}
419
420
	/**
421
	 * Does the legwork to determine whether the feature is enabled.
422
	 *
423
	 * @uses current_theme_supports, self::archive_supports_infinity, self::get_settings, add_filter, wp_enqueue_script, plugins_url, wp_enqueue_style, add_action
424
	 * @action template_redirect
425
	 * @return null
426
	 */
427
	function action_template_redirect() {
428
		// Check that we support infinite scroll, and are on the home page.
429
		if ( ! current_theme_supports( 'infinite-scroll' ) || ! self::archive_supports_infinity() )
430
			return;
431
432
		$id = self::get_settings()->container;
433
434
		// Check that we have an id.
435
		if ( empty( $id ) )
436
			return;
437
438
		// Add our scripts.
439
		wp_register_script(
440
			'the-neverending-homepage',
441
			Assets::get_file_url_for_environment(
442
				'_inc/build/infinite-scroll/infinity.min.js',
443
				'modules/infinite-scroll/infinity.js'
444
			),
445
			array(),
446
			JETPACK__VERSION . '-is5.0.0', // Added for ability to cachebust on WP.com.
447
			true
448
		);
449
450
		// Add our default styles.
451
		wp_register_style( 'the-neverending-homepage', plugins_url( 'infinity.css', __FILE__ ), array(), '20140422' );
452
453
		// Make sure there are enough posts for IS
454
		if ( self::is_last_batch() ) {
455
			return;
456
		}
457
458
		// Add our scripts.
459
		wp_enqueue_script( 'the-neverending-homepage' );
460
461
		// Add our default styles.
462
		wp_enqueue_style( 'the-neverending-homepage' );
463
464
		add_action( 'wp_footer', array( $this, 'action_wp_footer_settings' ), 2 );
465
466
		add_action( 'wp_footer', array( $this, 'action_wp_footer' ), 21 ); // Core prints footer scripts at priority 20, so we just need to be one later than that
467
468
		add_filter( 'infinite_scroll_results', array( $this, 'filter_infinite_scroll_results' ), 10, 3 );
469
	}
470
471
	/**
472
	 * Initialize the Customizer logic separately from the main JS.
473
	 *
474
	 * @since 8.4.0
475
	 */
476
	public function init_customizer_assets() {
477
		// Add our scripts.
478
		wp_register_script(
479
			'the-neverending-homepage-customizer',
480
			Assets::get_file_url_for_environment(
481
				'_inc/build/infinite-scroll/infinity-customizer.min.js',
482
				'modules/infinite-scroll/infinity-customizer.js'
483
			),
484
			array( 'customize-base' ),
485
			JETPACK__VERSION . '-is5.0.0', // Added for ability to cachebust on WP.com.
486
			true
487
		);
488
489
		wp_enqueue_script( 'the-neverending-homepage-customizer' );
490
	}
491
492
	/**
493
	 * Returns classes to be added to <body>. If it's enabled, 'infinite-scroll'. If set to continuous scroll, adds 'neverending' too.
494
	 *
495
	 * @since 4.7.0 No longer added as a 'body_class' filter but passed to JS environment and added using JS.
496
	 *
497
	 * @return string
498
	 */
499
	function body_class() {
500
		$classes = '';
501
		// Do not add infinity-scroll class if disabled through the Reading page
502
		$disabled = '' === get_option( self::$option_name_enabled ) ? true : false;
503
		if ( ! $disabled || 'click' == self::get_settings()->type ) {
504
			$classes = 'infinite-scroll';
505
506
			if ( 'scroll' == self::get_settings()->type )
507
				$classes .= ' neverending';
508
		}
509
510
		return $classes;
511
	}
512
513
	/**
514
	 * In case IS is activated on search page, we have to exclude initially loaded posts which match the keyword by title, not the content as they are displayed before content-matching ones
515
	 *
516
	 * @uses self::wp_query
517
	 * @uses self::get_last_post_date
518
	 * @uses self::has_only_title_matching_posts
519
	 * @return array
520
	 */
521
	function get_excluded_posts() {
522
523
		$excluded_posts = array();
524
		//loop through posts returned by wp_query call
525
		foreach( self::wp_query()->get_posts() as $post ) {
526
527
			$orderby = isset( self::wp_query()->query_vars['orderby'] ) ? self::wp_query()->query_vars['orderby'] : '';
528
			$post_date = ( ! empty( $post->post_date ) ? $post->post_date : false );
529
			if ( 'modified' === $orderby || false === $post_date ) {
530
				$post_date = $post->post_modified;
531
			}
532
533
			//in case all posts initially displayed match the keyword by title we add em all to excluded posts array
534
			//else, we add only posts which are older than last_post_date param as newer are natually excluded by last_post_date condition in the SQL query
535
			if ( self::has_only_title_matching_posts() || $post_date <= self::get_last_post_date() ) {
536
				array_push( $excluded_posts, $post->ID );
537
			}
538
		}
539
		return $excluded_posts;
540
	}
541
542
	/**
543
	 * In case IS is active on search, we have to exclude posts matched by title rather than by post_content in order to prevent dupes on next pages
544
	 *
545
	 * @uses self::wp_query
546
	 * @uses self::get_excluded_posts
547
	 * @return array
548
	 */
549
	function get_query_vars() {
550
551
		$query_vars = self::wp_query()->query_vars;
552
		//applies to search page only
553
		if ( true === self::wp_query()->is_search() ) {
554
			//set post__not_in array in query_vars in case it does not exists
555
			if ( false === isset( $query_vars['post__not_in'] ) ) {
556
				$query_vars['post__not_in'] = array();
557
			}
558
			//get excluded posts
559
			$excluded = self::get_excluded_posts();
560
			//merge them with other post__not_in posts (eg.: sticky posts)
561
			$query_vars['post__not_in'] = array_merge( $query_vars['post__not_in'], $excluded );
562
		}
563
		return $query_vars;
564
	}
565
566
	/**
567
	 * This function checks whether all posts returned by initial wp_query match the keyword by title
568
	 * The code used in this function is borrowed from WP_Query class where it is used to construct like conditions for keywords
569
	 *
570
	 * @uses self::wp_query
571
	 * @return bool
572
	 */
573
	function has_only_title_matching_posts() {
574
575
		//apply following logic for search page results only
576
		if ( false === self::wp_query()->is_search() ) {
577
			return false;
578
		}
579
580
		//grab the last posts in the stack as if the last one is title-matching the rest is title-matching as well
581
		$post = end( self::wp_query()->posts );
582
583
		//code inspired by WP_Query class
584
		if ( preg_match_all( '/".*?("|$)|((?<=[\t ",+])|^)[^\t ",+]+/', self::wp_query()->get( 's' ), $matches ) ) {
585
			$search_terms = self::wp_query()->query_vars['search_terms'];
586
			// if the search string has only short terms or stopwords, or is 10+ terms long, match it as sentence
587
			if ( empty( $search_terms ) || count( $search_terms ) > 9 ) {
588
				$search_terms = array( self::wp_query()->get( 's' ) );
589
			}
590
		} else {
591
			$search_terms = array( self::wp_query()->get( 's' ) );
592
		}
593
594
		//actual testing. As search query combines multiple keywords with AND, it's enough to check if any of the keywords is present in the title
595
		$term = current( $search_terms );
596
		if ( ! empty( $term ) && false !== strpos( $post->post_title, $term ) ) {
597
			return true;
598
		}
599
600
		return false;
601
	}
602
603
	/**
604
	 * Grab the timestamp for the initial query's last post.
605
	 *
606
	 * This takes into account the query's 'orderby' parameter and returns
607
	 * false if the posts are not ordered by date.
608
	 *
609
	 * @uses self::got_infinity
610
	 * @uses self::has_only_title_matching_posts
611
	 * @uses self::wp_query
612
	 * @return string 'Y-m-d H:i:s' or false
613
	 */
614
	function get_last_post_date() {
615
		if ( self::got_infinity() )
616
			return;
617
618
		if ( ! self::wp_query()->have_posts() ) {
619
			return null;
620
		}
621
622
		//In case there are only title-matching posts in the initial WP_Query result, we don't want to use the last_post_date param yet
623
		if ( true === self::has_only_title_matching_posts() ) {
624
			return false;
625
		}
626
627
		$post = end( self::wp_query()->posts );
628
		$orderby = isset( self::wp_query()->query_vars['orderby'] ) ?
629
			self::wp_query()->query_vars['orderby'] : '';
630
		$post_date = ( ! empty( $post->post_date ) ? $post->post_date : false );
631 View Code Duplication
		switch ( $orderby ) {
632
			case 'modified':
633
				return $post->post_modified;
634
			case 'date':
635
			case '':
636
				return $post_date;
637
			default:
638
				return false;
639
		}
640
	}
641
642
	/**
643
	 * Returns the appropriate `wp_posts` table field for a given query's
644
	 * 'orderby' parameter, if applicable.
645
	 *
646
	 * @param optional object $query
647
	 * @uses self::wp_query
648
	 * @return string or false
649
	 */
650
	function get_query_sort_field( $query = null ) {
651
		if ( empty( $query ) )
652
			$query = self::wp_query();
653
654
		$orderby = isset( $query->query_vars['orderby'] ) ? $query->query_vars['orderby'] : '';
655
656 View Code Duplication
		switch ( $orderby ) {
657
			case 'modified':
658
				return 'post_modified';
659
			case 'date':
660
			case '':
661
				return 'post_date';
662
			default:
663
				return false;
664
		}
665
	}
666
667
	/**
668
	 * Create a where clause that will make sure post queries return posts
669
	 * in the correct order, without duplicates, if a new post is added
670
	 * and we're sorting by post date.
671
	 *
672
	 * @global $wpdb
673
	 * @param string $where
674
	 * @param object $query
675
	 * @uses apply_filters
676
	 * @filter posts_where
677
	 * @return string
678
	 */
679
	function query_time_filter( $where, $query ) {
680
		if ( self::got_infinity() ) {
681
			global $wpdb;
682
683
			$sort_field = self::get_query_sort_field( $query );
684
685
			if ( 'post_date' !== $sort_field || 'DESC' !== $_REQUEST['query_args']['order'] ) {
686
				return $where;
687
			}
688
689
			$query_before = sanitize_text_field( wp_unslash( $_REQUEST['query_before'] ) );
690
691
			if ( empty( $query_before ) ) {
692
				return $where;
693
			}
694
695
			// Construct the date query using our timestamp
696
			$clause = $wpdb->prepare( " AND {$wpdb->posts}.post_date <= %s", $query_before );
697
698
			/**
699
			 * Filter Infinite Scroll's SQL date query making sure post queries
700
			 * will always return results prior to (descending sort)
701
			 * or before (ascending sort) the last post date.
702
			 *
703
			 * @module infinite-scroll
704
			 *
705
			 * @param string $clause SQL Date query.
706
			 * @param object $query Query.
707
			 * @param string $operator @deprecated Query operator.
708
			 * @param string $last_post_date @deprecated Last Post Date timestamp.
709
			 */
710
			$operator       = 'ASC' === $_REQUEST['query_args']['order'] ? '>' : '<';
711
			$last_post_date = sanitize_text_field( wp_unslash( $_REQUEST['last_post_date'] ) );
712
			$where         .= apply_filters( 'infinite_scroll_posts_where', $clause, $query, $operator, $last_post_date );
0 ignored issues
show
Unused Code introduced by
The call to apply_filters() has too many arguments starting with $query.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
713
		}
714
715
		return $where;
716
	}
717
718
	/**
719
	 * Let's overwrite the default post_per_page setting to always display a fixed amount.
720
	 *
721
	 * @param object $query
722
	 * @uses is_admin, self::archive_supports_infinity, self::get_settings
723
	 * @return null
724
	 */
725
	function posts_per_page_query( $query ) {
726
		if ( ! is_admin() && self::archive_supports_infinity() && $query->is_main_query() )
727
			$query->set( 'posts_per_page', self::posts_per_page() );
728
	}
729
730
	/**
731
	 * Check if the IS output should be wrapped in a div.
732
	 * Setting value can be a boolean or a string specifying the class applied to the div.
733
	 *
734
	 * @uses self::get_settings
735
	 * @return bool
736
	 */
737
	function has_wrapper() {
738
		return (bool) self::get_settings()->wrapper;
739
	}
740
741
	/**
742
	 * Returns the Ajax url
743
	 *
744
	 * @global $wp
745
	 * @uses home_url, add_query_arg, apply_filters
746
	 * @return string
747
	 */
748
	function ajax_url() {
749
		$base_url = set_url_scheme( home_url( '/' ) );
750
751
		$ajaxurl = add_query_arg( array( 'infinity' => 'scrolling' ), $base_url );
752
753
		/**
754
		 * Filter the Infinite Scroll Ajax URL.
755
		 *
756
		 * @module infinite-scroll
757
		 *
758
		 * @since 2.0.0
759
		 *
760
		 * @param string $ajaxurl Infinite Scroll Ajax URL.
761
		 */
762
		return apply_filters( 'infinite_scroll_ajax_url', $ajaxurl );
763
	}
764
765
	/**
766
	 * Our own Ajax response, avoiding calling admin-ajax
767
	 */
768
	function ajax_response() {
769
		// Only proceed if the url query has a key of "Infinity"
770
		if ( ! self::got_infinity() )
771
			return false;
772
773
		// This should already be defined below, but make sure.
774
		if ( ! defined( 'DOING_AJAX' ) ) {
775
			define( 'DOING_AJAX', true );
776
		}
777
778
		@header( 'Content-Type: text/html; charset=' . get_option( 'blog_charset' ) );
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
779
		send_nosniff_header();
780
781
		/**
782
		 * Fires at the end of the Infinite Scroll Ajax response.
783
		 *
784
		 * @module infinite-scroll
785
		 *
786
		 * @since 2.0.0
787
		 */
788
		do_action( 'custom_ajax_infinite_scroll' );
789
		die( '0' );
790
	}
791
792
	/**
793
	 * Alias for renamed class method.
794
	 *
795
	 * Previously, JS settings object was unnecessarily output in the document head.
796
	 * When the hook was changed, the method name no longer made sense.
797
	 */
798
	function action_wp_head() {
799
		$this->action_wp_footer_settings();
800
	}
801
802
	/**
803
	 * Prints the relevant infinite scroll settings in JS.
804
	 *
805
	 * @global $wp_rewrite
806
	 * @uses self::get_settings, esc_js, esc_url_raw, self::has_wrapper, __, apply_filters, do_action, self::get_query_vars
807
	 * @action wp_footer
808
	 * @return string
809
	 */
810
	function action_wp_footer_settings() {
811
		global $wp_rewrite;
812
		global $currentday;
813
814
		// Default click handle text
815
		$click_handle_text = __( 'Older posts', 'jetpack' );
816
817
		// If a single CPT is displayed, use its plural name instead of "posts"
818
		// Could be empty (posts) or an array of multiple post types.
819
		// In the latter two cases cases, the default text is used, leaving the `infinite_scroll_js_settings` filter for further customization.
820
		$post_type = self::wp_query()->get( 'post_type' );
821
822
		// If it's a taxonomy, try to change the button text.
823
		if ( is_tax() ) {
824
			// Get current taxonomy slug.
825
			$taxonomy_slug = self::wp_query()->get( 'taxonomy' );
826
827
			// Get taxonomy settings.
828
			$taxonomy = get_taxonomy( $taxonomy_slug );
829
830
			// Check if the taxonomy is attached to one post type only and use its plural name.
831
			// If not, use "Posts" without confusing the users.
832
			if (
833
				is_a( $taxonomy, 'WP_Taxonomy' )
834
				&& is_countable( $taxonomy->object_type )
835
				&& count( $taxonomy->object_type ) < 2
836
			) {
837
				$post_type = $taxonomy->object_type[0];
838
			}
839
		}
840
841
		if ( is_string( $post_type ) && ! empty( $post_type ) ) {
842
			$post_type = get_post_type_object( $post_type );
843
844
			if ( is_object( $post_type ) && ! is_wp_error( $post_type ) ) {
845
				if ( isset( $post_type->labels->name ) ) {
846
					$cpt_text = $post_type->labels->name;
847
				} elseif ( isset( $post_type->label ) ) {
848
					$cpt_text = $post_type->label;
849
				}
850
851
				if ( isset( $cpt_text ) ) {
852
					/* translators: %s is the name of a custom post type */
853
					$click_handle_text = sprintf( __( 'More %s', 'jetpack' ), $cpt_text );
854
					unset( $cpt_text );
855
				}
856
			}
857
		}
858
859
		unset( $post_type );
860
861
		// Base JS settings
862
		$js_settings = array(
863
			'id'               => self::get_settings()->container,
864
			'ajaxurl'          => esc_url_raw( self::ajax_url() ),
865
			'type'             => esc_js( self::get_settings()->type ),
866
			'wrapper'          => self::has_wrapper(),
867
			'wrapper_class'    => is_string( self::get_settings()->wrapper ) ? esc_js( self::get_settings()->wrapper ) : 'infinite-wrap',
868
			'footer'           => is_string( self::get_settings()->footer ) ? esc_js( self::get_settings()->footer ) : self::get_settings()->footer,
869
			'click_handle'     => esc_js( self::get_settings()->click_handle ),
870
			'text'             => esc_js( $click_handle_text ),
871
			'totop'            => esc_js( __( 'Scroll back to top', 'jetpack' ) ),
872
			'currentday'       => $currentday,
873
			'order'            => 'DESC',
874
			'scripts'          => array(),
875
			'styles'           => array(),
876
			'google_analytics' => false,
877
			'offset'           => max( 1, self::wp_query()->get( 'paged' ) ), // Pass through the current page so we can use that to offset the first load.
878
			'history'          => array(
879
				'host'                 => preg_replace( '#^http(s)?://#i', '', untrailingslashit( esc_url( get_home_url() ) ) ),
880
				'path'                 => self::get_request_path(),
881
				'use_trailing_slashes' => $wp_rewrite->use_trailing_slashes,
882
				'parameters'           => self::get_request_parameters(),
883
			),
884
			'query_args'      => self::get_query_vars(),
885
			'query_before'    => current_time( 'mysql' ),
886
			'last_post_date'  => self::get_last_post_date(),
887
			'body_class'	  => self::body_class(),
888
			'loading_text'	  => esc_js( __( 'Loading new page', 'jetpack' ) ),
889
		);
890
891
		// Optional order param
892
		if ( isset( $_REQUEST['order'] ) ) {
893
			$order = strtoupper( $_REQUEST['order'] );
894
895
			if ( in_array( $order, array( 'ASC', 'DESC' ) ) )
896
				$js_settings['order'] = $order;
897
		}
898
899
		/**
900
		 * Filter the Infinite Scroll JS settings outputted in the head.
901
		 *
902
		 * @module infinite-scroll
903
		 *
904
		 * @since 2.0.0
905
		 *
906
		 * @param array $js_settings Infinite Scroll JS settings.
907
		 */
908
		$js_settings = apply_filters( 'infinite_scroll_js_settings', $js_settings );
909
910
		/**
911
		 * Fires before Infinite Scroll outputs inline JavaScript in the head.
912
		 *
913
		 * @module infinite-scroll
914
		 *
915
		 * @since 2.0.0
916
		 */
917
		do_action( 'infinite_scroll_wp_head' );
918
919
		?>
920
		<script type="text/javascript">
921
		//<![CDATA[
922
		var infiniteScroll = JSON.parse( decodeURIComponent( '<?php echo
923
			rawurlencode( json_encode( array( 'settings' => $js_settings ) ) );
924
		?>' ) );
925
		//]]>
926
		</script>
927
		<?php
928
	}
929
930
	/**
931
	 * Build path data for current request.
932
	 * Used for Google Analytics and pushState history tracking.
933
	 *
934
	 * @global $wp_rewrite
935
	 * @global $wp
936
	 * @uses user_trailingslashit, sanitize_text_field, add_query_arg
937
	 * @return string|bool
938
	 */
939
	private function get_request_path() {
940
		global $wp_rewrite;
941
942
		if ( $wp_rewrite->using_permalinks() ) {
943
			global $wp;
944
945
			// If called too early, bail
946
			if ( ! isset( $wp->request ) )
947
				return false;
948
949
			// Determine path for paginated version of current request
950
			if ( false != preg_match( '#' . $wp_rewrite->pagination_base . '/\d+/?$#i', $wp->request ) )
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing preg_match('#' . $wp_rew...d+/?$#i', $wp->request) of type integer to the boolean false. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
951
				$path = preg_replace( '#' . $wp_rewrite->pagination_base . '/\d+$#i', $wp_rewrite->pagination_base . '/%d', $wp->request );
952
			else
953
				$path = $wp->request . '/' . $wp_rewrite->pagination_base . '/%d';
954
955
			// Slashes everywhere we need them
956
			if ( 0 !== strpos( $path, '/' ) )
957
				$path = '/' . $path;
958
959
			$path = user_trailingslashit( $path );
960
		} else {
961
			// Clean up raw $_REQUEST input
962
			$path = array_map( 'sanitize_text_field', $_REQUEST );
963
			$path = array_filter( $path );
964
965
			$path['paged'] = '%d';
966
967
			$path = add_query_arg( $path, '/' );
968
		}
969
970
		return empty( $path ) ? false : $path;
971
	}
972
973
	/**
974
	 * Return query string for current request, prefixed with '?'.
975
	 *
976
	 * @return string
977
	 */
978
	private function get_request_parameters() {
979
		$uri = $_SERVER[ 'REQUEST_URI' ];
980
		$uri = preg_replace( '/^[^?]*(\?.*$)/', '$1', $uri, 1, $count );
981
		if ( $count != 1 )
982
			return '';
983
		return $uri;
984
	}
985
986
	/**
987
	 * Provide IS with a list of the scripts and stylesheets already present on the page.
988
	 * Since posts may contain require additional assets that haven't been loaded, this data will be used to track the additional assets.
989
	 *
990
	 * @global $wp_scripts, $wp_styles
991
	 * @action wp_footer
992
	 * @return string
993
	 */
994
	function action_wp_footer() {
995
		global $wp_scripts, $wp_styles;
996
997
		$scripts = is_a( $wp_scripts, 'WP_Scripts' ) ? $wp_scripts->done : array();
998
		/**
999
		 * Filter the list of scripts already present on the page.
1000
		 *
1001
		 * @module infinite-scroll
1002
		 *
1003
		 * @since 2.1.2
1004
		 *
1005
		 * @param array $scripts Array of scripts present on the page.
1006
		 */
1007
		$scripts = apply_filters( 'infinite_scroll_existing_scripts', $scripts );
1008
1009
		$styles = is_a( $wp_styles, 'WP_Styles' ) ? $wp_styles->done : array();
1010
		/**
1011
		 * Filter the list of styles already present on the page.
1012
		 *
1013
		 * @module infinite-scroll
1014
		 *
1015
		 * @since 2.1.2
1016
		 *
1017
		 * @param array $styles Array of styles present on the page.
1018
		 */
1019
		$styles = apply_filters( 'infinite_scroll_existing_stylesheets', $styles );
1020
1021
		?><script type="text/javascript">
1022
			(function() {
1023
				var extend = function(out) {
1024
					out = out || {};
1025
1026
					for (var i = 1; i < arguments.length; i++) {
1027
						if (!arguments[i])
1028
						continue;
1029
1030
						for (var key in arguments[i]) {
1031
						if (arguments[i].hasOwnProperty(key))
1032
							out[key] = arguments[i][key];
1033
						}
1034
					}
1035
1036
					return out;
1037
				};
1038
				extend( window.infiniteScroll.settings.scripts, <?php echo wp_json_encode( $scripts ); ?> );
1039
				extend( window.infiniteScroll.settings.styles, <?php echo wp_json_encode( $styles ); ?> );
1040
			})();
1041
		</script>
1042
		<?php
1043
		$aria_live = 'assertive';
1044
		if ( 'scroll' === self::get_settings()->type ) {
1045
			$aria_live = 'polite';
1046
		}
1047
		?>
1048
		<span id="infinite-aria" aria-live="<?php echo esc_attr( $aria_live ); ?>"></span>
1049
		<?php
1050
	}
1051
1052
	/**
1053
	 * Identify additional scripts required by the latest set of IS posts and provide the necessary data to the IS response handler.
1054
	 *
1055
	 * @global $wp_scripts
1056
	 * @uses sanitize_text_field, add_query_arg
1057
	 * @filter infinite_scroll_results
1058
	 * @return array
1059
	 */
1060
	function filter_infinite_scroll_results( $results, $query_args, $wp_query ) {
1061
		// Don't bother unless there are posts to display
1062
		if ( 'success' != $results['type'] )
1063
			return $results;
1064
1065
		// Parse and sanitize the script handles already output
1066
		$initial_scripts = isset( $_REQUEST['scripts'] ) && is_array( $_REQUEST['scripts'] ) ? array_map( 'sanitize_text_field', $_REQUEST['scripts'] ) : false;
1067
1068
		if ( is_array( $initial_scripts ) ) {
1069
			global $wp_scripts;
1070
1071
			// Identify new scripts needed by the latest set of IS posts
1072
			$new_scripts = array_filter(
1073
				$wp_scripts->done,
1074
				function ( $script_name ) use ( $initial_scripts ) {
1075
					// Jetpack block scripts should always be sent, even if they've been
1076
					// sent before. These scripts only run once on when loaded, they don't
1077
					// watch for new blocks being added.
1078
					if ( 0 === strpos( $script_name, 'jetpack-block-' ) ) {
1079
						return true;
1080
					}
1081
1082
					return ! in_array( $script_name, $initial_scripts, true );
1083
				}
1084
			);
1085
1086
			// If new scripts are needed, extract relevant data from $wp_scripts
1087
			if ( ! empty( $new_scripts ) ) {
1088
				$results['scripts'] = array();
1089
1090
				foreach ( $new_scripts as $handle ) {
1091
					// Abort if somehow the handle doesn't correspond to a registered script
1092
					// or if the script doesn't have `src` set.
1093
					$script_not_registered = ! isset( $wp_scripts->registered[ $handle ] );
1094
					$empty_src             = empty( $wp_scripts->registered[ $handle ]->src );
1095
					if ( $script_not_registered || $empty_src ) {
1096
						continue;
1097
					}
1098
1099
					// Provide basic script data
1100
					$script_data = array(
1101
						'handle'        => $handle,
1102
						'footer'        => ( is_array( $wp_scripts->in_footer ) && in_array( $handle, $wp_scripts->in_footer, true ) ),
1103
						'extra_data'    => $wp_scripts->print_extra_script( $handle, false ),
1104
						'before_handle' => $wp_scripts->print_inline_script( $handle, 'before', false ),
1105
						'after_handle'  => $wp_scripts->print_inline_script( $handle, 'after', false ),
1106
					);
1107
1108
					// Base source
1109
					$src = $wp_scripts->registered[ $handle ]->src;
1110
1111
					// Take base_url into account
1112
					if ( strpos( $src, 'http' ) !== 0 )
1113
						$src = $wp_scripts->base_url . $src;
1114
1115
					// Version and additional arguments
1116 View Code Duplication
					if ( null === $wp_scripts->registered[ $handle ]->ver )
1117
						$ver = '';
1118
					else
1119
						$ver = $wp_scripts->registered[ $handle ]->ver ? $wp_scripts->registered[ $handle ]->ver : $wp_scripts->default_version;
1120
1121 View Code Duplication
					if ( isset( $wp_scripts->args[ $handle ] ) )
1122
						$ver = $ver ? $ver . '&amp;' . $wp_scripts->args[$handle] : $wp_scripts->args[$handle];
1123
1124
					// Full script source with version info
1125
					$script_data['src'] = add_query_arg( 'ver', $ver, $src );
1126
1127
					// Add script to data that will be returned to IS JS
1128
					array_push( $results['scripts'], $script_data );
1129
				}
1130
			}
1131
		}
1132
1133
		// Expose additional script data to filters, but only include in final `$results` array if needed.
1134
		if ( ! isset( $results['scripts'] ) )
1135
			$results['scripts'] = array();
1136
1137
		/**
1138
		 * Filter the additional scripts required by the latest set of IS posts.
1139
		 *
1140
		 * @module infinite-scroll
1141
		 *
1142
		 * @since 2.1.2
1143
		 *
1144
		 * @param array $results['scripts'] Additional scripts required by the latest set of IS posts.
1145
		 * @param array|bool $initial_scripts Set of scripts loaded on each page.
1146
		 * @param array $results Array of Infinite Scroll results.
1147
		 * @param array $query_args Array of Query arguments.
1148
		 * @param WP_Query $wp_query WP Query.
1149
		 */
1150
		$results['scripts'] = apply_filters(
1151
			'infinite_scroll_additional_scripts',
1152
			$results['scripts'],
1153
			$initial_scripts,
0 ignored issues
show
Unused Code introduced by
The call to apply_filters() has too many arguments starting with $initial_scripts.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
1154
			$results,
1155
			$query_args,
1156
			$wp_query
1157
		);
1158
1159
		if ( empty( $results['scripts'] ) )
1160
			unset( $results['scripts' ] );
1161
1162
		// Parse and sanitize the style handles already output
1163
		$initial_styles = isset( $_REQUEST['styles'] ) && is_array( $_REQUEST['styles'] ) ? array_map( 'sanitize_text_field', $_REQUEST['styles'] ) : false;
1164
1165
		if ( is_array( $initial_styles ) ) {
1166
			global $wp_styles;
1167
1168
			// Identify new styles needed by the latest set of IS posts
1169
			$new_styles = array_diff( $wp_styles->done, $initial_styles );
1170
1171
			// If new styles are needed, extract relevant data from $wp_styles
1172
			if ( ! empty( $new_styles ) ) {
1173
				$results['styles'] = array();
1174
1175
				foreach ( $new_styles as $handle ) {
1176
					// Abort if somehow the handle doesn't correspond to a registered stylesheet
1177
					if ( ! isset( $wp_styles->registered[ $handle ] ) )
1178
						continue;
1179
1180
					// Provide basic style data
1181
					$style_data = array(
1182
						'handle' => $handle,
1183
						'media'  => 'all'
1184
					);
1185
1186
					// Base source
1187
					$src = $wp_styles->registered[ $handle ]->src;
1188
1189
					// Take base_url into account
1190
					if ( strpos( $src, 'http' ) !== 0 )
1191
						$src = $wp_styles->base_url . $src;
1192
1193
					// Version and additional arguments
1194 View Code Duplication
					if ( null === $wp_styles->registered[ $handle ]->ver )
1195
						$ver = '';
1196
					else
1197
						$ver = $wp_styles->registered[ $handle ]->ver ? $wp_styles->registered[ $handle ]->ver : $wp_styles->default_version;
1198
1199 View Code Duplication
					if ( isset($wp_styles->args[ $handle ] ) )
1200
						$ver = $ver ? $ver . '&amp;' . $wp_styles->args[$handle] : $wp_styles->args[$handle];
1201
1202
					// Full stylesheet source with version info
1203
					$style_data['src'] = add_query_arg( 'ver', $ver, $src );
1204
1205
					// Parse stylesheet's conditional comments if present, converting to logic executable in JS
1206
					if ( isset( $wp_styles->registered[ $handle ]->extra['conditional'] ) && $wp_styles->registered[ $handle ]->extra['conditional'] ) {
1207
						// First, convert conditional comment operators to standard logical operators. %ver is replaced in JS with the IE version
1208
						$style_data['conditional'] = str_replace( array(
1209
							'lte',
1210
							'lt',
1211
							'gte',
1212
							'gt'
1213
						), array(
1214
							'%ver <=',
1215
							'%ver <',
1216
							'%ver >=',
1217
							'%ver >',
1218
						), $wp_styles->registered[ $handle ]->extra['conditional'] );
1219
1220
						// Next, replace any !IE checks. These shouldn't be present since WP's conditional stylesheet implementation doesn't support them, but someone could be _doing_it_wrong().
1221
						$style_data['conditional'] = preg_replace( '#!\s*IE(\s*\d+){0}#i', '1==2', $style_data['conditional'] );
1222
1223
						// Lastly, remove the IE strings
1224
						$style_data['conditional'] = str_replace( 'IE', '', $style_data['conditional'] );
1225
					}
1226
1227
					// Parse requested media context for stylesheet
1228 View Code Duplication
					if ( isset( $wp_styles->registered[ $handle ]->args ) )
1229
						$style_data['media'] = esc_attr( $wp_styles->registered[ $handle ]->args );
1230
1231
					// Add stylesheet to data that will be returned to IS JS
1232
					array_push( $results['styles'], $style_data );
1233
				}
1234
			}
1235
		}
1236
1237
		// Expose additional stylesheet data to filters, but only include in final `$results` array if needed.
1238
		if ( ! isset( $results['styles'] ) )
1239
			$results['styles'] = array();
1240
1241
		/**
1242
		 * Filter the additional styles required by the latest set of IS posts.
1243
		 *
1244
		 * @module infinite-scroll
1245
		 *
1246
		 * @since 2.1.2
1247
		 *
1248
		 * @param array $results['styles'] Additional styles required by the latest set of IS posts.
1249
		 * @param array|bool $initial_styles Set of styles loaded on each page.
1250
		 * @param array $results Array of Infinite Scroll results.
1251
		 * @param array $query_args Array of Query arguments.
1252
		 * @param WP_Query $wp_query WP Query.
1253
		 */
1254
		$results['styles'] = apply_filters(
1255
			'infinite_scroll_additional_stylesheets',
1256
			$results['styles'],
1257
			$initial_styles,
0 ignored issues
show
Unused Code introduced by
The call to apply_filters() has too many arguments starting with $initial_styles.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
1258
			$results,
1259
			$query_args,
1260
			$wp_query
1261
		);
1262
1263
		if ( empty( $results['styles'] ) )
1264
			unset( $results['styles' ] );
1265
1266
		// Lastly, return the IS results array
1267
		return $results;
1268
	}
1269
1270
	/**
1271
	 * Runs the query and returns the results via JSON.
1272
	 * Triggered by an AJAX request.
1273
	 *
1274
	 * @global $wp_query
1275
	 * @global $wp_the_query
1276
	 * @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
1277
	 * @return string or null
1278
	 */
1279
	function query() {
1280
		if ( ! isset( $_REQUEST['page'] ) || ! current_theme_supports( 'infinite-scroll' ) )
1281
			die;
1282
1283
		$page = (int) $_REQUEST['page'];
1284
1285
		// Sanitize and set $previousday. Expected format: dd.mm.yy
1286
		if ( preg_match( '/^\d{2}\.\d{2}\.\d{2}$/', $_REQUEST['currentday'] ) ) {
1287
			global $previousday;
1288
			$previousday = $_REQUEST['currentday'];
1289
		}
1290
1291
		$post_status = array( 'publish' );
1292
		if ( current_user_can( 'read_private_posts' ) )
1293
			array_push( $post_status, 'private' );
1294
1295
		$order = in_array( $_REQUEST['order'], array( 'ASC', 'DESC' ) ) ? $_REQUEST['order'] : 'DESC';
1296
1297
		$query_args = array_merge( self::wp_query()->query_vars, array(
1298
			'paged'          => $page,
1299
			'post_status'    => $post_status,
1300
			'posts_per_page' => self::posts_per_page(),
1301
			'order'          => $order
1302
		) );
1303
1304
		// 4.0 ?s= compatibility, see https://core.trac.wordpress.org/ticket/11330#comment:50
1305
		if ( empty( $query_args['s'] ) && ! isset( self::wp_query()->query['s'] ) ) {
1306
			unset( $query_args['s'] );
1307
		}
1308
1309
		// By default, don't query for a specific page of a paged post object.
1310
		// This argument can come from merging self::wp_query() into $query_args above.
1311
		// Since IS is only used on archives, we should always display the first page of any paged content.
1312
		unset( $query_args['page'] );
1313
1314
		/**
1315
		 * Filter the array of main query arguments.
1316
		 *
1317
		 * @module infinite-scroll
1318
		 *
1319
		 * @since 2.0.1
1320
		 *
1321
		 * @param array $query_args Array of Query arguments.
1322
		 */
1323
		$query_args = apply_filters( 'infinite_scroll_query_args', $query_args );
1324
1325
		add_filter( 'posts_where', array( $this, 'query_time_filter' ), 10, 2 );
1326
1327
		$GLOBALS['wp_the_query'] = $GLOBALS['wp_query'] = $infinite_scroll_query = new WP_Query();
1328
1329
		$infinite_scroll_query->query( $query_args );
1330
1331
		remove_filter( 'posts_where', array( $this, 'query_time_filter' ), 10, 2 );
1332
1333
		$results = array();
1334
1335
		if ( have_posts() ) {
1336
			// Fire wp_head to ensure that all necessary scripts are enqueued. Output isn't used, but scripts are extracted in self::action_wp_footer.
1337
			ob_start();
1338
			wp_head();
1339
			while ( ob_get_length() ) {
1340
				ob_end_clean();
1341
			}
1342
1343
			$results['type'] = 'success';
1344
1345
			/**
1346
			 * Gather renderer callbacks. These will be called in order and allow multiple callbacks to be queued. Once content is found, no futher callbacks will run.
1347
			 *
1348
			 * @module infinite-scroll
1349
			 *
1350
			 * @since 6.0.0
1351
			 */
1352
			$callbacks = apply_filters( 'infinite_scroll_render_callbacks', array(
1353
				self::get_settings()->render, // This is the setting callback e.g. from add theme support.
1354
			) );
1355
1356
			// Append fallback callback. That rhymes.
1357
			$callbacks[] = array( $this, 'render' );
1358
1359
			foreach ( $callbacks as $callback ) {
1360
				if ( false !== $callback && is_callable( $callback ) ) {
1361
					rewind_posts();
1362
					ob_start();
1363
					add_action( 'infinite_scroll_render', $callback );
1364
1365
					/**
1366
					 * Fires when rendering Infinite Scroll posts.
1367
					 *
1368
					 * @module infinite-scroll
1369
					 *
1370
					 * @since 2.0.0
1371
					 */
1372
					do_action( 'infinite_scroll_render' );
1373
1374
					$results['html'] = ob_get_clean();
1375
					remove_action( 'infinite_scroll_render', $callback );
1376
				}
1377
				if ( ! empty( $results['html'] ) ) {
1378
					break;
1379
				}
1380
			}
1381
1382
			// If primary and fallback rendering methods fail, prevent further IS rendering attempts. Otherwise, wrap the output if requested.
1383
			if ( empty( $results['html'] ) ) {
1384
				unset( $results['html'] );
1385
				/**
1386
				 * Fires when Infinite Scoll doesn't render any posts.
1387
				 *
1388
				 * @module infinite-scroll
1389
				 *
1390
				 * @since 2.0.0
1391
				 */
1392
				do_action( 'infinite_scroll_empty' );
1393
				$results['type'] = 'empty';
1394
			} elseif ( $this->has_wrapper() ) {
1395
				$wrapper_classes = is_string( self::get_settings()->wrapper ) ? self::get_settings()->wrapper : 'infinite-wrap';
1396
				$wrapper_classes .= ' infinite-view-' . $page;
1397
				$wrapper_classes = trim( $wrapper_classes );
1398
				$aria_label = sprintf(
1399
					/* translators: %1$s is the page count */
1400
					__( 'Page: %1$d.', 'jetpack' ),
1401
					$page
1402
				);
1403
1404
				$results['html'] = '<div class="' . esc_attr( $wrapper_classes ) . '" id="infinite-view-' . $page . '" data-page-num="' . $page . '" role="region" aria-label="' . esc_attr( $aria_label ) . '">' . $results['html'] . '</div>';
1405
			}
1406
1407
			// Fire wp_footer to ensure that all necessary scripts are enqueued. Output isn't used, but scripts are extracted in self::action_wp_footer.
1408
			ob_start();
1409
			wp_footer();
1410
			while ( ob_get_length() ) {
1411
				ob_end_clean();
1412
			}
1413
1414
			if ( 'success' == $results['type'] ) {
1415
				global $currentday;
1416
				$results['lastbatch'] = self::is_last_batch();
1417
				$results['currentday'] = $currentday;
1418
			}
1419
1420
			// Loop through posts to capture sharing data for new posts loaded via Infinite Scroll
1421
			if ( 'success' == $results['type'] && function_exists( 'sharing_register_post_for_share_counts' ) ) {
1422
				global $jetpack_sharing_counts;
1423
1424
				while( have_posts() ) {
1425
					the_post();
1426
1427
					sharing_register_post_for_share_counts( get_the_ID() );
1428
				}
1429
1430
				$results['postflair'] = array_flip( $jetpack_sharing_counts );
1431
			}
1432
		} else {
1433
			/** This action is already documented in modules/infinite-scroll/infinity.php */
1434
			do_action( 'infinite_scroll_empty' );
1435
			$results['type'] = 'empty';
1436
		}
1437
1438
		wp_send_json(
1439
			/**
1440
			 * Filter the Infinite Scroll results.
1441
			 *
1442
			 * @module infinite-scroll
1443
			 *
1444
			 * @since 2.0.0
1445
			 *
1446
			 * @param array $results Array of Infinite Scroll results.
1447
			 * @param array $query_args Array of main query arguments.
1448
			 * @param WP_Query $wp_query WP Query.
1449
			 */
1450
			apply_filters( 'infinite_scroll_results', $results, $query_args, self::wp_query() )
0 ignored issues
show
Unused Code introduced by
The call to apply_filters() has too many arguments starting with $query_args.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
1451
		);
1452
	}
1453
1454
	/**
1455
	 * Update the $allowed_vars array with the standard WP public and private
1456
	 * query vars, as well as taxonomy vars
1457
	 *
1458
	 * @global $wp
1459
	 * @param array $allowed_vars
1460
	 * @filter infinite_scroll_allowed_vars
1461
	 * @return array
1462
	 */
1463
	function allowed_query_vars( $allowed_vars ) {
1464
		global $wp;
1465
1466
		$allowed_vars += $wp->public_query_vars;
1467
		$allowed_vars += $wp->private_query_vars;
1468
		$allowed_vars += $this->get_taxonomy_vars();
1469
1470
		foreach ( array_keys( $allowed_vars, 'paged' ) as $key ) {
1471
			unset( $allowed_vars[ $key ] );
1472
		}
1473
1474
		return array_unique( $allowed_vars );
1475
	}
1476
1477
	/**
1478
	 * Returns an array of stock and custom taxonomy query vars
1479
	 *
1480
	 * @global $wp_taxonomies
1481
	 * @return array
1482
	 */
1483
	function get_taxonomy_vars() {
1484
		global $wp_taxonomies;
1485
1486
		$taxonomy_vars = array();
1487
		foreach ( $wp_taxonomies as $taxonomy => $t ) {
1488
			if ( $t->query_var )
1489
				$taxonomy_vars[] = $t->query_var;
1490
		}
1491
1492
		// still needed?
1493
		$taxonomy_vars[] = 'tag_id';
1494
1495
		return $taxonomy_vars;
1496
	}
1497
1498
	/**
1499
	 * Update the $query_args array with the parameters provided via AJAX/GET.
1500
	 *
1501
	 * @param array $query_args
1502
	 * @filter infinite_scroll_query_args
1503
	 * @return array
1504
	 */
1505
	function inject_query_args( $query_args ) {
1506
		/**
1507
		 * Filter the array of allowed Infinite Scroll query arguments.
1508
		 *
1509
		 * @module infinite-scroll
1510
		 *
1511
		 * @since 2.6.0
1512
		 *
1513
		 * @param array $args Array of allowed Infinite Scroll query arguments.
1514
		 * @param array $query_args Array of query arguments.
1515
		 */
1516
		$allowed_vars = apply_filters( 'infinite_scroll_allowed_vars', array(), $query_args );
0 ignored issues
show
Unused Code introduced by
The call to apply_filters() has too many arguments starting with $query_args.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
1517
1518
		$query_args = array_merge( $query_args, array(
1519
			'suppress_filters' => false,
1520
		) );
1521
1522
		if ( is_array( $_REQUEST[ 'query_args' ] ) ) {
1523
			foreach ( $_REQUEST[ 'query_args' ] as $var => $value ) {
1524
				if ( in_array( $var, $allowed_vars ) && ! empty( $value ) )
1525
					$query_args[ $var ] = $value;
1526
			}
1527
		}
1528
1529
		return $query_args;
1530
	}
1531
1532
	/**
1533
	 * Rendering fallback used when themes don't specify their own handler.
1534
	 *
1535
	 * @uses have_posts, the_post, get_template_part, get_post_format
1536
	 * @action infinite_scroll_render
1537
	 * @return string
1538
	 */
1539
	function render() {
1540
		while ( have_posts() ) {
1541
			the_post();
1542
1543
			get_template_part( 'content', get_post_format() );
1544
		}
1545
	}
1546
1547
	/**
1548
	 * Allow plugins to filter what archives Infinite Scroll supports
1549
	 *
1550
	 * @uses current_theme_supports, is_home, is_archive, apply_filters, self::get_settings
1551
	 * @return bool
1552
	 */
1553
	public static function archive_supports_infinity() {
1554
		$supported = current_theme_supports( 'infinite-scroll' ) && ( is_home() || is_archive() || is_search() );
1555
1556
		// Disable when previewing a non-active theme in the customizer
1557
		if ( is_customize_preview() && ! $GLOBALS['wp_customize']->is_theme_active() ) {
1558
			return false;
1559
		}
1560
1561
		/**
1562
		 * Allow plugins to filter what archives Infinite Scroll supports.
1563
		 *
1564
		 * @module infinite-scroll
1565
		 *
1566
		 * @since 2.0.0
1567
		 *
1568
		 * @param bool $supported Does the Archive page support Infinite Scroll.
1569
		 * @param object self::get_settings() IS settings provided by theme.
1570
		 */
1571
		return (bool) apply_filters( 'infinite_scroll_archive_supported', $supported, self::get_settings() );
0 ignored issues
show
Unused Code introduced by
The call to apply_filters() has too many arguments starting with self::get_settings().

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
1572
	}
1573
1574
	/**
1575
	 * The Infinite Blog Footer
1576
	 *
1577
	 * @uses self::get_settings, self::archive_supports_infinity, self::default_footer
1578
	 * @return string or null
1579
	 */
1580
	function footer() {
1581
		// Bail if theme requested footer not show
1582
		if ( false == self::get_settings()->footer )
1583
			return;
1584
1585
		// We only need the new footer for the 'scroll' type
1586
		if ( 'scroll' != self::get_settings()->type || ! self::archive_supports_infinity() )
1587
			return;
1588
1589
		if ( self::is_last_batch() ) {
1590
			return;
1591
		}
1592
1593
		// Display a footer, either user-specified or a default
1594
		if ( false !== self::get_settings()->footer_callback && is_callable( self::get_settings()->footer_callback ) )
1595
			call_user_func( self::get_settings()->footer_callback, self::get_settings() );
1596
		else
1597
			self::default_footer();
1598
	}
1599
1600
	/**
1601
	 * Render default IS footer
1602
	 *
1603
	 * @uses __, wp_get_theme, apply_filters, home_url, esc_attr, get_bloginfo, bloginfo
1604
	 * @return string
1605
	 *
1606
	 */
1607
	private function default_footer() {
1608
		if ( '' !== get_privacy_policy_url() ) {
1609
			$credits = get_the_privacy_policy_link() . '<span role="separator" aria-hidden="true"> / </span>';
1610
		} else {
1611
			$credits = '';
1612
		}
1613
		$credits .= sprintf(
1614
			'<a href="https://wordpress.org/" rel="noopener noreferrer" target="_blank" rel="generator">%1$s</a> ',
1615
			__( 'Proudly powered by WordPress', 'jetpack' )
1616
		);
1617
		$credits .= sprintf(
1618
			/* translators: %1$s is the name of a theme */
1619
			__( 'Theme: %1$s.', 'jetpack' ),
1620
			wp_get_theme()->Name
1621
		);
1622
		/**
1623
		 * Filter Infinite Scroll's credit text.
1624
		 *
1625
		 * @module infinite-scroll
1626
		 *
1627
		 * @since 2.0.0
1628
		 *
1629
		 * @param string $credits Infinite Scroll credits.
1630
		 */
1631
		$credits = apply_filters( 'infinite_scroll_credit', $credits );
1632
1633
		?>
1634
		<div id="infinite-footer">
1635
			<div class="container">
1636
				<div class="blog-info">
1637
					<a id="infinity-blog-title" href="<?php echo home_url( '/' ); ?>" rel="home">
1638
						<?php bloginfo( 'name' ); ?>
1639
					</a>
1640
				</div>
1641
				<div class="blog-credits">
1642
					<?php echo $credits; ?>
1643
				</div>
1644
			</div>
1645
		</div><!-- #infinite-footer -->
1646
		<?php
1647
	}
1648
1649
	/**
1650
	 * Ensure that IS doesn't interfere with Grunion by stripping IS query arguments from the Grunion redirect URL.
1651
	 * When arguments are present, Grunion redirects to the IS AJAX endpoint.
1652
	 *
1653
	 * @param string $url
1654
	 * @uses remove_query_arg
1655
	 * @filter grunion_contact_form_redirect_url
1656
	 * @return string
1657
	 */
1658
	public function filter_grunion_redirect_url( $url ) {
1659
		// Remove IS query args, if present
1660
		if ( false !== strpos( $url, 'infinity=scrolling' ) ) {
1661
			$url = remove_query_arg( array(
1662
				'infinity',
1663
				'action',
1664
				'page',
1665
				'order',
1666
				'scripts',
1667
				'styles'
1668
			), $url );
1669
		}
1670
1671
		return $url;
1672
	}
1673
1674
	/**
1675
	 * When the MediaElement is loaded in dynamically, we need to enforce that
1676
	 * its settings are added to the page as well.
1677
	 *
1678
	 * @param array $scripts_data New scripts exposed to the infinite scroll.
1679
	 *
1680
	 * @since 8.4.0
1681
	 */
1682
	public function add_mejs_config( $scripts_data ) {
1683
		foreach ( $scripts_data as $key => $data ) {
1684
			if ( 'mediaelement-core' === $data['handle'] ) {
1685
				$mejs_settings = array(
1686
					'pluginPath'  => includes_url( 'js/mediaelement/', 'relative' ),
1687
					'classPrefix' => 'mejs-',
1688
					'stretching'  => 'responsive',
1689
				);
1690
1691
				$scripts_data[ $key ]['extra_data'] = sprintf(
1692
					'window.%s = %s',
1693
					'_wpmejsSettings',
1694
					wp_json_encode( apply_filters( 'mejs_settings', $mejs_settings ) )
1695
				);
1696
			}
1697
		}
1698
		return $scripts_data;
1699
	}
1700
};
1701
1702
/**
1703
 * Initialize The_Neverending_Home_Page
1704
 */
1705
function the_neverending_home_page_init() {
1706
	if ( ! current_theme_supports( 'infinite-scroll' ) )
1707
		return;
1708
1709
	new The_Neverending_Home_Page();
1710
}
1711
add_action( 'init', 'the_neverending_home_page_init', 20 );
1712
1713
/**
1714
 * Check whether the current theme is infinite-scroll aware.
1715
 * If so, include the files which add theme support.
1716
 */
1717
function the_neverending_home_page_theme_support() {
1718
	if (
1719
			defined( 'IS_WPCOM' ) && IS_WPCOM &&
1720
			defined( 'REST_API_REQUEST' ) && REST_API_REQUEST &&
1721
			! doing_action( 'restapi_theme_after_setup_theme' )
1722
	) {
1723
		// Don't source theme compat files until we're in the site's context
1724
		return;
1725
	}
1726
	$theme_name = get_stylesheet();
1727
1728
	/**
1729
	 * Filter the path to the Infinite Scroll compatibility file.
1730
	 *
1731
	 * @module infinite-scroll
1732
	 *
1733
	 * @since 2.0.0
1734
	 *
1735
	 * @param string $str IS compatibility file path.
1736
	 * @param string $theme_name Theme name.
1737
	 */
1738
	$customization_file = apply_filters( 'infinite_scroll_customization_file', dirname( __FILE__ ) . "/themes/{$theme_name}.php", $theme_name );
0 ignored issues
show
Unused Code introduced by
The call to apply_filters() has too many arguments starting with $theme_name.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
1739
1740
	if ( is_readable( $customization_file ) )
1741
		require_once( $customization_file );
1742
}
1743
add_action( 'after_setup_theme', 'the_neverending_home_page_theme_support', 5 );
1744
1745
/**
1746
 * Early accommodation of the Infinite Scroll AJAX request
1747
 */
1748
if ( The_Neverending_Home_Page::got_infinity() ) {
1749
	/**
1750
	 * If we're sure this is an AJAX request (i.e. the HTTP_X_REQUESTED_WITH header says so),
1751
	 * indicate it as early as possible for actions like init
1752
	 */
1753
	if ( ! defined( 'DOING_AJAX' ) &&
1754
		isset( $_SERVER['HTTP_X_REQUESTED_WITH'] ) &&
1755
		strtoupper( $_SERVER['HTTP_X_REQUESTED_WITH'] ) == 'XMLHTTPREQUEST'
1756
	) {
1757
		define( 'DOING_AJAX', true );
1758
	}
1759
1760
	// Don't load the admin bar when doing the AJAX response.
1761
	show_admin_bar( false );
1762
}
1763