Completed
Push — add/related-posts-customize ( d78726...4f538b )
by
unknown
10:51
created

Jetpack_RelatedPosts::_get_title()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 7
nc 3
nop 2
dl 0
loc 12
rs 9.4285
c 0
b 0
f 0
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
81
	/**
82
	 * =================
83
	 * ACTIONS & FILTERS
84
	 * =================
85
	 */
86
87
	/**
88
	 * Add a checkbox field to Settings > Reading for enabling related posts.
89
	 *
90
	 * @action admin_init
91
	 * @uses add_settings_field, __, register_setting, add_action
92
	 * @return null
93
	 */
94
	public function action_admin_init() {
95
96
		// Add the setting field [jetpack_relatedposts] and place it in Settings > Reading
97
		add_settings_field( 'jetpack_relatedposts', '<span id="jetpack_relatedposts">' . __( 'Related posts', 'jetpack' ) . '</span>', array( $this, 'print_setting_html' ), 'reading' );
98
		register_setting( 'reading', 'jetpack_relatedposts', array( $this, 'parse_options' ) );
99
		add_action('admin_head', array( $this, 'print_setting_head' ) );
100
101
		if( 'options-reading.php' == $GLOBALS['pagenow'] ) {
102
			// Enqueue style for live preview on the reading settings page
103
			$this->_enqueue_assets( false, true );
104
		}
105
	}
106
107
	/**
108
	 * Load related posts assets if it's a elegiable front end page or execute search and return JSON if it's an endpoint request.
109
	 *
110
	 * @global $_GET
111
	 * @action wp
112
	 * @uses add_shortcode, get_the_ID
113
	 * @returns null
114
	 */
115
	public function action_frontend_init() {
116
		// Add a shortcode handler that outputs nothing, this gets overridden later if we can display related content
117
		add_shortcode( self::SHORTCODE, array( $this, 'get_target_html_unsupported' ) );
118
119
		if ( ! $this->_enabled_for_request() )
120
			return;
121
122
		if ( isset( $_GET['relatedposts'] ) ) {
123
			$excludes = array();
124
			if ( isset( $_GET['relatedposts_exclude'] ) ) {
125
				$excludes = explode( ',', $_GET['relatedposts_exclude'] );
126
			}
127
128
			$this->_action_frontend_init_ajax( $excludes );
129
		} else {
130
			if ( isset( $_GET['relatedposts_hit'], $_GET['relatedposts_origin'], $_GET['relatedposts_position'] ) ) {
131
				$this->_log_click( $_GET['relatedposts_origin'], get_the_ID(), $_GET['relatedposts_position'] );
132
				$this->_previous_post_id = (int) $_GET['relatedposts_origin'];
133
			}
134
135
			$this->_action_frontend_init_page();
136
		}
137
138
	}
139
140
	/**
141
	 * Render insertion point.
142
	 *
143
	 * @since 4.2.0
144
	 *
145
	 * @return string
146
	 */
147
	public function get_headline() {
148
		$options = $this->get_options();
149
150
		if ( $options['show_headline'] ) {
151
			$headline = sprintf(
152
				'<h3 class="jp-relatedposts-headline"><em>%s</em></h3>',
153
				esc_html__( 'Related', 'jetpack' )
154
			);
155
		} else {
156
			$headline = '';
157
		}
158
		return $headline;
159
	}
160
161
	/**
162
	 * Adds a target to the post content to load related posts into if a shortcode for it did not already exist.
163
	 *
164
	 * @filter the_content
165
	 * @param string $content
166
	 * @returns string
167
	 */
168
	public function filter_add_target_to_dom( $content ) {
169
		if ( !$this->_found_shortcode ) {
170
			$content .= "\n" . $this->get_target_html();
171
		}
172
173
		return $content;
174
	}
175
176
	/**
177
	 * Looks for our shortcode on the unfiltered content, this has to execute early.
178
	 *
179
	 * @filter the_content
180
	 * @param string $content
181
	 * @uses has_shortcode
182
	 * @returns string
183
	 */
184
	public function test_for_shortcode( $content ) {
185
		$this->_found_shortcode = has_shortcode( $content, self::SHORTCODE );
186
187
		return $content;
188
	}
189
190
	/**
191
	 * Returns the HTML for the related posts section.
192
	 *
193
	 * @uses esc_html__, apply_filters
194
	 * @returns string
195
	 */
196
	public function get_target_html() {
197
198
		/**
199
		 * Filter the Related Posts headline.
200
		 *
201
		 * @module related-posts
202
		 *
203
		 * @since 3.0.0
204
		 *
205
		 * @param string $headline Related Posts heading.
206
		 */
207
		$headline = apply_filters( 'jetpack_relatedposts_filter_headline', $this->get_headline() );
208
209
		if ( $this->_previous_post_id ) {
210
			$exclude = "data-exclude='{$this->_previous_post_id}'";
211
		} else {
212
			$exclude = "";
213
		}
214
215
		return <<<EOT
216
<div id='jp-relatedposts' class='jp-relatedposts' $exclude>
217
	$headline
218
</div>
219
EOT;
220
	}
221
222
	/**
223
	 * 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.
224
	 *
225
	 * @returns string
226
	 */
227
	public function get_target_html_unsupported() {
228
		return "\n\n<!-- Jetpack Related Posts is not supported in this context. -->\n\n";
229
	}
230
231
	/**
232
	 * ========================
233
	 * PUBLIC UTILITY FUNCTIONS
234
	 * ========================
235
	 */
236
237
	/**
238
	 * Gets options set for Jetpack_RelatedPosts and merge with defaults.
239
	 *
240
	 * @uses Jetpack_Options::get_option, apply_filters
241
	 * @return array
242
	 */
243
	public function get_options() {
244
		if ( null === $this->_options ) {
245
			$this->_options = Jetpack_Options::get_option( 'relatedposts', array() );
246
			if ( ! is_array( $this->_options ) )
247
				$this->_options = array();
248
			if ( ! isset( $this->_options['enabled'] ) )
249
				$this->_options['enabled'] = true;
250
			if ( ! isset( $this->_options['show_headline'] ) )
251
				$this->_options['show_headline'] = true;
252
			if ( ! isset( $this->_options['show_thumbnails'] ) )
253
				$this->_options['show_thumbnails'] = false;
254
			if ( ! isset( $this->_options['show_date'] ) ) {
255
				$this->_options['show_date'] = true;
256
			}
257
			if ( ! isset( $this->_options['show_context'] ) ) {
258
				$this->_options['show_context'] = true;
259
			}
260
			if ( ! isset( $this->_options['layout'] ) ) {
261
				$this->_options['layout'] = 'grid';
262
			}
263
			if ( empty( $this->_options['size'] ) || (int)$this->_options['size'] < 1 )
264
				$this->_options['size'] = 3;
265
266
			/**
267
			 * Filter Related Posts basic options.
268
			 *
269
			 * @module related-posts
270
			 *
271
			 * @since 2.8.0
272
			 *
273
			 * @param array $this->_options Array of basic Related Posts options.
274
			 */
275
			$this->_options = apply_filters( 'jetpack_relatedposts_filter_options', $this->_options );
276
		}
277
278
		return $this->_options;
279
	}
280
281
	/**
282
	 * Parses input and returns normalized options array.
283
	 *
284
	 * @param array $input
285
	 * @uses self::get_options
286
	 * @return array
287
	 */
288
	public function parse_options( $input ) {
289
		$current = $this->get_options();
290
291
		if ( !is_array( $input ) )
292
			$input = array();
293
294
		if ( isset( $input['enabled'] ) && '1' == $input['enabled'] ) {
295
			$current['enabled'] = true;
296
			$current['show_headline'] = ( isset( $input['show_headline'] ) && '1' == $input['show_headline'] );
297
			$current['show_thumbnails'] = ( isset( $input['show_thumbnails'] ) && '1' == $input['show_thumbnails'] );
298
			$current['show_date'] = ( isset( $input['show_date'] ) && '1' == $input['show_date'] );
299
			$current['show_context'] = ( isset( $input['show_context'] ) && '1' == $input['show_context'] );
300
			$current['layout'] = isset( $input['layout'] ) && in_array( $input['layout'], array( 'grid', 'list' ), true ) ? $input['layout'] : 'grid';
301
		} else {
302
			$current['enabled'] = false;
303
		}
304
305
		if ( isset( $input['size'] ) && (int)$input['size'] > 0 )
306
			$current['size'] = (int)$input['size'];
307
		else
308
			$current['size'] = null;
309
310
		return $current;
311
	}
312
313
	/**
314
	 * HTML for admin settings page.
315
	 *
316
	 * @uses self::get_options, checked, esc_html__
317
	 * @returns null
318
	 */
319
	public function print_setting_html() {
320
		$options = $this->get_options();
321
322
		$ui_settings_template = <<<EOT
323
<ul id="settings-reading-relatedposts-customize">
324
	<li>
325
		<label><input name="jetpack_relatedposts[show_headline]" type="checkbox" value="1" %s /> %s</label>
326
	</li>
327
	<li>
328
		<label><input name="jetpack_relatedposts[show_thumbnails]" type="checkbox" value="1" %s /> %s</label>
329
	</li>
330
	<li>
331
		<label><input name="jetpack_relatedposts[show_date]" type="checkbox" value="1" %s /> %s</label>
332
	</li>
333
	<li>
334
		<label><input name="jetpack_relatedposts[show_context]" type="checkbox" value="1" %s /> %s</label>
335
	</li>
336
</ul>
337
<div id='settings-reading-relatedposts-preview'>
338
	%s
339
	<div id="jp-relatedposts" class="jp-relatedposts"></div>
340
</div>
341
EOT;
342
		$ui_settings = sprintf(
343
			$ui_settings_template,
344
			checked( $options['show_headline'], true, false ),
345
			esc_html__( 'Show a "Related" header to more clearly separate the related section from posts', 'jetpack' ),
346
			checked( $options['show_thumbnails'], true, false ),
347
			esc_html__( 'Use a large and visually striking layout', 'jetpack' ),
348
			checked( $options['show_date'], true, false ),
349
			esc_html__( 'Show entry date', 'jetpack' ),
350
			checked( $options['show_context'], true, false ),
351
			esc_html__( 'Show context (category or tag)', 'jetpack' ),
352
			esc_html__( 'Preview:', 'jetpack' )
353
		);
354
355
		if ( !$this->_allow_feature_toggle() ) {
356
			$template = <<<EOT
357
<input type="hidden" name="jetpack_relatedposts[enabled]" value="1" />
358
%s
359
EOT;
360
			printf(
361
				$template,
362
				$ui_settings
363
			);
364
		} else {
365
			$template = <<<EOT
366
<ul id="settings-reading-relatedposts">
367
	<li>
368
		<label><input type="radio" name="jetpack_relatedposts[enabled]" value="0" class="tog" %s /> %s</label>
369
	</li>
370
	<li>
371
		<label><input type="radio" name="jetpack_relatedposts[enabled]" value="1" class="tog" %s /> %s</label>
372
		%s
373
	</li>
374
</ul>
375
EOT;
376
			printf(
377
				$template,
378
				checked( $options['enabled'], false, false ),
379
				esc_html__( 'Hide related content after posts', 'jetpack' ),
380
				checked( $options['enabled'], true, false ),
381
				esc_html__( 'Show related content after posts', 'jetpack' ),
382
				$ui_settings
383
			);
384
		}
385
	}
386
387
	/**
388
	 * Head JS/CSS for admin settings page.
389
	 *
390
	 * @uses esc_html__
391
	 * @returns null
392
	 */
393
	public function print_setting_head() {
394
395
		// only dislay the Related Posts JavaScript on the Reading Settings Admin Page
396
		$current_screen =  get_current_screen();
397
398
		if ( is_null( $current_screen ) ) {
399
			return;
400
		}
401
402
		if( 'options-reading' != $current_screen->id )
403
			return;
404
405
		$related_headline = sprintf(
406
			'<h3 class="jp-relatedposts-headline"><em>%s</em></h3>',
407
			esc_html__( 'Related', 'jetpack' )
408
		);
409
410
		$href_params = 'class="jp-relatedposts-post-a" href="#jetpack_relatedposts" rel="nofollow" data-origin="0" data-position="0"';
411
		$related_with_images = <<<EOT
412
<div class="jp-relatedposts-items jp-relatedposts-items-visual">
413
	<div class="jp-relatedposts-post jp-relatedposts-post0 jp-relatedposts-post-thumbs" data-post-id="0" data-post-format="image">
414
		<a $href_params>
415
			<img class="jp-relatedposts-post-img" src="https://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">
416
		</a>
417
		<h4 class="jp-relatedposts-post-title">
418
			<a $href_params>Big iPhone/iPad Update Now Available</a>
419
		</h4>
420
		<p class="jp-relatedposts-post-excerpt">Big iPhone/iPad Update Now Available</p>
421
		<p class="jp-relatedposts-post-context">In "Mobile"</p>
422
	</div>
423
	<div class="jp-relatedposts-post jp-relatedposts-post1 jp-relatedposts-post-thumbs" data-post-id="0" data-post-format="image">
424
		<a $href_params>
425
			<img class="jp-relatedposts-post-img" src="https://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">
426
		</a>
427
		<h4 class="jp-relatedposts-post-title">
428
			<a $href_params>The WordPress for Android App Gets a Big Facelift</a>
429
		</h4>
430
		<p class="jp-relatedposts-post-excerpt">The WordPress for Android App Gets a Big Facelift</p>
431
		<p class="jp-relatedposts-post-context">In "Mobile"</p>
432
	</div>
433
	<div class="jp-relatedposts-post jp-relatedposts-post2 jp-relatedposts-post-thumbs" data-post-id="0" data-post-format="image">
434
		<a $href_params>
435
			<img class="jp-relatedposts-post-img" src="https://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">
436
		</a>
437
		<h4 class="jp-relatedposts-post-title">
438
			<a $href_params>Upgrade Focus: VideoPress For Weddings</a>
439
		</h4>
440
		<p class="jp-relatedposts-post-excerpt">Upgrade Focus: VideoPress For Weddings</p>
441
		<p class="jp-relatedposts-post-context">In "Upgrade"</p>
442
	</div>
443
</div>
444
EOT;
445
		$related_with_images = str_replace( "\n", '', $related_with_images );
446
		$related_without_images = <<<EOT
447
<div class="jp-relatedposts-items jp-relatedposts-items-minimal">
448
	<p class="jp-relatedposts-post jp-relatedposts-post0" data-post-id="0" data-post-format="image">
449
		<span class="jp-relatedposts-post-title"><a $href_params>Big iPhone/iPad Update Now Available</a></span>
450
		<span class="jp-relatedposts-post-context">In "Mobile"</span>
451
	</p>
452
	<p class="jp-relatedposts-post jp-relatedposts-post1" data-post-id="0" data-post-format="image">
453
		<span class="jp-relatedposts-post-title"><a $href_params>The WordPress for Android App Gets a Big Facelift</a></span>
454
		<span class="jp-relatedposts-post-context">In "Mobile"</span>
455
	</p>
456
	<p class="jp-relatedposts-post jp-relatedposts-post2" data-post-id="0" data-post-format="image">
457
		<span class="jp-relatedposts-post-title"><a $href_params>Upgrade Focus: VideoPress For Weddings</a></span>
458
		<span class="jp-relatedposts-post-context">In "Upgrade"</span>
459
	</p>
460
</div>
461
EOT;
462
		$related_without_images = str_replace( "\n", '', $related_without_images );
463
464
		if ( $this->_allow_feature_toggle() ) {
465
			$extra_css = '#settings-reading-relatedposts-customize { padding-left:2em; margin-top:.5em; }';
466
		} else {
467
			$extra_css = '';
468
		}
469
470
		echo <<<EOT
471
<style type="text/css">
472
	#settings-reading-relatedposts .disabled { opacity:.5; filter:Alpha(opacity=50); }
473
	#settings-reading-relatedposts-preview .jp-relatedposts { background:#fff; padding:.5em; width:75%; }
474
	$extra_css
475
</style>
476
<script type="text/javascript">
477
	jQuery( document ).ready( function($) {
478
		var update_ui = function() {
479
			var is_enabled = true;
480
			if ( 'radio' == $( 'input[name="jetpack_relatedposts[enabled]"]' ).attr('type') ) {
481
				if ( '0' == $( 'input[name="jetpack_relatedposts[enabled]"]:checked' ).val() ) {
482
					is_enabled = false;
483
				}
484
			}
485
			if ( is_enabled ) {
486
				$( '#settings-reading-relatedposts-customize' )
487
					.removeClass( 'disabled' )
488
					.find( 'input' )
489
					.attr( 'disabled', false );
490
				$( '#settings-reading-relatedposts-preview' )
491
					.removeClass( 'disabled' );
492
			} else {
493
				$( '#settings-reading-relatedposts-customize' )
494
					.addClass( 'disabled' )
495
					.find( 'input' )
496
					.attr( 'disabled', true );
497
				$( '#settings-reading-relatedposts-preview' )
498
					.addClass( 'disabled' );
499
			}
500
		};
501
502
		var update_preview = function() {
503
			var html = '';
504
			if ( $( 'input[name="jetpack_relatedposts[show_headline]"]:checked' ).length ) {
505
				html += '$related_headline';
506
			}
507
			if ( $( 'input[name="jetpack_relatedposts[show_thumbnails]"]:checked' ).length ) {
508
				html += '$related_with_images';
509
			} else {
510
				html += '$related_without_images';
511
			}
512
			$( '#settings-reading-relatedposts-preview .jp-relatedposts' ).html( html );
513
			if ( $( 'input[name="jetpack_relatedposts[show_date]"]:checked' ).length ) {
514
				$( '.jp-relatedposts-post-title' ).each( function() {
515
					$( this ).after( $( '<span>August 8, 2005</span>' ) );
516
				} );
517
			}
518
			if ( $( 'input[name="jetpack_relatedposts[show_context]"]:checked' ).length ) {
519
				$( '.jp-relatedposts-post-context' ).show();
520
			} else {
521
				$( '.jp-relatedposts-post-context' ).hide();
522
			}
523
			$( '#settings-reading-relatedposts-preview .jp-relatedposts' ).show();
524
		};
525
526
		// Update on load
527
		update_preview();
528
		update_ui();
529
530
		// Update on change
531
		$( '#settings-reading-relatedposts-customize input' )
532
			.change( update_preview );
533
		$( '#settings-reading-relatedposts' )
534
			.find( 'input.tog' )
535
			.change( update_ui );
536
	});
537
</script>
538
EOT;
539
	}
540
541
	/**
542
	 * Gets an array of related posts that match the given post_id.
543
	 *
544
	 * @param int $post_id
545
	 * @param array $args - params to use when building ElasticSearch filters to narrow down the search domain.
546
	 * @uses self::get_options, get_post_type, wp_parse_args, apply_filters
547
	 * @return array
548
	 */
549
	public function get_for_post_id( $post_id, array $args ) {
550
		$options = $this->get_options();
551
552
		if ( ! empty( $args['size'] ) )
553
			$options['size'] = $args['size'];
554
555
		if ( ! $options['enabled'] || 0 == (int)$post_id || empty( $options['size'] ) )
556
			return array();
557
558
		$defaults = array(
559
			'size' => (int)$options['size'],
560
			'post_type' => get_post_type( $post_id ),
561
			'post_formats' => array(),
562
			'has_terms' => array(),
563
			'date_range' => array(),
564
			'exclude_post_ids' => array(),
565
		);
566
		$args = wp_parse_args( $args, $defaults );
567
		/**
568
		 * Filter the arguments used to retrieve a list of Related Posts.
569
		 *
570
		 * @module related-posts
571
		 *
572
		 * @since 2.8.0
573
		 *
574
		 * @param array $args Array of options to retrieve Related Posts.
575
		 * @param string $post_id Post ID of the post for which we are retrieving Related Posts.
576
		 */
577
		$args = apply_filters( 'jetpack_relatedposts_filter_args', $args, $post_id );
578
579
		$filters = $this->_get_es_filters_from_args( $post_id, $args );
580
		/**
581
		 * Filter ElasticSearch options used to calculate Related Posts.
582
		 *
583
		 * @module related-posts
584
		 *
585
		 * @since 2.8.0
586
		 *
587
		 * @param array $filters Array of ElasticSearch filters based on the post_id and args.
588
		 * @param string $post_id Post ID of the post for which we are retrieving Related Posts.
589
		 */
590
		$filters = apply_filters( 'jetpack_relatedposts_filter_filters', $filters, $post_id );
591
592
		$results = $this->_get_related_posts( $post_id, $args['size'], $filters );
593
		/**
594
		 * Filter the array of related posts matched by ElasticSearch.
595
		 *
596
		 * @module related-posts
597
		 *
598
		 * @since 2.8.0
599
		 *
600
		 * @param array $results Array of related posts matched by ElasticSearch.
601
		 * @param string $post_id Post ID of the post for which we are retrieving Related Posts.
602
		 */
603
		return apply_filters( 'jetpack_relatedposts_returned_results', $results, $post_id );
604
	}
605
606
	/**
607
	 * =========================
608
	 * PRIVATE UTILITY FUNCTIONS
609
	 * =========================
610
	 */
611
612
	/**
613
	 * Creates an array of ElasticSearch filters based on the post_id and args.
614
	 *
615
	 * @param int $post_id
616
	 * @param array $args
617
	 * @uses apply_filters, get_post_types, get_post_format_strings
618
	 * @return array
619
	 */
620
	protected function _get_es_filters_from_args( $post_id, array $args ) {
621
		$filters = array();
622
623
		/**
624
		 * Filter the terms used to search for Related Posts.
625
		 *
626
		 * @module related-posts
627
		 *
628
		 * @since 2.8.0
629
		 *
630
		 * @param array $args['has_terms'] Array of terms associated to the Related Posts.
631
		 * @param string $post_id Post ID of the post for which we are retrieving Related Posts.
632
		 */
633
		$args['has_terms'] = apply_filters( 'jetpack_relatedposts_filter_has_terms', $args['has_terms'], $post_id );
634
		if ( ! empty( $args['has_terms'] ) ) {
635
			foreach( (array)$args['has_terms'] as $term ) {
636
				if ( mb_strlen( $term->taxonomy ) ) {
637
					switch ( $term->taxonomy ) {
638
						case 'post_tag':
639
							$tax_fld = 'tag.slug';
640
							break;
641
						case 'category':
642
							$tax_fld = 'category.slug';
643
							break;
644
						default:
645
							$tax_fld = 'taxonomy.' . $term->taxonomy . '.slug';
646
							break;
647
					}
648
					$filters[] = array( 'term' => array( $tax_fld => $term->slug ) );
649
				}
650
			}
651
		}
652
653
		/**
654
		 * Filter the Post Types where we search Related Posts.
655
		 *
656
		 * @module related-posts
657
		 *
658
		 * @since 2.8.0
659
		 *
660
		 * @param array $args['post_type'] Array of Post Types.
661
		 * @param string $post_id Post ID of the post for which we are retrieving Related Posts.
662
		 */
663
		$args['post_type'] = apply_filters( 'jetpack_relatedposts_filter_post_type', $args['post_type'], $post_id );
664
		$valid_post_types = get_post_types();
665
		if ( is_array( $args['post_type'] ) ) {
666
			$sanitized_post_types = array();
667
			foreach ( $args['post_type'] as $pt ) {
668
				if ( in_array( $pt, $valid_post_types ) )
669
					$sanitized_post_types[] = $pt;
670
			}
671
			if ( ! empty( $sanitized_post_types ) )
672
				$filters[] = array( 'terms' => array( 'post_type' => $sanitized_post_types ) );
673
		} else if ( in_array( $args['post_type'], $valid_post_types ) && 'all' != $args['post_type'] ) {
674
			$filters[] = array( 'term' => array( 'post_type' => $args['post_type'] ) );
675
		}
676
677
		/**
678
		 * Filter the Post Formats where we search Related Posts.
679
		 *
680
		 * @module related-posts
681
		 *
682
		 * @since 3.3.0
683
		 *
684
		 * @param array $args['post_formats'] Array of Post Formats.
685
		 * @param string $post_id Post ID of the post for which we are retrieving Related Posts.
686
		 */
687
		$args['post_formats'] = apply_filters( 'jetpack_relatedposts_filter_post_formats', $args['post_formats'], $post_id );
688
		$valid_post_formats = get_post_format_strings();
689
		$sanitized_post_formats = array();
690
		foreach ( $args['post_formats'] as $pf ) {
691
			if ( array_key_exists( $pf, $valid_post_formats ) ) {
692
				$sanitized_post_formats[] = $pf;
693
			}
694
		}
695
		if ( ! empty( $sanitized_post_formats ) ) {
696
			$filters[] = array( 'terms' => array( 'post_format' => $sanitized_post_formats ) );
697
		}
698
699
		/**
700
		 * Filter the date range used to search Related Posts.
701
		 *
702
		 * @module related-posts
703
		 *
704
		 * @since 2.8.0
705
		 *
706
		 * @param array $args['date_range'] Array of a month interval where we search Related Posts.
707
		 * @param string $post_id Post ID of the post for which we are retrieving Related Posts.
708
		 */
709
		$args['date_range'] = apply_filters( 'jetpack_relatedposts_filter_date_range', $args['date_range'], $post_id );
710
		if ( is_array( $args['date_range'] ) && ! empty( $args['date_range'] ) ) {
711
			$args['date_range'] = array_map( 'intval', $args['date_range'] );
712
			if ( !empty( $args['date_range']['from'] ) && !empty( $args['date_range']['to'] ) ) {
713
				$filters[] = array(
714
					'range' => array(
715
						'date_gmt' => $this->_get_coalesced_range( $args['date_range'] ),
716
					)
717
				);
718
			}
719
		}
720
721
		/**
722
		 * Filter the Post IDs excluded from appearing in Related Posts.
723
		 *
724
		 * @module related-posts
725
		 *
726
		 * @since 2.9.0
727
		 *
728
		 * @param array $args['exclude_post_ids'] Array of Post IDs.
729
		 * @param string $post_id Post ID of the post for which we are retrieving Related Posts.
730
		 */
731
		$args['exclude_post_ids'] = apply_filters( 'jetpack_relatedposts_filter_exclude_post_ids', $args['exclude_post_ids'], $post_id );
732
		if ( !empty( $args['exclude_post_ids'] ) && is_array( $args['exclude_post_ids'] ) ) {
733
			foreach ( $args['exclude_post_ids'] as $exclude_post_id) {
734
				$exclude_post_id = (int)$exclude_post_id;
735
736
				if ( $exclude_post_id > 0 )
737
					$filters[] = array( 'not' => array( 'term' => array( 'post_id' => $exclude_post_id ) ) );
738
			}
739
		}
740
741
		return $filters;
742
	}
743
744
	/**
745
	 * Takes a range and coalesces it into a month interval bracketed by a time as determined by the blog_id to enhance caching.
746
	 *
747
	 * @param array $date_range
748
	 * @return array
749
	 */
750
	protected function _get_coalesced_range( array $date_range ) {
751
		$now = time();
752
		$coalesce_time = $this->_blog_id_wpcom % 86400;
753
		$current_time = $now - strtotime( 'today', $now );
754
755
		if ( $current_time < $coalesce_time && '01' == date( 'd', $now ) ) {
756
			// Move back 1 period
757
			return array(
758
				'from' => date( 'Y-m-01', strtotime( '-1 month', $date_range['from'] ) ) . ' ' . date( 'H:i:s', $coalesce_time ),
759
				'to'   => date( 'Y-m-01', $date_range['to'] ) . ' ' . date( 'H:i:s', $coalesce_time ),
760
			);
761
		} else {
762
			// Use current period
763
			return array(
764
				'from' => date( 'Y-m-01', $date_range['from'] ) . ' ' . date( 'H:i:s', $coalesce_time ),
765
				'to'   => date( 'Y-m-01', strtotime( '+1 month', $date_range['to'] ) ) . ' ' . date( 'H:i:s', $coalesce_time ),
766
			);
767
		}
768
	}
769
770
	/**
771
	 * Generate and output ajax response for related posts API call.
772
	 * NOTE: Calls exit() to end all further processing after payload has been outputed.
773
	 *
774
	 * @param array $excludes array of post_ids to exclude
775
	 * @uses send_nosniff_header, self::get_for_post_id, get_the_ID
776
	 * @return null
777
	 */
778
	protected function _action_frontend_init_ajax( array $excludes ) {
779
		define( 'DOING_AJAX', true );
780
781
		header( 'Content-type: application/json; charset=utf-8' ); // JSON can only be UTF-8
782
		send_nosniff_header();
783
784
		$options = $this->get_options();
785
786
		if ( isset( $_GET['jetpackrpcustomize'] ) ) {
787
788
			// If we're in the customizer, add dummy content.
789
			$date_now = current_time( get_option( 'date_format' ) );
790
			$related_posts = array(
791
				array(
792
					'id'       => - 1,
793
					'url'      => 'https://jetpackme.files.wordpress.com/2014/08/1-wpios-ipad-3-1-viewsite.png?w=350&h=200&crop=1',
794
					'url_meta' => array(
795
						'origin'   => 0,
796
						'position' => 0
797
					),
798
					'title'    => esc_html__( 'Big iPhone/iPad Update Now Available', 'jetpack' ),
799
					'date'     => $date_now,
800
					'format'   => false,
801
					'excerpt'  => esc_html__( 'It is that time of the year when devices are shiny again.', 'jetpack' ),
802
					'rel'      => 'nofollow',
803
					'context'  => esc_html__( 'In "Mobile"', 'jetpack' ),
804
					'img'      => array(
805
						'src'    => 'https://jetpackme.files.wordpress.com/2014/08/1-wpios-ipad-3-1-viewsite.png?w=350&h=200&crop=1',
806
						'width'  => 350,
807
						'height' => 200
808
					),
809
					'classes'  => array()
810
				),
811
				array(
812
					'id'       => - 1,
813
					'url'      => 'https://jetpackme.files.wordpress.com/2014/08/wordpress-com-news-wordpress-for-android-ui-update2.jpg?w=350&h=200&crop=1',
814
					'url_meta' => array(
815
						'origin'   => 0,
816
						'position' => 0
817
					),
818
					'title'    => esc_html__( 'The WordPress for Android App Gets a Big Facelift', 'jetpack' ),
819
					'date'     => $date_now,
820
					'format'   => false,
821
					'excerpt'  => esc_html__( 'Writing is new again in Android with the new WordPress app.' ),
822
					'rel'      => 'nofollow',
823
					'context'  => esc_html__( 'In "Mobile"', 'jetpack' ),
824
					'img'      => array(
825
						'src'    => 'https://jetpackme.files.wordpress.com/2014/08/wordpress-com-news-wordpress-for-android-ui-update2.jpg?w=350&h=200&crop=1',
826
						'width'  => 350,
827
						'height' => 200
828
					),
829
					'classes'  => array()
830
				),
831
				array(
832
					'id'       => - 1,
833
					'url'      => 'https://jetpackme.files.wordpress.com/2014/08/videopresswedding.jpg?w=350&h=200&crop=1',
834
					'url_meta' => array(
835
						'origin'   => 0,
836
						'position' => 0
837
					),
838
					'title'    => esc_html__( 'Upgrade Focus, VideoPress for weddings', 'jetpack' ),
839
					'date'     => $date_now,
840
					'format'   => false,
841
					'excerpt'  => esc_html__( 'Weddings are in the spotlight now with VideoPress for weddings.' ),
842
					'rel'      => 'nofollow',
843
					'context'  => esc_html__( 'In "Mobile"', 'jetpack' ),
844
					'img'      => array(
845
						'src'    => 'https://jetpackme.files.wordpress.com/2014/08/videopresswedding.jpg?w=350&h=200&crop=1',
846
						'width'  => 350,
847
						'height' => 200
848
					),
849
					'classes'  => array()
850
				),
851
			);
852
			$options['size'] = 3;
853
		} else {
854
			$related_posts = $this->get_for_post_id(
855
				get_the_ID(),
856
				array(
857
					'exclude_post_ids' => $excludes,
858
				)
859
			);
860
		}
861
862
		$response = array(
863
			'version' => self::VERSION,
864
			'show_thumbnails' => (bool) $options['show_thumbnails'],
865
			'show_date' => (bool) $options['show_date'],
866
			'show_context' => (bool) $options['show_context'],
867
			'layout' => (string) $options['layout'],
868
			'items' => array(),
869
		);
870
871
		if ( count( $related_posts ) == $options['size'] )
872
			$response['items'] = $related_posts;
873
874
		echo json_encode( $response );
875
876
		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...
877
	}
878
879
	/**
880
	 * Returns a UTF-8 encoded array of post information for the given post_id
881
	 *
882
	 * @param int $post_id
883
	 * @param int $position
884
	 * @param int $origin The post id that this is related to
885
	 * @uses get_post, get_permalink, remove_query_arg, get_post_format, apply_filters
886
	 * @return array
887
	 */
888
	protected function _get_related_post_data_for_post( $post_id, $position, $origin ) {
889
		$post = get_post( $post_id );
890
891
		return array(
892
			'id' => $post->ID,
893
			'url' => get_permalink( $post->ID ),
894
			'url_meta' => array( 'origin' => $origin, 'position' => $position ),
895
			'title' => $this->_to_utf8( $this->_get_title( $post->post_title, $post->post_content ) ),
896
			'date' => get_the_date( '', $post->ID ),
897
			'format' => get_post_format( $post->ID ),
898
			'excerpt' => html_entity_decode( $this->_to_utf8( $this->_get_excerpt( $post->post_excerpt, $post->post_content ) ), ENT_QUOTES, 'UTF-8' ),
899
			/**
900
			 * Filters the rel attribute for the Related Posts' links.
901
			 *
902
			 * @module related-posts
903
			 *
904
			 * @since 3.7.0
905
			 *
906
			 * @param string nofollow Link rel attribute for Related Posts' link. Default is nofollow.
907
			 * @param int $post->ID Post ID.
908
			 */
909
			'rel' => apply_filters( 'jetpack_relatedposts_filter_post_link_rel', 'nofollow', $post->ID ),
910
			/**
911
			 * Filter the context displayed below each Related Post.
912
			 *
913
			 * @module related-posts
914
			 *
915
			 * @since 3.0.0
916
			 *
917
			 * @param string $this->_to_utf8( $this->_generate_related_post_context( $post->ID ) ) Context displayed below each related post.
918
			 * @param string $post_id Post ID of the post for which we are retrieving Related Posts.
919
			 */
920
			'context' => apply_filters(
921
				'jetpack_relatedposts_filter_post_context',
922
				$this->_to_utf8( $this->_generate_related_post_context( $post->ID ) ),
923
				$post->ID
924
			),
925
			'img' => $this->_generate_related_post_image_params( $post->ID ),
926
			/**
927
			 * Filter the post css classes added on HTML markup.
928
			 *
929
			 * @module related-posts
930
			 *
931
			 * @since 3.8.0
932
			 *
933
			 * @param array array() CSS classes added on post HTML markup.
934
			 * @param string $post_id Post ID.
935
			 */
936
			'classes' => apply_filters(
937
				'jetpack_relatedposts_filter_post_css_classes',
938
				array(),
939
				$post->ID
940
			),
941
		);
942
	}
943
944
	/**
945
	 * Returns either the title or a small excerpt to use as title for post.
946
	 *
947
	 * @param string $post_title
948
	 * @param string $post_content
949
	 * @uses strip_shortcodes, wp_trim_words, __
950
	 * @return string
951
	 */
952
	protected function _get_title( $post_title, $post_content ) {
953
		if ( ! empty( $post_title ) ) {
954
			return wp_strip_all_tags( $post_title );
955
		}
956
957
		$post_title = wp_trim_words( wp_strip_all_tags( strip_shortcodes( $post_content ) ), 5, '…' );
958
		if ( ! empty( $post_title ) ) {
959
			return $post_title;
960
		}
961
962
		return __( 'Untitled Post', 'jetpack' );
963
	}
964
965
	/**
966
	 * Returns a plain text post excerpt for title attribute of links.
967
	 *
968
	 * @param string $post_excerpt
969
	 * @param string $post_content
970
	 * @uses strip_shortcodes, wp_strip_all_tags, wp_trim_words
971
	 * @return string
972
	 */
973
	protected function _get_excerpt( $post_excerpt, $post_content ) {
974
		if ( empty( $post_excerpt ) )
975
			$excerpt = $post_content;
976
		else
977
			$excerpt = $post_excerpt;
978
979
		return wp_trim_words( wp_strip_all_tags( strip_shortcodes( $excerpt ) ), 50, '…' );
980
	}
981
982
	/**
983
	 * Generates the thumbnail image to be used for the post. Uses the
984
	 * image as returned by Jetpack_PostImages::get_image()
985
	 *
986
	 * @param int $post_id
987
	 * @uses self::get_options, apply_filters, Jetpack_PostImages::get_image, Jetpack_PostImages::fit_image_url
988
	 * @return string
989
	 */
990
	protected function _generate_related_post_image_params( $post_id ) {
991
		$options = $this->get_options();
992
		$image_params = array(
993
			'src' => '',
994
			'width' => 0,
995
			'height' => 0,
996
		);
997
998
		if ( ! $options['show_thumbnails'] ) {
999
			return $image_params;
1000
		}
1001
1002
		/**
1003
		 * Filter the size of the Related Posts images.
1004
		 *
1005
		 * @module related-posts
1006
		 *
1007
		 * @since 2.8.0
1008
		 *
1009
		 * @param array array( 'width' => 350, 'height' => 200 ) Size of the images displayed below each Related Post.
1010
		 */
1011
		$thumbnail_size = apply_filters(
1012
			'jetpack_relatedposts_filter_thumbnail_size',
1013
			array( 'width' => 350, 'height' => 200 )
1014
		);
1015
		if ( !is_array( $thumbnail_size ) ) {
1016
			$thumbnail_size = array(
1017
				'width' => (int)$thumbnail_size,
1018
				'height' => (int)$thumbnail_size
1019
			);
1020
		}
1021
1022
		// Try to get post image
1023
		if ( class_exists( 'Jetpack_PostImages' ) ) {
1024
			$img_url = '';
1025
			$post_image = Jetpack_PostImages::get_image(
1026
				$post_id,
1027
				$thumbnail_size
1028
			);
1029
1030
			if ( is_array($post_image) ) {
1031
				$img_url = $post_image['src'];
1032
			} elseif ( class_exists( 'Jetpack_Media_Summary' ) ) {
1033
				$media = Jetpack_Media_Summary::get( $post_id );
1034
1035
				if ( is_array($media) && !empty( $media['image'] ) ) {
1036
					$img_url = $media['image'];
1037
				}
1038
			}
1039
1040
			if ( !empty( $img_url ) ) {
1041
				$image_params['width'] = $thumbnail_size['width'];
1042
				$image_params['height'] = $thumbnail_size['height'];
1043
				$image_params['src'] = Jetpack_PostImages::fit_image_url(
1044
					$img_url,
1045
					$thumbnail_size['width'],
1046
					$thumbnail_size['height']
1047
				);
1048
			}
1049
		}
1050
1051
		return $image_params;
1052
	}
1053
1054
	/**
1055
	 * Returns the string UTF-8 encoded
1056
	 *
1057
	 * @param string $text
1058
	 * @return string
1059
	 */
1060
	protected function _to_utf8( $text ) {
1061
		if ( $this->_convert_charset ) {
1062
			return iconv( $this->_blog_charset, 'UTF-8', $text );
1063
		} else {
1064
			return $text;
1065
		}
1066
	}
1067
1068
	/**
1069
	 * =============================================
1070
	 * PROTECTED UTILITY FUNCTIONS EXTENDED BY WPCOM
1071
	 * =============================================
1072
	 */
1073
1074
	/**
1075
	 * Workhorse method to return array of related posts matched by ElasticSearch.
1076
	 *
1077
	 * @param int $post_id
1078
	 * @param int $size
1079
	 * @param array $filters
1080
	 * @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
1081
	 * @return array
1082
	 */
1083
	protected function _get_related_posts( $post_id, $size, array $filters ) {
1084
		$hits = $this->_filter_non_public_posts(
1085
			$this->_get_related_post_ids(
1086
				$post_id,
1087
				$size,
1088
				$filters
1089
			)
1090
		);
1091
1092
		/**
1093
		 * Filter the Related Posts matched by ElasticSearch.
1094
		 *
1095
		 * @module related-posts
1096
		 *
1097
		 * @since 2.9.0
1098
		 *
1099
		 * @param array $hits Array of Post IDs matched by ElasticSearch.
1100
		 * @param string $post_id Post ID of the post for which we are retrieving Related Posts.
1101
		 */
1102
		$hits = apply_filters( 'jetpack_relatedposts_filter_hits', $hits, $post_id );
1103
1104
		$related_posts = array();
1105
		foreach ( $hits as $i => $hit ) {
1106
			$related_posts[] = $this->_get_related_post_data_for_post( $hit['id'], $i, $post_id );
1107
		}
1108
		return $related_posts;
1109
	}
1110
1111
	/**
1112
	 * Get array of related posts matched by ElasticSearch.
1113
	 *
1114
	 * @param int $post_id
1115
	 * @param int $size
1116
	 * @param array $filters
1117
	 * @uses wp_remote_post, is_wp_error, wp_remote_retrieve_body, get_post_meta, update_post_meta
1118
	 * @return array
1119
	 */
1120
	protected function _get_related_post_ids( $post_id, $size, array $filters ) {
1121
		$now_ts = time();
1122
		$cache_meta_key = '_jetpack_related_posts_cache';
1123
1124
		$body = array(
1125
			'size' => (int) $size,
1126
		);
1127
1128
		if ( !empty( $filters ) )
1129
			$body['filter'] = array( 'and' => $filters );
1130
1131
		// Build cache key
1132
		$cache_key = md5( serialize( $body ) );
1133
1134
		// Load all cached values
1135
		if ( wp_using_ext_object_cache() ) {
1136
			$transient_name = "{$cache_meta_key}_{$cache_key}_{$post_id}";
1137
			$cache = get_transient( $transient_name );
1138
			if ( false !== $cache ) {
1139
				return $cache;
1140
			}
1141
		} else {
1142
			$cache = get_post_meta( $post_id, $cache_meta_key, true );
1143
1144
			if ( empty( $cache ) )
1145
				$cache = array();
1146
1147
1148
			// Cache is valid! Return cached value.
1149
			if ( isset( $cache[ $cache_key ] ) && is_array( $cache[ $cache_key ] ) && $cache[ $cache_key ][ 'expires' ] > $now_ts ) {
1150
				return $cache[ $cache_key ][ 'payload' ];
1151
			}
1152
		}
1153
1154
		$response = wp_remote_post(
1155
			"https://public-api.wordpress.com/rest/v1/sites/{$this->_blog_id_wpcom}/posts/$post_id/related/",
1156
			array(
1157
				'timeout' => 10,
1158
				'user-agent' => 'jetpack_related_posts',
1159
				'sslverify' => true,
1160
				'body' => $body,
1161
			)
1162
		);
1163
1164
		// Oh no... return nothing don't cache errors.
1165
		if ( is_wp_error( $response ) ) {
1166
			if ( isset( $cache[ $cache_key ] ) && is_array( $cache[ $cache_key ] ) )
1167
				return $cache[ $cache_key ][ 'payload' ]; // return stale
1168
			else
1169
				return array();
1170
		}
1171
1172
		$results = json_decode( wp_remote_retrieve_body( $response ), true );
1173
		$related_posts = array();
1174
		if ( is_array( $results ) && !empty( $results['hits'] ) ) {
1175
			foreach( $results['hits'] as $hit ) {
1176
				$related_posts[] = array(
1177
					'id' => $hit['fields']['post_id'],
1178
				);
1179
			}
1180
		}
1181
1182
		// An empty array might indicate no related posts or that posts
1183
		// are not yet synced to WordPress.com, so we cache for only 1
1184
		// minute in this case
1185
		if ( empty( $related_posts ) ) {
1186
			$cache_ttl = 60;
1187
		} else {
1188
			$cache_ttl = 12 * HOUR_IN_SECONDS;
1189
		}
1190
1191
		// Update cache
1192
		if ( wp_using_ext_object_cache() ) {
1193
			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...
1194
		} else {
1195
			// Copy all valid cache values
1196
			$new_cache = array();
1197
			foreach ( $cache as $k => $v ) {
1198
				if ( is_array( $v ) && $v[ 'expires' ] > $now_ts ) {
1199
					$new_cache[ $k ] = $v;
1200
				}
1201
			}
1202
1203
			// Set new cache value
1204
			$cache_expires = $cache_ttl + $now_ts;
1205
			$new_cache[ $cache_key ] = array(
1206
				'expires' => $cache_expires,
1207
				'payload' => $related_posts,
1208
			);
1209
			update_post_meta( $post_id, $cache_meta_key, $new_cache );
1210
		}
1211
1212
		return $related_posts;
1213
	}
1214
1215
	/**
1216
	 * Filter out any hits that are not public anymore.
1217
	 *
1218
	 * @param array $related_posts
1219
	 * @uses get_post_stati, get_post_status
1220
	 * @return array
1221
	 */
1222
	protected function _filter_non_public_posts( array $related_posts ) {
1223
		$public_stati = get_post_stati( array( 'public' => true ) );
1224
1225
		$filtered = array();
1226
		foreach ( $related_posts as $hit ) {
1227
			if ( in_array( get_post_status( $hit['id'] ), $public_stati ) ) {
1228
				$filtered[] = $hit;
1229
			}
1230
		}
1231
		return $filtered;
1232
	}
1233
1234
	/**
1235
	 * Generates a context for the related content (second line in related post output).
1236
	 * Order of importance:
1237
	 *   - First category (Not 'Uncategorized')
1238
	 *   - First post tag
1239
	 *   - Number of comments
1240
	 *
1241
	 * @param int $post_id
1242
	 * @uses get_the_category, get_the_terms, get_comments_number, number_format_i18n, __, _n
1243
	 * @return string
1244
	 */
1245
	protected function _generate_related_post_context( $post_id ) {
1246
		$categories = get_the_category( $post_id );
1247 View Code Duplication
		if ( is_array( $categories ) ) {
1248
			foreach ( $categories as $category ) {
1249
				if ( 'uncategorized' != $category->slug && '' != trim( $category->name ) ) {
1250
					$post_cat_context = sprintf(
1251
						_x( 'In "%s"', 'in {category/tag name}', 'jetpack' ),
1252
						$category->name
1253
					);
1254
					/**
1255
					 * Filter the "In Category" line displayed in the post context below each Related Post.
1256
					 *
1257
					 * @module related-posts
1258
					 *
1259
					 * @since 3.2.0
1260
					 *
1261
					 * @param string $post_cat_context "In Category" line displayed in the post context below each Related Post.
1262
					 * @param array $category Array containing information about the category.
1263
					 */
1264
					return apply_filters( 'jetpack_relatedposts_post_category_context', $post_cat_context, $category );
1265
				}
1266
			}
1267
		}
1268
1269
		$tags = get_the_terms( $post_id, 'post_tag' );
1270 View Code Duplication
		if ( is_array( $tags ) ) {
1271
			foreach ( $tags as $tag ) {
1272
				if ( '' != trim( $tag->name ) ) {
1273
					$post_tag_context = sprintf(
1274
						_x( 'In "%s"', 'in {category/tag name}', 'jetpack' ),
1275
						$tag->name
1276
					);
1277
					/**
1278
					 * Filter the "In Tag" line displayed in the post context below each Related Post.
1279
					 *
1280
					 * @module related-posts
1281
					 *
1282
					 * @since 3.2.0
1283
					 *
1284
					 * @param string $post_tag_context "In Tag" line displayed in the post context below each Related Post.
1285
					 * @param array $tag Array containing information about the tag.
1286
					 */
1287
					return apply_filters( 'jetpack_relatedposts_post_tag_context', $post_tag_context, $tag );
1288
				}
1289
			}
1290
		}
1291
1292
		$comment_count = get_comments_number( $post_id );
1293
		if ( $comment_count > 0 ) {
1294
			return sprintf(
1295
				_n( 'With 1 comment', 'With %s comments', $comment_count, 'jetpack' ),
1296
				number_format_i18n( $comment_count )
1297
			);
1298
		}
1299
1300
		return __( 'Similar post', 'jetpack' );
1301
	}
1302
1303
	/**
1304
	 * Logs clicks for clickthrough analysis and related result tuning.
1305
	 *
1306
	 * @return null
1307
	 */
1308
	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...
1309
1310
	}
1311
1312
	/**
1313
	 * Determines if the current post is able to use related posts.
1314
	 *
1315
	 * @uses self::get_options, is_admin, is_single, apply_filters
1316
	 * @return bool
1317
	 */
1318
	protected function _enabled_for_request() {
1319
		// Default to enabled
1320
		$enabled = true;
1321
1322
		// Must have feature enabled
1323
		$options = $this->get_options();
1324
		if ( ! $options['enabled'] ) {
1325
			$enabled = false;
1326
		}
1327
1328
		// Only run for frontend pages
1329
		if ( is_admin() ) {
1330
			$enabled = false;
1331
		}
1332
1333
		// Only run for standalone posts
1334
		if ( ! is_single() ) {
1335
			$enabled = false;
1336
		}
1337
1338
		/**
1339
		 * Filter the Enabled value to allow related posts to be shown on pages as well.
1340
		 *
1341
		 * @module related-posts
1342
		 *
1343
		 * @since 3.3.0
1344
		 *
1345
		 * @param bool $enabled Should Related Posts be enabled on the current page.
1346
		 */
1347
		return apply_filters( 'jetpack_relatedposts_filter_enabled_for_request', $enabled );
1348
	}
1349
1350
	/**
1351
	 * Adds filters and enqueues assets.
1352
	 *
1353
	 * @uses self::_enqueue_assets, self::_setup_shortcode, add_filter
1354
	 * @return null
1355
	 */
1356
	protected function _action_frontend_init_page() {
1357
		$this->_enqueue_assets( true, true );
1358
		$this->_setup_shortcode();
1359
1360
		add_filter( 'the_content', array( $this, 'filter_add_target_to_dom' ), 40 );
1361
	}
1362
1363
	/**
1364
	 * Enqueues assets needed to do async loading of related posts.
1365
	 *
1366
	 * @uses wp_enqueue_script, wp_enqueue_style, plugins_url
1367
	 * @return null
1368
	 */
1369
	protected function _enqueue_assets( $script, $style ) {
1370
		$dependencies = is_customize_preview() ? array( 'customize-base' ) : array( 'jquery' );
1371
		if ( $script ) {
1372
			wp_enqueue_script( 'jetpack_related-posts', plugins_url( 'related-posts.js', __FILE__ ), $dependencies, self::VERSION );
1373
			$related_posts_js_options = array(
1374
				/**
1375
				 * Filter each Related Post Heading structure.
1376
				 *
1377
				 * @since 4.0.0
1378
				 *
1379
				 * @param string $str Related Post Heading structure. Default to h4.
1380
				 */
1381
				'post_heading' => apply_filters( 'jetpack_relatedposts_filter_post_heading', esc_attr( 'h4' ) ),
1382
			);
1383
			wp_localize_script( 'jetpack_related-posts', 'related_posts_js_options', $related_posts_js_options );
1384
		}
1385
		if ( $style ){
1386
			if( is_rtl() ) {
1387
				wp_enqueue_style( 'jetpack_related-posts', plugins_url( 'rtl/related-posts-rtl.css', __FILE__ ), array(), self::VERSION );
1388
			} else {
1389
				wp_enqueue_style( 'jetpack_related-posts', plugins_url( 'related-posts.css', __FILE__ ), array(), self::VERSION );
1390
			}
1391
		}
1392
	}
1393
1394
	/**
1395
	 * Sets up the shortcode processing.
1396
	 *
1397
	 * @uses add_filter, add_shortcode
1398
	 * @return null
1399
	 */
1400
	protected function _setup_shortcode() {
1401
		add_filter( 'the_content', array( $this, 'test_for_shortcode' ), 0 );
1402
1403
		add_shortcode( self::SHORTCODE, array( $this, 'get_target_html' ) );
1404
	}
1405
1406
	protected function _allow_feature_toggle() {
1407
		if ( null === $this->_allow_feature_toggle ) {
1408
			/**
1409
			 * Filter the display of the Related Posts toggle in Settings > Reading.
1410
			 *
1411
			 * @module related-posts
1412
			 *
1413
			 * @since 2.8.0
1414
			 *
1415
			 * @param bool false Display a feature toggle. Default to false.
1416
			 */
1417
			$this->_allow_feature_toggle = apply_filters( 'jetpack_relatedposts_filter_allow_feature_toggle', false );
1418
		}
1419
		return $this->_allow_feature_toggle;
1420
	}
1421
}
1422
1423
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...
1424
	protected $_query_name;
1425
1426
	/**
1427
	 * Allows callers of this class to tag each query with a unique name for tracking purposes.
1428
	 *
1429
	 * @param string $name
1430
	 * @return Jetpack_RelatedPosts_Raw
1431
	 */
1432
	public function set_query_name( $name ) {
1433
		$this->_query_name = (string) $name;
1434
		return $this;
1435
	}
1436
1437
	/**
1438
	 * The raw related posts class can be used by other plugins or themes
1439
	 * to get related content. This class wraps the existing RelatedPosts
1440
	 * logic thus we never want to add anything to the DOM or do anything
1441
	 * for event hooks. We will also not present any settings for this
1442
	 * class and keep it enabled as calls to this class is done
1443
	 * programmatically.
1444
	 */
1445
	public function action_admin_init() {}
1446
	public function action_frontend_init() {}
1447
	public function get_options() {
1448
		return array(
1449
			'enabled' => true,
1450
		);
1451
	}
1452
1453
	/**
1454
	 * Workhorse method to return array of related posts ids matched by ElasticSearch.
1455
	 *
1456
	 * @param int $post_id
1457
	 * @param int $size
1458
	 * @param array $filters
1459
	 * @uses wp_remote_post, is_wp_error, wp_remote_retrieve_body
1460
	 * @return array
1461
	 */
1462
	protected function _get_related_posts( $post_id, $size, array $filters ) {
1463
		$hits = $this->_filter_non_public_posts(
1464
			$this->_get_related_post_ids(
1465
				$post_id,
1466
				$size,
1467
				$filters
1468
			)
1469
		);
1470
1471
		/** This filter is already documented in modules/related-posts/related-posts.php */
1472
		$hits = apply_filters( 'jetpack_relatedposts_filter_hits', $hits, $post_id );
1473
1474
		return $hits;
1475
	}
1476
}
1477