Completed
Push — add/google-translate-widget ( aadaa8...af6d12 )
by
unknown
30:44 queued 19:47
created

Jetpack_RelatedPosts::_get_related_post_ids()   D

Complexity

Conditions 19
Paths 66

Size

Total Lines 94
Code Lines 54

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 19
eloc 54
nc 66
nop 3
dl 0
loc 94
rs 4.764
c 0
b 0
f 0

How to fix   Long Method    Complexity   

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
class Jetpack_RelatedPosts {
3
	const VERSION = '20150408';
4
	const SHORTCODE = 'jetpack-related-posts';
5
6
	/**
7
	 * Creates and returns a static instance of Jetpack_RelatedPosts.
8
	 *
9
	 * @return Jetpack_RelatedPosts
10
	 */
11 View Code Duplication
	public static function init() {
12
		static $instance = NULL;
13
14
		if ( ! $instance ) {
15
			if ( class_exists('WPCOM_RelatedPosts') && method_exists( 'WPCOM_RelatedPosts', 'init' ) ) {
16
				$instance = WPCOM_RelatedPosts::init();
17
			} else {
18
				$instance = new Jetpack_RelatedPosts(
19
					get_current_blog_id(),
20
					Jetpack_Options::get_option( 'id' )
21
				);
22
			}
23
		}
24
25
		return $instance;
26
	}
27
28
	/**
29
	 * Creates and returns a static instance of Jetpack_RelatedPosts_Raw.
30
	 *
31
	 * @return Jetpack_RelatedPosts
32
	 */
33 View Code Duplication
	public static function init_raw() {
34
		static $instance = NULL;
35
36
		if ( ! $instance ) {
37
			if ( class_exists('WPCOM_RelatedPosts') && method_exists( 'WPCOM_RelatedPosts', 'init_raw' ) ) {
38
				$instance = WPCOM_RelatedPosts::init_raw();
39
			} else {
40
				$instance = new Jetpack_RelatedPosts_Raw(
41
					get_current_blog_id(),
42
					Jetpack_Options::get_option( 'id' )
43
				);
44
			}
45
		}
46
47
		return $instance;
48
	}
49
50
	protected $_blog_id_local;
51
	protected $_blog_id_wpcom;
52
	protected $_options;
53
	protected $_allow_feature_toggle;
54
	protected $_blog_charset;
55
	protected $_convert_charset;
56
	protected $_previous_post_id;
57
	protected $_found_shortcode = false;
58
59
	/**
60
	 * Constructor for Jetpack_RelatedPosts.
61
	 *
62
	 * @param int $blog_id_local
63
	 * @param int $blog_id_wpcom
64
	 * @uses get_option, add_action, apply_filters
65
	 * @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...
66
	 */
67
	public function __construct( $blog_id_local, $blog_id_wpcom ) {
68
		$this->_blog_id_local = $blog_id_local;
69
		$this->_blog_id_wpcom = $blog_id_wpcom;
70
		$this->_blog_charset = get_option( 'blog_charset' );
71
		$this->_convert_charset = ( function_exists( 'iconv' ) && ! preg_match( '/^utf\-?8$/i', $this->_blog_charset ) );
72
73
		add_action( 'admin_init', array( $this, 'action_admin_init' ) );
74
		add_action( 'wp', array( $this, 'action_frontend_init' ) );
75
76
		if ( ! class_exists( 'Jetpack_Media_Summary' ) ) {
77
			jetpack_require_lib( 'class.media-summary' );
78
		}
79
80
		// Add Related Posts to the REST API Post response.
81
		if ( function_exists( 'register_rest_field' ) ) {
82
			add_action( 'rest_api_init',  array( $this, 'rest_register_related_posts' ) );
83
		}
84
	}
85
86
	/**
87
	 * =================
88
	 * ACTIONS & FILTERS
89
	 * =================
90
	 */
91
92
	/**
93
	 * Add a checkbox field to Settings > Reading for enabling related posts.
94
	 *
95
	 * @action admin_init
96
	 * @uses add_settings_field, __, register_setting, add_action
97
	 * @return null
98
	 */
99
	public function action_admin_init() {
100
101
		// Add the setting field [jetpack_relatedposts] and place it in Settings > Reading
102
		add_settings_field( 'jetpack_relatedposts', '<span id="jetpack_relatedposts">' . __( 'Related posts', 'jetpack' ) . '</span>', array( $this, 'print_setting_html' ), 'reading' );
103
		register_setting( 'reading', 'jetpack_relatedposts', array( $this, 'parse_options' ) );
104
		add_action('admin_head', array( $this, 'print_setting_head' ) );
105
106
		if( 'options-reading.php' == $GLOBALS['pagenow'] ) {
107
			// Enqueue style for live preview on the reading settings page
108
			$this->_enqueue_assets( false, true );
109
		}
110
	}
111
112
	/**
113
	 * Load related posts assets if it's a elegiable front end page or execute search and return JSON if it's an endpoint request.
114
	 *
115
	 * @global $_GET
116
	 * @action wp
117
	 * @uses add_shortcode, get_the_ID
118
	 * @returns null
119
	 */
120
	public function action_frontend_init() {
121
		// Add a shortcode handler that outputs nothing, this gets overridden later if we can display related content
122
		add_shortcode( self::SHORTCODE, array( $this, 'get_target_html_unsupported' ) );
123
124
		if ( ! $this->_enabled_for_request() )
125
			return;
126
127
		if ( isset( $_GET['relatedposts'] ) ) {
128
			$excludes = array();
129
			if ( isset( $_GET['relatedposts_exclude'] ) ) {
130
				$excludes = explode( ',', $_GET['relatedposts_exclude'] );
131
			}
132
133
			$this->_action_frontend_init_ajax( $excludes );
134
		} else {
135
			if ( isset( $_GET['relatedposts_hit'], $_GET['relatedposts_origin'], $_GET['relatedposts_position'] ) ) {
136
				$this->_log_click( $_GET['relatedposts_origin'], get_the_ID(), $_GET['relatedposts_position'] );
137
				$this->_previous_post_id = (int) $_GET['relatedposts_origin'];
138
			}
139
140
			$this->_action_frontend_init_page();
141
		}
142
143
	}
144
145
	/**
146
	 * Adds a target to the post content to load related posts into if a shortcode for it did not already exist.
147
	 *
148
	 * @filter the_content
149
	 * @param string $content
150
	 * @returns string
151
	 */
152
	public function filter_add_target_to_dom( $content ) {
153
		if ( !$this->_found_shortcode ) {
154
			$content .= "\n" . $this->get_target_html();
155
		}
156
157
		return $content;
158
	}
159
160
	/**
161
	 * Looks for our shortcode on the unfiltered content, this has to execute early.
162
	 *
163
	 * @filter the_content
164
	 * @param string $content
165
	 * @uses has_shortcode
166
	 * @returns string
167
	 */
168
	public function test_for_shortcode( $content ) {
169
		$this->_found_shortcode = has_shortcode( $content, self::SHORTCODE );
170
171
		return $content;
172
	}
173
174
	/**
175
	 * Returns the HTML for the related posts section.
176
	 *
177
	 * @uses esc_html__, apply_filters
178
	 * @returns string
179
	 */
180
	public function get_target_html() {
181
		if ( Jetpack_Sync_Settings::is_syncing() ) {
182
			return '';
183
		}
184
185
		$options = $this->get_options();
186
187
		if ( $options['show_headline'] ) {
188
			$headline = sprintf(
189
				'<h3 class="jp-relatedposts-headline"><em>%s</em></h3>',
190
				esc_html__( 'Related', 'jetpack' )
191
			);
192
		} else {
193
			$headline = '';
194
		}
195
196
		/**
197
		 * Filter the Related Posts headline.
198
		 *
199
		 * @module related-posts
200
		 *
201
		 * @since 3.0.0
202
		 *
203
		 * @param string $headline Related Posts heading.
204
		 */
205
		$headline = apply_filters( 'jetpack_relatedposts_filter_headline', $headline );
206
207
		if ( $this->_previous_post_id ) {
208
			$exclude = "data-exclude='{$this->_previous_post_id}'";
209
		} else {
210
			$exclude = "";
211
		}
212
213
		return <<<EOT
214
<div id='jp-relatedposts' class='jp-relatedposts' $exclude>
215
	$headline
216
</div>
217
EOT;
218
	}
219
220
	/**
221
	 * Returns the HTML for the related posts section if it's running in the loop or other instances where we don't support related posts.
222
	 *
223
	 * @returns string
224
	 */
225
	public function get_target_html_unsupported() {
226
		if ( Jetpack_Sync_Settings::is_syncing() ) {
227
			return '';
228
		}
229
		return "\n\n<!-- Jetpack Related Posts is not supported in this context. -->\n\n";
230
	}
231
232
	/**
233
	 * ========================
234
	 * PUBLIC UTILITY FUNCTIONS
235
	 * ========================
236
	 */
237
238
	/**
239
	 * Gets options set for Jetpack_RelatedPosts and merge with defaults.
240
	 *
241
	 * @uses Jetpack_Options::get_option, apply_filters
242
	 * @return array
243
	 */
244
	public function get_options() {
245
		if ( null === $this->_options ) {
246
			$this->_options = Jetpack_Options::get_option( 'relatedposts', array() );
247
			if ( ! is_array( $this->_options ) )
248
				$this->_options = array();
249
			if ( ! isset( $this->_options['enabled'] ) )
250
				$this->_options['enabled'] = true;
251
			if ( ! isset( $this->_options['show_headline'] ) )
252
				$this->_options['show_headline'] = true;
253
			if ( ! isset( $this->_options['show_thumbnails'] ) )
254
				$this->_options['show_thumbnails'] = false;
255
			if ( empty( $this->_options['size'] ) || (int)$this->_options['size'] < 1 )
256
				$this->_options['size'] = 3;
257
258
			/**
259
			 * Filter Related Posts basic options.
260
			 *
261
			 * @module related-posts
262
			 *
263
			 * @since 2.8.0
264
			 *
265
			 * @param array $this->_options Array of basic Related Posts options.
266
			 */
267
			$this->_options = apply_filters( 'jetpack_relatedposts_filter_options', $this->_options );
268
		}
269
270
		return $this->_options;
271
	}
272
273
	/**
274
	 * Parses input and returnes normalized options array.
275
	 *
276
	 * @param array $input
277
	 * @uses self::get_options
278
	 * @return array
279
	 */
280
	public function parse_options( $input ) {
281
		$current = $this->get_options();
282
283
		if ( !is_array( $input ) )
284
			$input = array();
285
286
		if ( isset( $input['enabled'] ) && '1' == $input['enabled'] ) {
287
			$current['enabled'] = true;
288
			$current['show_headline'] = ( isset( $input['show_headline'] ) && '1' == $input['show_headline'] );
289
			$current['show_thumbnails'] = ( isset( $input['show_thumbnails'] ) && '1' == $input['show_thumbnails'] );
290
		} else {
291
			$current['enabled'] = false;
292
		}
293
294
		if ( isset( $input['size'] ) && (int)$input['size'] > 0 )
295
			$current['size'] = (int)$input['size'];
296
		else
297
			$current['size'] = null;
298
299
		return $current;
300
	}
301
302
	/**
303
	 * HTML for admin settings page.
304
	 *
305
	 * @uses self::get_options, checked, esc_html__
306
	 * @returns null
307
	 */
308
	public function print_setting_html() {
309
		$options = $this->get_options();
310
311
		$ui_settings_template = <<<EOT
312
<ul id="settings-reading-relatedposts-customize">
313
	<li>
314
		<label><input name="jetpack_relatedposts[show_headline]" type="checkbox" value="1" %s /> %s</label>
315
	</li>
316
	<li>
317
		<label><input name="jetpack_relatedposts[show_thumbnails]" type="checkbox" value="1" %s /> %s</label>
318
	</li>
319
</ul>
320
<div id='settings-reading-relatedposts-preview'>
321
	%s
322
	<div id="jp-relatedposts" class="jp-relatedposts"></div>
323
</div>
324
EOT;
325
		$ui_settings = sprintf(
326
			$ui_settings_template,
327
			checked( $options['show_headline'], true, false ),
328
			esc_html__( 'Show a "Related" header to more clearly separate the related section from posts', 'jetpack' ),
329
			checked( $options['show_thumbnails'], true, false ),
330
			esc_html__( 'Use a large and visually striking layout', 'jetpack' ),
331
			esc_html__( 'Preview:', 'jetpack' )
332
		);
333
334
		if ( !$this->_allow_feature_toggle() ) {
335
			$template = <<<EOT
336
<input type="hidden" name="jetpack_relatedposts[enabled]" value="1" />
337
%s
338
EOT;
339
			printf(
340
				$template,
341
				$ui_settings
342
			);
343
		} else {
344
			$template = <<<EOT
345
<ul id="settings-reading-relatedposts">
346
	<li>
347
		<label><input type="radio" name="jetpack_relatedposts[enabled]" value="0" class="tog" %s /> %s</label>
348
	</li>
349
	<li>
350
		<label><input type="radio" name="jetpack_relatedposts[enabled]" value="1" class="tog" %s /> %s</label>
351
		%s
352
	</li>
353
</ul>
354
EOT;
355
			printf(
356
				$template,
357
				checked( $options['enabled'], false, false ),
358
				esc_html__( 'Hide related content after posts', 'jetpack' ),
359
				checked( $options['enabled'], true, false ),
360
				esc_html__( 'Show related content after posts', 'jetpack' ),
361
				$ui_settings
362
			);
363
		}
364
	}
365
366
	/**
367
	 * Head JS/CSS for admin settings page.
368
	 *
369
	 * @uses esc_html__
370
	 * @returns null
371
	 */
372
	public function print_setting_head() {
373
374
		// only dislay the Related Posts JavaScript on the Reading Settings Admin Page
375
		$current_screen =  get_current_screen();
376
377
		if ( is_null( $current_screen ) ) {
378
			return;
379
		}
380
381
		if( 'options-reading' != $current_screen->id )
382
			return;
383
384
		$related_headline = sprintf(
385
			'<h3 class="jp-relatedposts-headline"><em>%s</em></h3>',
386
			esc_html__( 'Related', 'jetpack' )
387
		);
388
389
		$href_params = 'class="jp-relatedposts-post-a" href="#jetpack_relatedposts" rel="nofollow" data-origin="0" data-position="0"';
390
		$related_with_images = <<<EOT
391
<div class="jp-relatedposts-items jp-relatedposts-items-visual">
392
	<div class="jp-relatedposts-post jp-relatedposts-post0 jp-relatedposts-post-thumbs" data-post-id="0" data-post-format="image">
393
		<a $href_params>
394
			<img class="jp-relatedposts-post-img" src="http://jetpackme.files.wordpress.com/2014/08/1-wpios-ipad-3-1-viewsite.png?w=350&amp;h=200&amp;crop=1" width="350" alt="Big iPhone/iPad Update Now Available" scale="0">
395
		</a>
396
		<h4 class="jp-relatedposts-post-title">
397
			<a $href_params>Big iPhone/iPad Update Now Available</a>
398
		</h4>
399
		<p class="jp-relatedposts-post-excerpt">Big iPhone/iPad Update Now Available</p>
400
		<p class="jp-relatedposts-post-context">In "Mobile"</p>
401
	</div>
402
	<div class="jp-relatedposts-post jp-relatedposts-post1 jp-relatedposts-post-thumbs" data-post-id="0" data-post-format="image">
403
		<a $href_params>
404
			<img class="jp-relatedposts-post-img" src="http://jetpackme.files.wordpress.com/2014/08/wordpress-com-news-wordpress-for-android-ui-update2.jpg?w=350&amp;h=200&amp;crop=1" width="350" alt="The WordPress for Android App Gets a Big Facelift" scale="0">
405
		</a>
406
		<h4 class="jp-relatedposts-post-title">
407
			<a $href_params>The WordPress for Android App Gets a Big Facelift</a>
408
		</h4>
409
		<p class="jp-relatedposts-post-excerpt">The WordPress for Android App Gets a Big Facelift</p>
410
		<p class="jp-relatedposts-post-context">In "Mobile"</p>
411
	</div>
412
	<div class="jp-relatedposts-post jp-relatedposts-post2 jp-relatedposts-post-thumbs" data-post-id="0" data-post-format="image">
413
		<a $href_params>
414
			<img class="jp-relatedposts-post-img" src="http://jetpackme.files.wordpress.com/2014/08/videopresswedding.jpg?w=350&amp;h=200&amp;crop=1" width="350" alt="Upgrade Focus: VideoPress For Weddings" scale="0">
415
		</a>
416
		<h4 class="jp-relatedposts-post-title">
417
			<a $href_params>Upgrade Focus: VideoPress For Weddings</a>
418
		</h4>
419
		<p class="jp-relatedposts-post-excerpt">Upgrade Focus: VideoPress For Weddings</p>
420
		<p class="jp-relatedposts-post-context">In "Upgrade"</p>
421
	</div>
422
</div>
423
EOT;
424
		$related_with_images = str_replace( "\n", '', $related_with_images );
425
		$related_without_images = <<<EOT
426
<div class="jp-relatedposts-items jp-relatedposts-items-minimal">
427
	<p class="jp-relatedposts-post jp-relatedposts-post0" data-post-id="0" data-post-format="image">
428
		<span class="jp-relatedposts-post-title"><a $href_params>Big iPhone/iPad Update Now Available</a></span>
429
		<span class="jp-relatedposts-post-context">In "Mobile"</span>
430
	</p>
431
	<p class="jp-relatedposts-post jp-relatedposts-post1" data-post-id="0" data-post-format="image">
432
		<span class="jp-relatedposts-post-title"><a $href_params>The WordPress for Android App Gets a Big Facelift</a></span>
433
		<span class="jp-relatedposts-post-context">In "Mobile"</span>
434
	</p>
435
	<p class="jp-relatedposts-post jp-relatedposts-post2" data-post-id="0" data-post-format="image">
436
		<span class="jp-relatedposts-post-title"><a $href_params>Upgrade Focus: VideoPress For Weddings</a></span>
437
		<span class="jp-relatedposts-post-context">In "Upgrade"</span>
438
	</p>
439
</div>
440
EOT;
441
		$related_without_images = str_replace( "\n", '', $related_without_images );
442
443
		if ( $this->_allow_feature_toggle() ) {
444
			$extra_css = '#settings-reading-relatedposts-customize { padding-left:2em; margin-top:.5em; }';
445
		} else {
446
			$extra_css = '';
447
		}
448
449
		echo <<<EOT
450
<style type="text/css">
451
	#settings-reading-relatedposts .disabled { opacity:.5; filter:Alpha(opacity=50); }
452
	#settings-reading-relatedposts-preview .jp-relatedposts { background:#fff; padding:.5em; width:75%; }
453
	$extra_css
454
</style>
455
<script type="text/javascript">
456
	jQuery( document ).ready( function($) {
457
		var update_ui = function() {
458
			var is_enabled = true;
459
			if ( 'radio' == $( 'input[name="jetpack_relatedposts[enabled]"]' ).attr('type') ) {
460
				if ( '0' == $( 'input[name="jetpack_relatedposts[enabled]"]:checked' ).val() ) {
461
					is_enabled = false;
462
				}
463
			}
464
			if ( is_enabled ) {
465
				$( '#settings-reading-relatedposts-customize' )
466
					.removeClass( 'disabled' )
467
					.find( 'input' )
468
					.attr( 'disabled', false );
469
				$( '#settings-reading-relatedposts-preview' )
470
					.removeClass( 'disabled' );
471
			} else {
472
				$( '#settings-reading-relatedposts-customize' )
473
					.addClass( 'disabled' )
474
					.find( 'input' )
475
					.attr( 'disabled', true );
476
				$( '#settings-reading-relatedposts-preview' )
477
					.addClass( 'disabled' );
478
			}
479
		};
480
481
		var update_preview = function() {
482
			var html = '';
483
			if ( $( 'input[name="jetpack_relatedposts[show_headline]"]:checked' ).length ) {
484
				html += '$related_headline';
485
			}
486
			if ( $( 'input[name="jetpack_relatedposts[show_thumbnails]"]:checked' ).length ) {
487
				html += '$related_with_images';
488
			} else {
489
				html += '$related_without_images';
490
			}
491
			$( '#settings-reading-relatedposts-preview .jp-relatedposts' )
492
				.html( html )
493
				.show();
494
		};
495
496
		// Update on load
497
		update_preview();
498
		update_ui();
499
500
		// Update on change
501
		$( '#settings-reading-relatedposts-customize input' )
502
			.change( update_preview );
503
		$( '#settings-reading-relatedposts' )
504
			.find( 'input.tog' )
505
			.change( update_ui );
506
	});
507
</script>
508
EOT;
509
	}
510
511
	/**
512
	 * Gets an array of related posts that match the given post_id.
513
	 *
514
	 * @param int $post_id
515
	 * @param array $args - params to use when building ElasticSearch filters to narrow down the search domain.
516
	 * @uses self::get_options, get_post_type, wp_parse_args, apply_filters
517
	 * @return array
518
	 */
519
	public function get_for_post_id( $post_id, array $args ) {
520
		$options = $this->get_options();
521
522
		if ( ! empty( $args['size'] ) )
523
			$options['size'] = $args['size'];
524
525
		if ( ! $options['enabled'] || 0 == (int)$post_id || empty( $options['size'] ) )
526
			return array();
527
528
		$defaults = array(
529
			'size' => (int)$options['size'],
530
			'post_type' => get_post_type( $post_id ),
531
			'post_formats' => array(),
532
			'has_terms' => array(),
533
			'date_range' => array(),
534
			'exclude_post_ids' => array(),
535
		);
536
		$args = wp_parse_args( $args, $defaults );
537
		/**
538
		 * Filter the arguments used to retrieve a list of Related Posts.
539
		 *
540
		 * @module related-posts
541
		 *
542
		 * @since 2.8.0
543
		 *
544
		 * @param array $args Array of options to retrieve Related Posts.
545
		 * @param string $post_id Post ID of the post for which we are retrieving Related Posts.
546
		 */
547
		$args = apply_filters( 'jetpack_relatedposts_filter_args', $args, $post_id );
548
549
		$filters = $this->_get_es_filters_from_args( $post_id, $args );
550
		/**
551
		 * Filter ElasticSearch options used to calculate Related Posts.
552
		 *
553
		 * @module related-posts
554
		 *
555
		 * @since 2.8.0
556
		 *
557
		 * @param array $filters Array of ElasticSearch filters based on the post_id and args.
558
		 * @param string $post_id Post ID of the post for which we are retrieving Related Posts.
559
		 */
560
		$filters = apply_filters( 'jetpack_relatedposts_filter_filters', $filters, $post_id );
561
562
		$results = $this->_get_related_posts( $post_id, $args['size'], $filters );
563
		/**
564
		 * Filter the array of related posts matched by ElasticSearch.
565
		 *
566
		 * @module related-posts
567
		 *
568
		 * @since 2.8.0
569
		 *
570
		 * @param array $results Array of related posts matched by ElasticSearch.
571
		 * @param string $post_id Post ID of the post for which we are retrieving Related Posts.
572
		 */
573
		return apply_filters( 'jetpack_relatedposts_returned_results', $results, $post_id );
574
	}
575
576
	/**
577
	 * =========================
578
	 * PRIVATE UTILITY FUNCTIONS
579
	 * =========================
580
	 */
581
582
	/**
583
	 * Creates an array of ElasticSearch filters based on the post_id and args.
584
	 *
585
	 * @param int $post_id
586
	 * @param array $args
587
	 * @uses apply_filters, get_post_types, get_post_format_strings
588
	 * @return array
589
	 */
590
	protected function _get_es_filters_from_args( $post_id, array $args ) {
591
		$filters = array();
592
593
		/**
594
		 * Filter the terms used to search for Related Posts.
595
		 *
596
		 * @module related-posts
597
		 *
598
		 * @since 2.8.0
599
		 *
600
		 * @param array $args['has_terms'] Array of terms associated to the Related Posts.
601
		 * @param string $post_id Post ID of the post for which we are retrieving Related Posts.
602
		 */
603
		$args['has_terms'] = apply_filters( 'jetpack_relatedposts_filter_has_terms', $args['has_terms'], $post_id );
604
		if ( ! empty( $args['has_terms'] ) ) {
605
			foreach( (array)$args['has_terms'] as $term ) {
606
				if ( mb_strlen( $term->taxonomy ) ) {
607
					switch ( $term->taxonomy ) {
608
						case 'post_tag':
609
							$tax_fld = 'tag.slug';
610
							break;
611
						case 'category':
612
							$tax_fld = 'category.slug';
613
							break;
614
						default:
615
							$tax_fld = 'taxonomy.' . $term->taxonomy . '.slug';
616
							break;
617
					}
618
					$filters[] = array( 'term' => array( $tax_fld => $term->slug ) );
619
				}
620
			}
621
		}
622
623
		/**
624
		 * Filter the Post Types where we search Related Posts.
625
		 *
626
		 * @module related-posts
627
		 *
628
		 * @since 2.8.0
629
		 *
630
		 * @param array $args['post_type'] Array of Post Types.
631
		 * @param string $post_id Post ID of the post for which we are retrieving Related Posts.
632
		 */
633
		$args['post_type'] = apply_filters( 'jetpack_relatedposts_filter_post_type', $args['post_type'], $post_id );
634
		$valid_post_types = get_post_types();
635
		if ( is_array( $args['post_type'] ) ) {
636
			$sanitized_post_types = array();
637
			foreach ( $args['post_type'] as $pt ) {
638
				if ( in_array( $pt, $valid_post_types ) )
639
					$sanitized_post_types[] = $pt;
640
			}
641
			if ( ! empty( $sanitized_post_types ) )
642
				$filters[] = array( 'terms' => array( 'post_type' => $sanitized_post_types ) );
643
		} else if ( in_array( $args['post_type'], $valid_post_types ) && 'all' != $args['post_type'] ) {
644
			$filters[] = array( 'term' => array( 'post_type' => $args['post_type'] ) );
645
		}
646
647
		/**
648
		 * Filter the Post Formats where we search Related Posts.
649
		 *
650
		 * @module related-posts
651
		 *
652
		 * @since 3.3.0
653
		 *
654
		 * @param array $args['post_formats'] Array of Post Formats.
655
		 * @param string $post_id Post ID of the post for which we are retrieving Related Posts.
656
		 */
657
		$args['post_formats'] = apply_filters( 'jetpack_relatedposts_filter_post_formats', $args['post_formats'], $post_id );
658
		$valid_post_formats = get_post_format_strings();
659
		$sanitized_post_formats = array();
660
		foreach ( $args['post_formats'] as $pf ) {
661
			if ( array_key_exists( $pf, $valid_post_formats ) ) {
662
				$sanitized_post_formats[] = $pf;
663
			}
664
		}
665
		if ( ! empty( $sanitized_post_formats ) ) {
666
			$filters[] = array( 'terms' => array( 'post_format' => $sanitized_post_formats ) );
667
		}
668
669
		/**
670
		 * Filter the date range used to search Related Posts.
671
		 *
672
		 * @module related-posts
673
		 *
674
		 * @since 2.8.0
675
		 *
676
		 * @param array $args['date_range'] Array of a month interval where we search Related Posts.
677
		 * @param string $post_id Post ID of the post for which we are retrieving Related Posts.
678
		 */
679
		$args['date_range'] = apply_filters( 'jetpack_relatedposts_filter_date_range', $args['date_range'], $post_id );
680
		if ( is_array( $args['date_range'] ) && ! empty( $args['date_range'] ) ) {
681
			$args['date_range'] = array_map( 'intval', $args['date_range'] );
682
			if ( !empty( $args['date_range']['from'] ) && !empty( $args['date_range']['to'] ) ) {
683
				$filters[] = array(
684
					'range' => array(
685
						'date_gmt' => $this->_get_coalesced_range( $args['date_range'] ),
686
					)
687
				);
688
			}
689
		}
690
691
		/**
692
		 * Filter the Post IDs excluded from appearing in Related Posts.
693
		 *
694
		 * @module related-posts
695
		 *
696
		 * @since 2.9.0
697
		 *
698
		 * @param array $args['exclude_post_ids'] Array of Post IDs.
699
		 * @param string $post_id Post ID of the post for which we are retrieving Related Posts.
700
		 */
701
		$args['exclude_post_ids'] = apply_filters( 'jetpack_relatedposts_filter_exclude_post_ids', $args['exclude_post_ids'], $post_id );
702
		if ( !empty( $args['exclude_post_ids'] ) && is_array( $args['exclude_post_ids'] ) ) {
703
			foreach ( $args['exclude_post_ids'] as $exclude_post_id) {
704
				$exclude_post_id = (int)$exclude_post_id;
705
706
				if ( $exclude_post_id > 0 )
707
					$filters[] = array( 'not' => array( 'term' => array( 'post_id' => $exclude_post_id ) ) );
708
			}
709
		}
710
711
		return $filters;
712
	}
713
714
	/**
715
	 * Takes a range and coalesces it into a month interval bracketed by a time as determined by the blog_id to enhance caching.
716
	 *
717
	 * @param array $date_range
718
	 * @return array
719
	 */
720
	protected function _get_coalesced_range( array $date_range ) {
721
		$now = time();
722
		$coalesce_time = $this->_blog_id_wpcom % 86400;
723
		$current_time = $now - strtotime( 'today', $now );
724
725
		if ( $current_time < $coalesce_time && '01' == date( 'd', $now ) ) {
726
			// Move back 1 period
727
			return array(
728
				'from' => date( 'Y-m-01', strtotime( '-1 month', $date_range['from'] ) ) . ' ' . date( 'H:i:s', $coalesce_time ),
729
				'to'   => date( 'Y-m-01', $date_range['to'] ) . ' ' . date( 'H:i:s', $coalesce_time ),
730
			);
731
		} else {
732
			// Use current period
733
			return array(
734
				'from' => date( 'Y-m-01', $date_range['from'] ) . ' ' . date( 'H:i:s', $coalesce_time ),
735
				'to'   => date( 'Y-m-01', strtotime( '+1 month', $date_range['to'] ) ) . ' ' . date( 'H:i:s', $coalesce_time ),
736
			);
737
		}
738
	}
739
740
	/**
741
	 * Generate and output ajax response for related posts API call.
742
	 * NOTE: Calls exit() to end all further processing after payload has been outputed.
743
	 *
744
	 * @param array $excludes array of post_ids to exclude
745
	 * @uses send_nosniff_header, self::get_for_post_id, get_the_ID
746
	 * @return null
747
	 */
748
	protected function _action_frontend_init_ajax( array $excludes ) {
749
		define( 'DOING_AJAX', true );
750
751
		header( 'Content-type: application/json; charset=utf-8' ); // JSON can only be UTF-8
752
		send_nosniff_header();
753
754
		$related_posts = $this->get_for_post_id(
755
			get_the_ID(),
756
			array(
757
				'exclude_post_ids' => $excludes,
758
			)
759
		);
760
761
		$options = $this->get_options();
762
763
		$response = array(
764
			'version' => self::VERSION,
765
			'show_thumbnails' => (bool) $options['show_thumbnails'],
766
			'items' => array(),
767
		);
768
769
		if ( count( $related_posts ) == $options['size'] )
770
			$response['items'] = $related_posts;
771
772
		echo json_encode( $response );
773
774
		exit();
0 ignored issues
show
Coding Style Compatibility introduced by
The method _action_frontend_init_ajax() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
775
	}
776
777
	/**
778
	 * Returns a UTF-8 encoded array of post information for the given post_id
779
	 *
780
	 * @param int $post_id
781
	 * @param int $position
782
	 * @param int $origin The post id that this is related to
783
	 * @uses get_post, get_permalink, remove_query_arg, get_post_format, apply_filters
784
	 * @return array
785
	 */
786
	protected function _get_related_post_data_for_post( $post_id, $position, $origin ) {
787
		$post = get_post( $post_id );
788
789
		return array(
790
			'id' => $post->ID,
791
			'url' => get_permalink( $post->ID ),
792
			'url_meta' => array( 'origin' => $origin, 'position' => $position ),
793
			'title' => $this->_to_utf8( $this->_get_title( $post->post_title, $post->post_content ) ),
794
			'date' => get_the_date( '', $post->ID ),
795
			'format' => get_post_format( $post->ID ),
796
			'excerpt' => html_entity_decode( $this->_to_utf8( $this->_get_excerpt( $post->post_excerpt, $post->post_content ) ), ENT_QUOTES, 'UTF-8' ),
797
			/**
798
			 * Filters the rel attribute for the Related Posts' links.
799
			 *
800
			 * @module related-posts
801
			 *
802
			 * @since 3.7.0
803
			 *
804
			 * @param string nofollow Link rel attribute for Related Posts' link. Default is nofollow.
805
			 * @param int $post->ID Post ID.
806
			 */
807
			'rel' => apply_filters( 'jetpack_relatedposts_filter_post_link_rel', 'nofollow', $post->ID ),
808
			/**
809
			 * Filter the context displayed below each Related Post.
810
			 *
811
			 * @module related-posts
812
			 *
813
			 * @since 3.0.0
814
			 *
815
			 * @param string $this->_to_utf8( $this->_generate_related_post_context( $post->ID ) ) Context displayed below each related post.
816
			 * @param string $post_id Post ID of the post for which we are retrieving Related Posts.
817
			 */
818
			'context' => apply_filters(
819
				'jetpack_relatedposts_filter_post_context',
820
				$this->_to_utf8( $this->_generate_related_post_context( $post->ID ) ),
821
				$post->ID
822
			),
823
			'img' => $this->_generate_related_post_image_params( $post->ID ),
824
			/**
825
			 * Filter the post css classes added on HTML markup.
826
			 *
827
			 * @module related-posts
828
			 *
829
			 * @since 3.8.0
830
			 *
831
			 * @param array array() CSS classes added on post HTML markup.
832
			 * @param string $post_id Post ID.
833
			 */
834
			'classes' => apply_filters(
835
				'jetpack_relatedposts_filter_post_css_classes',
836
				array(),
837
				$post->ID
838
			),
839
		);
840
	}
841
842
	/**
843
	 * Returns either the title or a small excerpt to use as title for post.
844
	 *
845
	 * @param string $post_title
846
	 * @param string $post_content
847
	 * @uses strip_shortcodes, wp_trim_words, __
848
	 * @return string
849
	 */
850
	protected function _get_title( $post_title, $post_content ) {
851
		if ( ! empty( $post_title ) ) {
852
			return wp_strip_all_tags( $post_title );
853
		}
854
855
		$post_title = wp_trim_words( wp_strip_all_tags( strip_shortcodes( $post_content ) ), 5, '…' );
856
		if ( ! empty( $post_title ) ) {
857
			return $post_title;
858
		}
859
860
		return __( 'Untitled Post', 'jetpack' );
861
	}
862
863
	/**
864
	 * Returns a plain text post excerpt for title attribute of links.
865
	 *
866
	 * @param string $post_excerpt
867
	 * @param string $post_content
868
	 * @uses strip_shortcodes, wp_strip_all_tags, wp_trim_words
869
	 * @return string
870
	 */
871
	protected function _get_excerpt( $post_excerpt, $post_content ) {
872
		if ( empty( $post_excerpt ) )
873
			$excerpt = $post_content;
874
		else
875
			$excerpt = $post_excerpt;
876
877
		return wp_trim_words( wp_strip_all_tags( strip_shortcodes( $excerpt ) ), 50, '…' );
878
	}
879
880
	/**
881
	 * Generates the thumbnail image to be used for the post. Uses the
882
	 * image as returned by Jetpack_PostImages::get_image()
883
	 *
884
	 * @param int $post_id
885
	 * @uses self::get_options, apply_filters, Jetpack_PostImages::get_image, Jetpack_PostImages::fit_image_url
886
	 * @return string
887
	 */
888
	protected function _generate_related_post_image_params( $post_id ) {
889
		$options = $this->get_options();
890
		$image_params = array(
891
			'src' => '',
892
			'width' => 0,
893
			'height' => 0,
894
		);
895
896
		if ( ! $options['show_thumbnails'] ) {
897
			return $image_params;
898
		}
899
900
		/**
901
		 * Filter the size of the Related Posts images.
902
		 *
903
		 * @module related-posts
904
		 *
905
		 * @since 2.8.0
906
		 *
907
		 * @param array array( 'width' => 350, 'height' => 200 ) Size of the images displayed below each Related Post.
908
		 */
909
		$thumbnail_size = apply_filters(
910
			'jetpack_relatedposts_filter_thumbnail_size',
911
			array( 'width' => 350, 'height' => 200 )
912
		);
913
		if ( !is_array( $thumbnail_size ) ) {
914
			$thumbnail_size = array(
915
				'width' => (int)$thumbnail_size,
916
				'height' => (int)$thumbnail_size
917
			);
918
		}
919
920
		// Try to get post image
921
		if ( class_exists( 'Jetpack_PostImages' ) ) {
922
			$img_url = '';
923
			$post_image = Jetpack_PostImages::get_image(
924
				$post_id,
925
				$thumbnail_size
926
			);
927
928
			if ( is_array($post_image) ) {
929
				$img_url = $post_image['src'];
930
			} elseif ( class_exists( 'Jetpack_Media_Summary' ) ) {
931
				$media = Jetpack_Media_Summary::get( $post_id );
932
933
				if ( is_array($media) && !empty( $media['image'] ) ) {
934
					$img_url = $media['image'];
935
				}
936
			}
937
938
			if ( !empty( $img_url ) ) {
939
				$image_params['width'] = $thumbnail_size['width'];
940
				$image_params['height'] = $thumbnail_size['height'];
941
				$image_params['src'] = Jetpack_PostImages::fit_image_url(
942
					$img_url,
943
					$thumbnail_size['width'],
944
					$thumbnail_size['height']
945
				);
946
			}
947
		}
948
949
		return $image_params;
950
	}
951
952
	/**
953
	 * Returns the string UTF-8 encoded
954
	 *
955
	 * @param string $text
956
	 * @return string
957
	 */
958
	protected function _to_utf8( $text ) {
959
		if ( $this->_convert_charset ) {
960
			return iconv( $this->_blog_charset, 'UTF-8', $text );
961
		} else {
962
			return $text;
963
		}
964
	}
965
966
	/**
967
	 * =============================================
968
	 * PROTECTED UTILITY FUNCTIONS EXTENDED BY WPCOM
969
	 * =============================================
970
	 */
971
972
	/**
973
	 * Workhorse method to return array of related posts matched by ElasticSearch.
974
	 *
975
	 * @param int $post_id
976
	 * @param int $size
977
	 * @param array $filters
978
	 * @uses wp_remote_post, is_wp_error, get_option, wp_remote_retrieve_body, get_post, add_query_arg, remove_query_arg, get_permalink, get_post_format, apply_filters
979
	 * @return array
980
	 */
981
	protected function _get_related_posts( $post_id, $size, array $filters ) {
982
		$hits = $this->_filter_non_public_posts(
983
			$this->_get_related_post_ids(
984
				$post_id,
985
				$size,
986
				$filters
987
			)
988
		);
989
990
		/**
991
		 * Filter the Related Posts matched by ElasticSearch.
992
		 *
993
		 * @module related-posts
994
		 *
995
		 * @since 2.9.0
996
		 *
997
		 * @param array $hits Array of Post IDs matched by ElasticSearch.
998
		 * @param string $post_id Post ID of the post for which we are retrieving Related Posts.
999
		 */
1000
		$hits = apply_filters( 'jetpack_relatedposts_filter_hits', $hits, $post_id );
1001
1002
		$related_posts = array();
1003
		foreach ( $hits as $i => $hit ) {
1004
			$related_posts[] = $this->_get_related_post_data_for_post( $hit['id'], $i, $post_id );
1005
		}
1006
		return $related_posts;
1007
	}
1008
1009
	/**
1010
	 * Get array of related posts matched by ElasticSearch.
1011
	 *
1012
	 * @param int $post_id
1013
	 * @param int $size
1014
	 * @param array $filters
1015
	 * @uses wp_remote_post, is_wp_error, wp_remote_retrieve_body, get_post_meta, update_post_meta
1016
	 * @return array
1017
	 */
1018
	protected function _get_related_post_ids( $post_id, $size, array $filters ) {
1019
		$now_ts = time();
1020
		$cache_meta_key = '_jetpack_related_posts_cache';
1021
1022
		$body = array(
1023
			'size' => (int) $size,
1024
		);
1025
1026
		if ( !empty( $filters ) )
1027
			$body['filter'] = array( 'and' => $filters );
1028
1029
		// Build cache key
1030
		$cache_key = md5( serialize( $body ) );
1031
1032
		// Load all cached values
1033
		if ( wp_using_ext_object_cache() ) {
1034
			$transient_name = "{$cache_meta_key}_{$cache_key}_{$post_id}";
1035
			$cache = get_transient( $transient_name );
1036
			if ( false !== $cache ) {
1037
				return $cache;
1038
			}
1039
		} else {
1040
			$cache = get_post_meta( $post_id, $cache_meta_key, true );
1041
1042
			if ( empty( $cache ) )
1043
				$cache = array();
1044
1045
1046
			// Cache is valid! Return cached value.
1047
			if ( isset( $cache[ $cache_key ] ) && is_array( $cache[ $cache_key ] ) && $cache[ $cache_key ][ 'expires' ] > $now_ts ) {
1048
				return $cache[ $cache_key ][ 'payload' ];
1049
			}
1050
		}
1051
1052
		$response = wp_remote_post(
1053
			"https://public-api.wordpress.com/rest/v1/sites/{$this->_blog_id_wpcom}/posts/$post_id/related/",
1054
			array(
1055
				'timeout' => 10,
1056
				'user-agent' => 'jetpack_related_posts',
1057
				'sslverify' => true,
1058
				'body' => $body,
1059
			)
1060
		);
1061
1062
		// Oh no... return nothing don't cache errors.
1063
		if ( is_wp_error( $response ) ) {
1064
			if ( isset( $cache[ $cache_key ] ) && is_array( $cache[ $cache_key ] ) )
1065
				return $cache[ $cache_key ][ 'payload' ]; // return stale
1066
			else
1067
				return array();
1068
		}
1069
1070
		$results = json_decode( wp_remote_retrieve_body( $response ), true );
1071
		$related_posts = array();
1072
		if ( is_array( $results ) && !empty( $results['hits'] ) ) {
1073
			foreach( $results['hits'] as $hit ) {
1074
				$related_posts[] = array(
1075
					'id' => $hit['fields']['post_id'],
1076
				);
1077
			}
1078
		}
1079
1080
		// An empty array might indicate no related posts or that posts
1081
		// are not yet synced to WordPress.com, so we cache for only 1
1082
		// minute in this case
1083
		if ( empty( $related_posts ) ) {
1084
			$cache_ttl = 60;
1085
		} else {
1086
			$cache_ttl = 12 * HOUR_IN_SECONDS;
1087
		}
1088
1089
		// Update cache
1090
		if ( wp_using_ext_object_cache() ) {
1091
			set_transient( $transient_name, $related_posts, $cache_ttl );
0 ignored issues
show
Bug introduced by
The variable $transient_name does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1092
		} else {
1093
			// Copy all valid cache values
1094
			$new_cache = array();
1095
			foreach ( $cache as $k => $v ) {
1096
				if ( is_array( $v ) && $v[ 'expires' ] > $now_ts ) {
1097
					$new_cache[ $k ] = $v;
1098
				}
1099
			}
1100
1101
			// Set new cache value
1102
			$cache_expires = $cache_ttl + $now_ts;
1103
			$new_cache[ $cache_key ] = array(
1104
				'expires' => $cache_expires,
1105
				'payload' => $related_posts,
1106
			);
1107
			update_post_meta( $post_id, $cache_meta_key, $new_cache );
1108
		}
1109
1110
		return $related_posts;
1111
	}
1112
1113
	/**
1114
	 * Filter out any hits that are not public anymore.
1115
	 *
1116
	 * @param array $related_posts
1117
	 * @uses get_post_stati, get_post_status
1118
	 * @return array
1119
	 */
1120
	protected function _filter_non_public_posts( array $related_posts ) {
1121
		$public_stati = get_post_stati( array( 'public' => true ) );
1122
1123
		$filtered = array();
1124
		foreach ( $related_posts as $hit ) {
1125
			if ( in_array( get_post_status( $hit['id'] ), $public_stati ) ) {
1126
				$filtered[] = $hit;
1127
			}
1128
		}
1129
		return $filtered;
1130
	}
1131
1132
	/**
1133
	 * Generates a context for the related content (second line in related post output).
1134
	 * Order of importance:
1135
	 *   - First category (Not 'Uncategorized')
1136
	 *   - First post tag
1137
	 *   - Number of comments
1138
	 *
1139
	 * @param int $post_id
1140
	 * @uses get_the_category, get_the_terms, get_comments_number, number_format_i18n, __, _n
1141
	 * @return string
1142
	 */
1143
	protected function _generate_related_post_context( $post_id ) {
1144
		$categories = get_the_category( $post_id );
1145 View Code Duplication
		if ( is_array( $categories ) ) {
1146
			foreach ( $categories as $category ) {
1147
				if ( 'uncategorized' != $category->slug && '' != trim( $category->name ) ) {
1148
					$post_cat_context = sprintf(
1149
						_x( 'In "%s"', 'in {category/tag name}', 'jetpack' ),
1150
						$category->name
1151
					);
1152
					/**
1153
					 * Filter the "In Category" line displayed in the post context below each Related Post.
1154
					 *
1155
					 * @module related-posts
1156
					 *
1157
					 * @since 3.2.0
1158
					 *
1159
					 * @param string $post_cat_context "In Category" line displayed in the post context below each Related Post.
1160
					 * @param array $category Array containing information about the category.
1161
					 */
1162
					return apply_filters( 'jetpack_relatedposts_post_category_context', $post_cat_context, $category );
1163
				}
1164
			}
1165
		}
1166
1167
		$tags = get_the_terms( $post_id, 'post_tag' );
1168 View Code Duplication
		if ( is_array( $tags ) ) {
1169
			foreach ( $tags as $tag ) {
1170
				if ( '' != trim( $tag->name ) ) {
1171
					$post_tag_context = sprintf(
1172
						_x( 'In "%s"', 'in {category/tag name}', 'jetpack' ),
1173
						$tag->name
1174
					);
1175
					/**
1176
					 * Filter the "In Tag" line displayed in the post context below each Related Post.
1177
					 *
1178
					 * @module related-posts
1179
					 *
1180
					 * @since 3.2.0
1181
					 *
1182
					 * @param string $post_tag_context "In Tag" line displayed in the post context below each Related Post.
1183
					 * @param array $tag Array containing information about the tag.
1184
					 */
1185
					return apply_filters( 'jetpack_relatedposts_post_tag_context', $post_tag_context, $tag );
1186
				}
1187
			}
1188
		}
1189
1190
		$comment_count = get_comments_number( $post_id );
1191
		if ( $comment_count > 0 ) {
1192
			return sprintf(
1193
				_n( 'With 1 comment', 'With %s comments', $comment_count, 'jetpack' ),
1194
				number_format_i18n( $comment_count )
1195
			);
1196
		}
1197
1198
		return __( 'Similar post', 'jetpack' );
1199
	}
1200
1201
	/**
1202
	 * Logs clicks for clickthrough analysis and related result tuning.
1203
	 *
1204
	 * @return null
1205
	 */
1206
	protected function _log_click( $post_id, $to_post_id, $link_position ) {
0 ignored issues
show
Unused Code introduced by
The parameter $post_id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $to_post_id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $link_position is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1207
1208
	}
1209
1210
	/**
1211
	 * Determines if the current post is able to use related posts.
1212
	 *
1213
	 * @uses self::get_options, is_admin, is_single, apply_filters
1214
	 * @return bool
1215
	 */
1216
	protected function _enabled_for_request() {
1217
		// Default to enabled
1218
		$enabled = true;
1219
1220
		// Must have feature enabled
1221
		$options = $this->get_options();
1222
		if ( ! $options['enabled'] ) {
1223
			$enabled = false;
1224
		}
1225
1226
		// Only run for frontend pages
1227
		if ( is_admin() ) {
1228
			$enabled = false;
1229
		}
1230
1231
		// Only run for standalone posts
1232
		if ( ! is_single() ) {
1233
			$enabled = false;
1234
		}
1235
1236
		/**
1237
		 * Filter the Enabled value to allow related posts to be shown on pages as well.
1238
		 *
1239
		 * @module related-posts
1240
		 *
1241
		 * @since 3.3.0
1242
		 *
1243
		 * @param bool $enabled Should Related Posts be enabled on the current page.
1244
		 */
1245
		return apply_filters( 'jetpack_relatedposts_filter_enabled_for_request', $enabled );
1246
	}
1247
1248
	/**
1249
	 * Adds filters and enqueues assets.
1250
	 *
1251
	 * @uses self::_enqueue_assets, self::_setup_shortcode, add_filter
1252
	 * @return null
1253
	 */
1254
	protected function _action_frontend_init_page() {
1255
		$this->_enqueue_assets( true, true );
1256
		$this->_setup_shortcode();
1257
1258
		add_filter( 'the_content', array( $this, 'filter_add_target_to_dom' ), 40 );
1259
	}
1260
1261
	/**
1262
	 * Enqueues assets needed to do async loading of related posts.
1263
	 *
1264
	 * @uses wp_enqueue_script, wp_enqueue_style, plugins_url
1265
	 * @return null
1266
	 */
1267
	protected function _enqueue_assets( $script, $style ) {
1268
		if ( $script ) {
1269
			wp_enqueue_script( 'jetpack_related-posts', plugins_url( 'related-posts.js', __FILE__ ), array( 'jquery' ), self::VERSION );
1270
			$related_posts_js_options = array(
1271
				/**
1272
				 * Filter each Related Post Heading structure.
1273
				 *
1274
				 * @since 4.0.0
1275
				 *
1276
				 * @param string $str Related Post Heading structure. Default to h4.
1277
				 */
1278
				'post_heading' => apply_filters( 'jetpack_relatedposts_filter_post_heading', esc_attr( 'h4' ) ),
1279
			);
1280
			wp_localize_script( 'jetpack_related-posts', 'related_posts_js_options', $related_posts_js_options );
1281
		}
1282
		if ( $style ){
1283
			if( is_rtl() ) {
1284
				wp_enqueue_style( 'jetpack_related-posts', plugins_url( 'rtl/related-posts-rtl.css', __FILE__ ), array(), self::VERSION );
1285
			} else {
1286
				wp_enqueue_style( 'jetpack_related-posts', plugins_url( 'related-posts.css', __FILE__ ), array(), self::VERSION );
1287
			}
1288
		}
1289
	}
1290
1291
	/**
1292
	 * Sets up the shortcode processing.
1293
	 *
1294
	 * @uses add_filter, add_shortcode
1295
	 * @return null
1296
	 */
1297
	protected function _setup_shortcode() {
1298
		add_filter( 'the_content', array( $this, 'test_for_shortcode' ), 0 );
1299
1300
		add_shortcode( self::SHORTCODE, array( $this, 'get_target_html' ) );
1301
	}
1302
1303
	protected function _allow_feature_toggle() {
1304
		if ( null === $this->_allow_feature_toggle ) {
1305
			/**
1306
			 * Filter the display of the Related Posts toggle in Settings > Reading.
1307
			 *
1308
			 * @module related-posts
1309
			 *
1310
			 * @since 2.8.0
1311
			 *
1312
			 * @param bool false Display a feature toggle. Default to false.
1313
			 */
1314
			$this->_allow_feature_toggle = apply_filters( 'jetpack_relatedposts_filter_allow_feature_toggle', false );
1315
		}
1316
		return $this->_allow_feature_toggle;
1317
	}
1318
1319
	/**
1320
	 * ===================================================
1321
	 * FUNCTIONS EXPOSING RELATED POSTS IN THE WP REST API
1322
	 * ===================================================
1323
	 */
1324
1325
	/**
1326
	 * Add Related Posts to the REST API Post response.
1327
	 *
1328
	 * @since 4.4.0
1329
	 *
1330
	 * @action rest_api_init
1331
	 * @uses register_rest_field, self::rest_get_related_posts
1332
	 * @return null
1333
	 */
1334
	public function rest_register_related_posts() {
1335
		register_rest_field( 'post',
1336
			'jetpack-related-posts',
1337
			array(
1338
				'get_callback' => array( $this, 'rest_get_related_posts' ),
1339
				'update_callback' => null,
1340
				'schema'          => null,
1341
			)
1342
		);
1343
	}
1344
1345
	/**
1346
	 * Build an array of Related Posts.
1347
	 *
1348
	 * @since 4.4.0
1349
	 *
1350
	 * @param array $object Details of current post.
1351
	 * @param string $field_name Name of field.
1352
	 * @param WP_REST_Request $request Current request
1353
	 *
1354
	 * @uses self::get_for_post_id
1355
	 *
1356
	 * @return array
1357
	 */
1358
	public function rest_get_related_posts( $object, $field_name, $request ) {
0 ignored issues
show
Unused Code introduced by
The parameter $field_name is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1359
		return $this->get_for_post_id( $object['id'], array() );
1360
	}
1361
}
1362
1363
class Jetpack_RelatedPosts_Raw extends Jetpack_RelatedPosts {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
1364
	protected $_query_name;
1365
1366
	/**
1367
	 * Allows callers of this class to tag each query with a unique name for tracking purposes.
1368
	 *
1369
	 * @param string $name
1370
	 * @return Jetpack_RelatedPosts_Raw
1371
	 */
1372
	public function set_query_name( $name ) {
1373
		$this->_query_name = (string) $name;
1374
		return $this;
1375
	}
1376
1377
	/**
1378
	 * The raw related posts class can be used by other plugins or themes
1379
	 * to get related content. This class wraps the existing RelatedPosts
1380
	 * logic thus we never want to add anything to the DOM or do anything
1381
	 * for event hooks. We will also not present any settings for this
1382
	 * class and keep it enabled as calls to this class is done
1383
	 * programmatically.
1384
	 */
1385
	public function action_admin_init() {}
1386
	public function action_frontend_init() {}
1387
	public function get_options() {
1388
		return array(
1389
			'enabled' => true,
1390
		);
1391
	}
1392
1393
	/**
1394
	 * Workhorse method to return array of related posts ids matched by ElasticSearch.
1395
	 *
1396
	 * @param int $post_id
1397
	 * @param int $size
1398
	 * @param array $filters
1399
	 * @uses wp_remote_post, is_wp_error, wp_remote_retrieve_body
1400
	 * @return array
1401
	 */
1402
	protected function _get_related_posts( $post_id, $size, array $filters ) {
1403
		$hits = $this->_filter_non_public_posts(
1404
			$this->_get_related_post_ids(
1405
				$post_id,
1406
				$size,
1407
				$filters
1408
			)
1409
		);
1410
1411
		/** This filter is already documented in modules/related-posts/related-posts.php */
1412
		$hits = apply_filters( 'jetpack_relatedposts_filter_hits', $hits, $post_id );
1413
1414
		return $hits;
1415
	}
1416
}
1417