Jetpack_Top_Posts_Widget::widget()   F
last analyzed

Complexity

Conditions 36
Paths > 20000

Size

Total Lines 277

Duplication

Lines 5
Ratio 1.81 %

Importance

Changes 0
Metric Value
cc 36
nc 337920
nop 2
dl 5
loc 277
rs 0
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
3
/*
4
 * Currently, this widget depends on the Stats Module. To not load this file
5
 * when the Stats Module is not active would potentially bypass Jetpack's
6
 * fatal error detection on module activation, so we always load this file.
7
 * Instead, we don't register the widget if the Stats Module isn't active.
8
 */
9
10
use Automattic\Jetpack\Redirect;
11
12
/**
13
 * Register the widget for use in Appearance -> Widgets
14
 */
15
add_action( 'widgets_init', 'jetpack_top_posts_widget_init' );
16
17
function jetpack_top_posts_widget_init() {
18
	// Currently, this widget depends on the Stats Module
19
	if (
20
		( ! defined( 'IS_WPCOM' ) || ! IS_WPCOM )
21
	&&
22
		! function_exists( 'stats_get_from_restapi' )
23
	) {
24
		return;
25
	}
26
27
	register_widget( 'Jetpack_Top_Posts_Widget' );
28
}
29
30
class Jetpack_Top_Posts_Widget extends WP_Widget {
31
	public $alt_option_name = 'widget_stats_topposts';
32
	public $default_title   = '';
33
34
	function __construct() {
35
		parent::__construct(
36
			'top-posts',
37
			/** This filter is documented in modules/widgets/facebook-likebox.php */
38
			apply_filters( 'jetpack_widget_name', __( 'Top Posts &amp; Pages', 'jetpack' ) ),
39
			array(
40
				'description'                 => __( 'Shows your most viewed posts and pages.', 'jetpack' ),
41
				'customize_selective_refresh' => true,
42
			)
43
		);
44
45
		$this->default_title = __( 'Top Posts &amp; Pages', 'jetpack' );
46
47
		if ( is_active_widget( false, false, $this->id_base ) || is_customize_preview() ) {
48
			add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_style' ) );
49
		}
50
51
		/**
52
		 * Add explanation about how the statistics are calculated.
53
		 *
54
		 * @module widgets
55
		 *
56
		 * @since 3.9.3
57
		 */
58
		add_action( 'jetpack_widget_top_posts_after_fields', array( $this, 'stats_explanation' ) );
59
	}
60
61
	function enqueue_style() {
62
		wp_register_style( 'jetpack-top-posts-widget', plugins_url( 'top-posts/style.css', __FILE__ ), array(), '20141013' );
63
		wp_enqueue_style( 'jetpack-top-posts-widget' );
64
	}
65
66
	function form( $instance ) {
67
		$instance = wp_parse_args( (array) $instance, $this->defaults() );
0 ignored issues
show
Documentation introduced by
$this->defaults() is of type array<string,?,{"title":...g","display":"string"}>, but the function expects a string.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
68
69
		if ( false === $instance['title'] ) {
70
			$instance['title'] = $this->default_title;
71
		}
72
		$title = stripslashes( $instance['title'] );
73
74
		$count = isset( $instance['count'] ) ? (int) $instance['count'] : 10;
75
		if ( $count < 1 || 10 < $count ) {
76
			$count = 10;
77
		}
78
79
		$allowed_post_types = array_values( get_post_types( array( 'public' => true ) ) );
80
		$types              = isset( $instance['types'] ) ? (array) $instance['types'] : array( 'post', 'page' );
81
82
		// 'likes' are not available in Jetpack
83
		$ordering = isset( $instance['ordering'] ) && 'likes' === $instance['ordering'] ? 'likes' : 'views';
84
85 View Code Duplication
		if ( isset( $instance['display'] ) && in_array( $instance['display'], array( 'grid', 'list', 'text' ) ) ) {
86
			$display = $instance['display'];
87
		} else {
88
			$display = 'text';
89
		}
90
91
		?>
92
93
		<p>
94
			<label for="<?php echo $this->get_field_id( 'title' ); ?>"><?php esc_html_e( 'Title:', 'jetpack' ); ?></label>
95
			<input class="widefat" id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>" />
96
		</p>
97
98
		<p>
99
			<label for="<?php echo $this->get_field_id( 'count' ); ?>"><?php esc_html_e( 'Maximum number of posts to show (no more than 10):', 'jetpack' ); ?></label>
100
			<input id="<?php echo $this->get_field_id( 'count' ); ?>" name="<?php echo $this->get_field_name( 'count' ); ?>" type="number" value="<?php echo (int) $count; ?>" min="1" max="10" />
101
		</p>
102
103
		<?php if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) : ?>
104
		<p>
105
			<label><?php esc_html_e( 'Order Top Posts &amp; Pages By:', 'jetpack' ); ?></label>
106
			<ul>
107
				<li><label><input id="<?php echo $this->get_field_id( 'ordering' ); ?>-likes" name="<?php echo $this->get_field_name( 'ordering' ); ?>" type="radio" value="likes" <?php checked( 'likes', $ordering ); ?> /> <?php esc_html_e( 'Likes', 'jetpack' ); ?></label></li>
108
				<li><label><input id="<?php echo $this->get_field_id( 'ordering' ); ?>-views" name="<?php echo $this->get_field_name( 'ordering' ); ?>" type="radio" value="views" <?php checked( 'views', $ordering ); ?> /> <?php esc_html_e( 'Views', 'jetpack' ); ?></label></li>
109
			</ul>
110
		</p>
111
		<?php endif; ?>
112
113
		<p>
114
			<label for="<?php echo $this->get_field_id( 'types' ); ?>"><?php esc_html_e( 'Types of pages to display:', 'jetpack' ); ?></label>
115
			<ul>
116
				<?php
117
				foreach ( $allowed_post_types as $type ) {
118
					// Get the Post Type name to display next to the checkbox
119
					$post_type_object = get_post_type_object( $type );
120
					$label            = $post_type_object->labels->name;
121
122
					$checked = '';
123
					if ( in_array( $type, $types ) ) {
124
						$checked = 'checked="checked" ';
125
					}
126
					?>
127
128
					<li><label>
129
						<input value="<?php echo esc_attr( $type ); ?>" name="<?php echo $this->get_field_name( 'types' ); ?>[]" id="<?php echo $this->get_field_id( 'types' ); ?>-<?php echo $type; ?>" type="checkbox" <?php echo $checked; ?>>
130
						<?php echo esc_html( $label ); ?>
131
					</label></li>
132
133
				<?php } // End foreach ?>
134
			</ul>
135
		</p>
136
137
		<p>
138
			<label><?php esc_html_e( 'Display as:', 'jetpack' ); ?></label>
139
			<ul>
140
				<li><label><input id="<?php echo $this->get_field_id( 'display' ); ?>-text" name="<?php echo $this->get_field_name( 'display' ); ?>" type="radio" value="text" <?php checked( 'text', $display ); ?> /> <?php esc_html_e( 'Text List', 'jetpack' ); ?></label></li>
141
				<li><label><input id="<?php echo $this->get_field_id( 'display' ); ?>-list" name="<?php echo $this->get_field_name( 'display' ); ?>" type="radio" value="list" <?php checked( 'list', $display ); ?> /> <?php esc_html_e( 'Image List', 'jetpack' ); ?></label></li>
142
				<li><label><input id="<?php echo $this->get_field_id( 'display' ); ?>-grid" name="<?php echo $this->get_field_name( 'display' ); ?>" type="radio" value="grid" <?php checked( 'grid', $display ); ?> /> <?php esc_html_e( 'Image Grid', 'jetpack' ); ?></label></li>
143
			</ul>
144
		</p>
145
		<?php
146
147
		/**
148
		 * Fires after the fields are displayed in the Top Posts Widget settings in wp-admin.
149
		 *
150
		 * Allow adding extra content after the fields are displayed.
151
		 *
152
		 * @module widgets
153
		 *
154
		 * @since 3.9.3
155
		 *
156
		 * @param array $args {
157
		 *     @param array $instance The widget instance.
158
		 *     @param object $this The class object.
159
		 * }
160
		 */
161
		do_action( 'jetpack_widget_top_posts_after_fields', array( $instance, $this ) );
162
	}
163
164
	/**
165
	 * Explains how the statics are calculated.
166
	 */
167
	function stats_explanation() {
168
		?>
169
170
		<p><?php esc_html_e( 'Top Posts &amp; Pages by views are calculated from 24-48 hours of stats. They take a while to change.', 'jetpack' ); ?></p>
171
								<?php
172
	}
173
174
	function update( $new_instance, $old_instance ) {
175
		$instance          = array();
176
		$instance['title'] = wp_kses( $new_instance['title'], array() );
177
		if ( $instance['title'] === $this->default_title ) {
178
			$instance['title'] = false; // Store as false in case of language change
179
		}
180
181
		$instance['count'] = (int) $new_instance['count'];
182
		if ( $instance['count'] < 1 || 10 < $instance['count'] ) {
183
			$instance['count'] = 10;
184
		}
185
186
		// 'likes' are not available in Jetpack
187
		$instance['ordering'] = isset( $new_instance['ordering'] ) && 'likes' == $new_instance['ordering'] ? 'likes' : 'views';
188
189
		$allowed_post_types = array_values( get_post_types( array( 'public' => true ) ) );
190
		$instance['types']  = $new_instance['types'];
191
		foreach ( $new_instance['types'] as $key => $type ) {
192
			if ( ! in_array( $type, $allowed_post_types ) ) {
193
				unset( $new_instance['types'][ $key ] );
194
			}
195
		}
196
197 View Code Duplication
		if ( isset( $new_instance['display'] ) && in_array( $new_instance['display'], array( 'grid', 'list', 'text' ) ) ) {
198
			$instance['display'] = $new_instance['display'];
199
		} else {
200
			$instance['display'] = 'text';
201
		}
202
203
		/**
204
		 * Filters Top Posts Widget settings before they're saved.
205
		 *
206
		 * @module widgets
207
		 *
208
		 * @since 3.9.3
209
		 *
210
		 * @param array $instance The santized widget instance. Only contains data processed by the current widget.
211
		 * @param array $new_instance The new widget instance before sanitization.
212
		 */
213
		$instance = apply_filters( 'jetpack_top_posts_saving', $instance, $new_instance );
0 ignored issues
show
Unused Code introduced by
The call to apply_filters() has too many arguments starting with $new_instance.

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

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

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

Loading history...
214
215
		return $instance;
216
	}
217
218
	function widget( $args, $instance ) {
219
		/** This action is documented in modules/widgets/gravatar-profile.php */
220
		do_action( 'jetpack_stats_extra', 'widget_view', 'top_posts' );
221
222
		$instance = wp_parse_args( (array) $instance, $this->defaults() );
0 ignored issues
show
Documentation introduced by
$this->defaults() is of type array<string,?,{"title":...g","display":"string"}>, but the function expects a string.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
223
224
		$title = isset( $instance['title'] ) ? $instance['title'] : false;
225
		if ( false === $title ) {
226
			$title = $this->default_title;
227
		}
228
		/** This filter is documented in core/src/wp-includes/default-widgets.php */
229
		$title = apply_filters( 'widget_title', $title );
230
231
		$count = isset( $instance['count'] ) ? (int) $instance['count'] : false;
232
		if ( $count < 1 || 10 < $count ) {
233
			$count = 10;
234
		}
235
		/**
236
		 * Control the number of displayed posts.
237
		 *
238
		 * @module widgets
239
		 *
240
		 * @since 3.3.0
241
		 *
242
		 * @param string $count Number of Posts displayed in the Top Posts widget. Default is 10.
243
		 */
244
		$count = apply_filters( 'jetpack_top_posts_widget_count', $count );
245
246
		$types = isset( $instance['types'] ) ? (array) $instance['types'] : array( 'post', 'page' );
247
248
		// 'likes' are not available in Jetpack
249
		$ordering = isset( $instance['ordering'] ) && 'likes' == $instance['ordering'] ? 'likes' : 'views';
250
251 View Code Duplication
		if ( isset( $instance['display'] ) && in_array( $instance['display'], array( 'grid', 'list', 'text' ) ) ) {
252
			$display = $instance['display'];
253
		} else {
254
			$display = 'text';
255
		}
256
257
		if ( 'text' != $display ) {
258
			$get_image_options = array(
259
				'fallback_to_avatars' => true,
260
				/** This filter is documented in modules/stats.php */
261
				'gravatar_default'    => apply_filters( 'jetpack_static_url', set_url_scheme( 'https://en.wordpress.com/i/logo/white-gray-80.png' ) ),
262
				'avatar_size'         => 40,
263
				'width'               => null,
264
				'height'              => null,
265
			);
266
			if ( 'grid' == $display ) {
267
				$get_image_options['avatar_size'] = 200;
268
			}
269
			/**
270
			 * Top Posts Widget Image options.
271
			 *
272
			 * @module widgets
273
			 *
274
			 * @since 1.8.0
275
			 *
276
			 * @param array $get_image_options {
277
			 * Array of Image options.
278
			 * @type bool true Should we default to Gravatars when no image is found? Default is true.
279
			 * @type string $gravatar_default Default Image URL if no Gravatar is found.
280
			 * @type int $avatar_size Default Image size.
281
			 * @type mixed $width Image width, not set by default and $avatar_size is used instead.
282
			 * @type mixed $height Image height, not set by default and $avatar_size is used instead.
283
			 * }
284
			 */
285
			$get_image_options = apply_filters( 'jetpack_top_posts_widget_image_options', $get_image_options );
286
		}
287
288
		if ( function_exists( 'wpl_get_blogs_most_liked_posts' ) && 'likes' == $ordering ) {
289
			$posts = $this->get_by_likes( $count, $types );
290
		} else {
291
			$posts = $this->get_by_views( $count, $args, $types );
292
		}
293
294
		if ( ! $posts ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $posts of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
295
			$posts = $this->get_fallback_posts( $count, $types );
296
		}
297
298
		echo $args['before_widget'];
299
		if ( ! empty( $title ) ) {
300
			echo $args['before_title'] . $title . $args['after_title'];
301
		}
302
303
		if ( ! $posts ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $posts of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
304
			$link = esc_url( Redirect::get_url( 'jetpack-support-getting-more-views-and-traffic' ) );
305
			if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
306
				$link = 'https://en.support.wordpress.com/getting-more-site-traffic/';
307
			}
308
309
			if ( current_user_can( 'edit_theme_options' ) ) {
310
				echo '<p>' . sprintf(
311
					__( 'There are no posts to display. <a href="%s" target="_blank">Want more traffic?</a>', 'jetpack' ),
312
					esc_url( $link )
313
				) . '</p>';
314
			}
315
316
			echo $args['after_widget'];
317
			return;
318
		}
319
320
		/**
321
		 * Filter the layout of the Top Posts Widget
322
		 *
323
		 * @module widgets
324
		 *
325
		 * @since 6.4.0
326
		 *
327
		 * @param string $layout layout of the Top Posts Widget (empty string)
328
		 * @param array $posts IDs of the posts to be displayed
329
		 * @param array $display Display option from widget form
330
		 */
331
		$layout = apply_filters( 'jetpack_top_posts_widget_layout', '', $posts, $display );
0 ignored issues
show
Unused Code introduced by
The call to apply_filters() has too many arguments starting with $posts.

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

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

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

Loading history...
332
		if ( ! empty( $layout ) ) {
333
			echo $layout;
334
			echo $args['after_widget'];
335
			return;
336
		}
337
338
		switch ( $display ) {
339
			case 'list':
340
			case 'grid':
341
				// Keep the avatar_size as default dimensions for backward compatibility.
342
				$width  = (int) $get_image_options['avatar_size'];
0 ignored issues
show
Bug introduced by
The variable $get_image_options 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...
343
				$height = (int) $get_image_options['avatar_size'];
344
345
				// Check if the user has changed the width.
346
				if ( ! empty( $get_image_options['width'] ) ) {
347
					$width = (int) $get_image_options['width'];
348
				}
349
350
				// Check if the user has changed the height.
351
				if ( ! empty( $get_image_options['height'] ) ) {
352
					$height = (int) $get_image_options['height'];
353
				}
354
355
				foreach ( $posts as &$post ) {
356
					$image         = Jetpack_PostImages::get_image(
357
						$post['post_id'],
358
						array(
359
							'fallback_to_avatars' => (bool) $get_image_options['fallback_to_avatars'],
360
							'width'               => (int) $width,
361
							'height'              => (int) $height,
362
							'avatar_size'         => (int) $get_image_options['avatar_size'],
363
						)
364
					);
365
					$post['image'] = $image['src'];
366
					if ( 'blavatar' != $image['from'] && 'gravatar' != $image['from'] ) {
367
						$post['image'] = jetpack_photon_url( $post['image'], array( 'resize' => "$width,$height" ) );
368
					}
369
				}
370
371
				unset( $post );
372
373
				if ( 'grid' == $display ) {
374
					echo "<div class='widgets-grid-layout no-grav'>\n";
375
					foreach ( $posts as $post ) :
376
					?>
377
					<div class="widget-grid-view-image">
378
						<?php
379
						/**
380
						 * Fires before each Top Post result, inside <li>.
381
						 *
382
						 * @module widgets
383
						 *
384
						 * @since 3.2.0
385
						 *
386
						 * @param string $post['post_id'] Post ID.
387
						 */
388
						do_action( 'jetpack_widget_top_posts_before_post', $post['post_id'] );
389
390
						/**
391
						 * Filter the permalink of items in the Top Posts widget.
392
						 *
393
						 * @module widgets
394
						 *
395
						 * @since 4.4.0
396
						 *
397
						 * @param string $post['permalink'] Post permalink.
398
						 * @param array  $post              Post array.
399
						 */
400
						$filtered_permalink = apply_filters( 'jetpack_top_posts_widget_permalink', $post['permalink'], $post );
0 ignored issues
show
Unused Code introduced by
The call to apply_filters() has too many arguments starting with $post.

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

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

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

Loading history...
401
402
						printf(
403
							'<a href="%1$s" title="%2$s" class="bump-view" data-bump-view="tp"%3$s><img width="%4$d" height="%5$d" src="%6$s" alt="%2$s" data-pin-nopin="true"/></a>',
404
							esc_url( $filtered_permalink ),
405
							esc_attr( wp_kses( $post['title'], array() ) ),
406
							( get_queried_object_id() === $post['post_id'] ? ' aria-current="page"' : '' ),
407
							absint( $width ),
408
							absint( $height ),
409
							esc_url( $post['image'] )
410
						);
411
412
						/**
413
						 * Fires after each Top Post result, inside <li>.
414
						 *
415
						 * @module widgets
416
						 *
417
						 * @since 3.2.0
418
						 *
419
						 * @param string $post['post_id'] Post ID.
420
						 */
421
						do_action( 'jetpack_widget_top_posts_after_post', $post['post_id'] );
422
						?>
423
						</div>
424
					<?php
425
					endforeach;
426
					echo "</div>\n";
427
				} else {
428
					echo "<ul class='widgets-list-layout no-grav'>\n";
429
					foreach ( $posts as $post ) :
430
					?>
431
					<li>
432
						<?php
433
						/** This action is documented in modules/widgets/top-posts.php */
434
						do_action( 'jetpack_widget_top_posts_before_post', $post['post_id'] );
435
436
						/** This filter is documented in modules/widgets/top-posts.php */
437
						$filtered_permalink = apply_filters( 'jetpack_top_posts_widget_permalink', $post['permalink'], $post );
0 ignored issues
show
Unused Code introduced by
The call to apply_filters() has too many arguments starting with $post.

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

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

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

Loading history...
438
439
						printf(
440
							'<a href="%1$s" title="%2$s" class="bump-view" data-bump-view="tp"%3$s>
441
								<img width="%4$d" height="%5$d" src="%6$s" alt="%2$s" data-pin-nopin="true" class="widgets-list-layout-blavatar"/>
442
							</a>
443
							<div class="widgets-list-layout-links">
444
								<a href="%1$s" title="%2$s" class="bump-view" data-bump-view="tp"%3$s>%7$s</a>
445
							</div>
446
							',
447
							esc_url( $filtered_permalink ),
448
							esc_attr( wp_kses( $post['title'], array() ) ),
449
							( get_queried_object_id() === $post['post_id'] ? ' aria-current="page"' : '' ),
450
							absint( $width ),
451
							absint( $height ),
452
							esc_url( $post['image'] ),
453
							esc_html( wp_kses( $post['title'], array() ) )
454
						);
455
456
						/** This action is documented in modules/widgets/top-posts.php */
457
						do_action( 'jetpack_widget_top_posts_after_post', $post['post_id'] );
458
						?>
459
						</li>
460
					<?php
461
					endforeach;
462
					echo "</ul>\n";
463
				}
464
				break;
465
			default:
466
				echo '<ul>';
467
				foreach ( $posts as $post ) :
468
				?>
469
				<li>
470
					<?php
471
					/** This action is documented in modules/widgets/top-posts.php */
472
					do_action( 'jetpack_widget_top_posts_before_post', $post['post_id'] );
473
474
					/** This filter is documented in modules/widgets/top-posts.php */
475
					$filtered_permalink = apply_filters( 'jetpack_top_posts_widget_permalink', $post['permalink'], $post );
0 ignored issues
show
Unused Code introduced by
The call to apply_filters() has too many arguments starting with $post.

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

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

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

Loading history...
476
477
					printf(
478
						'<a href="%1$s" class="bump-view" data-bump-view="tp"%2$s>%3$s</a>',
479
						esc_url( $filtered_permalink ),
480
						( get_queried_object_id() === $post['post_id'] ? ' aria-current="page"' : '' ),
481
						esc_html( wp_kses( $post['title'], array() ) )
482
					);
483
484
					/** This action is documented in modules/widgets/top-posts.php */
485
					do_action( 'jetpack_widget_top_posts_after_post', $post['post_id'] );
486
					?>
487
					</li>
488
				<?php
489
				endforeach;
490
				echo '</ul>';
491
		}
492
493
		echo $args['after_widget'];
494
	}
495
496
	public static function defaults() {
497
		return array(
498
			'title'    => esc_html__( 'Top Posts &amp; Pages', 'jetpack' ),
499
			'count'    => absint( 10 ),
500
			'types'    => array( 'post', 'page' ),
501
			'ordering' => 'views',
502
			'display'  => 'text',
503
		);
504
	}
505
506
	/**
507
	 * Get most liked posts
508
	 *
509
	 * ONLY TO BE USED IN WPCOM
510
	 *
511
	 * @since 8.4.0 Added $types param
512
	 *
513
	 * @param int   $count The maximum number of posts to be returned.
514
	 * @param array $types The post types that should be returned. Optional. Defaults to 'post' and 'page'.
515
	 *
516
	 * @return array array of posts.
517
	 */
518
	public function get_by_likes( $count, $types = array( 'post', 'page' ) ) {
519
		$post_likes = wpl_get_blogs_most_liked_posts();
520
		if ( ! $post_likes ) {
521
			return array();
522
		}
523
524
		return $this->get_posts( array_keys( $post_likes ), $count, $types );
525
	}
526
527
	/**
528
	 * Get the top posts based on views
529
	 *
530
	 * @since 8.4.0 Added $types param
531
	 *
532
	 * @param int   $count The maximum number of posts to be returned.
533
	 * @param array $args The widget arguments.
534
	 * @param array $types The post types that should be returned.
535
	 *
536
	 * @return array array of posts. Defaults to 'post' and 'page'.
537
	 */
538
	public function get_by_views( $count, $args, $types = array( 'post', 'page' ) ) {
539
		if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
540
			global $wpdb;
541
542
			$post_views = wp_cache_get( "get_top_posts_$count", 'stats' );
543
			if ( false === $post_views ) {
544
				$post_views = array_shift( stats_get_daily_history( false, get_current_blog_id(), 'postviews', 'post_id', false, 2, '', $count * 2 + 10, true ) );
0 ignored issues
show
Bug introduced by
stats_get_daily_history(... $count * 2 + 10, true) cannot be passed to array_shift() as the parameter $array expects a reference.
Loading history...
545
				unset( $post_views[0] );
546
				wp_cache_add( "get_top_posts_$count", $post_views, 'stats', 1200 );
547
			}
548
549
			return $this->get_posts( array_keys( $post_views ), $count, $types );
550
		}
551
552
		/**
553
		 * Filter the number of days used to calculate Top Posts for the Top Posts widget.
554
		 * We do not recommend accessing more than 10 days of results at one.
555
		 * When more than 10 days of results are accessed at once, results should be cached via the WordPress transients API.
556
		 * Querying for -1 days will give results for an infinite number of days.
557
		 *
558
		 * @module widgets
559
		 *
560
		 * @since 3.9.3
561
		 *
562
		 * @param int 2 Number of days. Default is 2.
563
		 * @param array $args The widget arguments.
564
		 */
565
		$days = (int) apply_filters( 'jetpack_top_posts_days', 2, $args );
0 ignored issues
show
Unused Code introduced by
The call to apply_filters() has too many arguments starting with $args.

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

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

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

Loading history...
566
567
		/** Handling situations where the number of days makes no sense - allows for unlimited days where $days = -1 */
568
		if ( 0 == $days || false == $days ) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $days of type integer to the boolean false. If you are specifically checking for 0, consider using something more explicit like === 0 instead.
Loading history...
569
			$days = 2;
570
		}
571
572
		$post_view_posts = stats_get_from_restapi( array(), 'top-posts?max=11&summarize=1&num=' . (int) $days );
573
574
		if ( ! isset( $post_view_posts->summary ) || empty( $post_view_posts->summary->postviews ) ) {
575
			return array();
576
		}
577
578
		$post_view_ids = array_filter( wp_list_pluck( $post_view_posts->summary->postviews, 'id' ) );
579
580
		if ( ! $post_view_ids ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $post_view_ids of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
581
			return array();
582
		}
583
584
		return $this->get_posts( $post_view_ids, $count, $types );
585
	}
586
587
	/**
588
	 * Get some posts if no posts are found in the stats API
589
	 *
590
	 * @since 8.4.0 Added $count and $types
591
	 *
592
	 * @param int   $count The maximum number of posts to be returned.
593
	 * @param array $types The post types that should be returned.
594
	 * @return array
595
	 */
596
	public function get_fallback_posts( $count = 10, $types = array( 'post', 'page' ) ) {
597
		if ( current_user_can( 'edit_theme_options' ) ) {
598
			return array();
599
		}
600
601
		$post_query = new WP_Query();
602
603
		if ( ! is_array( $types ) || empty( $types ) ) {
604
			$types = array( 'post', 'page' );
605
		}
606
607
		$posts = $post_query->query(
608
			array(
609
				'posts_per_page' => $count,
610
				'post_status'    => 'publish',
611
				'post_type'      => $types,
612
				'no_found_rows'  => true,
613
				'fields'         => 'ids',
614
			)
615
		);
616
617
		if ( ! $posts ) {
618
			return array();
619
		}
620
621
		return $this->get_posts( $posts, $count, $types );
622
	}
623
624
	/**
625
	 * Get posts from an array of IDs
626
	 *
627
	 * @since 8.4.0 Added $types parameters
628
	 *
629
	 * @param array $post_ids The post IDs.
630
	 * @param int   $count The maximum number of posts to return.
631
	 * @param array $types The post types that should be returned. Optional. Defaults to 'post', 'page'.
632
	 * @return array
633
	 */
634
	public function get_posts( $post_ids, $count, $types = array( 'post', 'page' ) ) {
635
		$counter = 0;
636
637
		if ( ! is_array( $types ) || empty( $types ) ) {
638
			$types = array( 'post', 'page' );
639
		}
640
641
		$posts = array();
642
		foreach ( (array) $post_ids as $post_id ) {
643
			$post = get_post( $post_id );
644
645
			if ( ! $post ) {
646
				continue;
647
			}
648
649
			/**
650
			 * Attachment pages use the 'inherit' post status by default.
651
			 * To be able to remove attachment pages from private and password protect posts,
652
			 * we need to replace their post status by the parent post' status.
653
			 */
654 View Code Duplication
			if ( 'inherit' == $post->post_status && 'attachment' == $post->post_type ) {
655
				$post->post_status = get_post_status( $post_id );
656
			}
657
658
			// hide private and password protected posts
659
			if ( 'publish' != $post->post_status || ! empty( $post->post_password ) ) {
660
				continue;
661
			}
662
663
			// Filter by chosen Post Types.
664
			if ( ! in_array( $post->post_type, $types, true ) ) {
665
				continue;
666
			}
667
668
			// Both get HTML stripped etc on display
669
			if ( empty( $post->post_title ) ) {
670
				$title_source = $post->post_content;
671
				$title        = wp_html_excerpt( $title_source, 50 );
672
				$title       .= '&hellip;';
673
			} else {
674
				$title = $post->post_title;
675
			}
676
677
			$permalink = get_permalink( $post->ID );
678
679
			$post_type = $post->post_type;
680
681
			$posts[] = compact( 'title', 'permalink', 'post_id', 'post_type' );
682
			$counter++;
683
684
			if ( $counter == $count ) {
685
				break; // only need to load and show x number of likes
686
			}
687
		}
688
689
		/**
690
		 * Filter the Top Posts and Pages.
691
		 *
692
		 * @module widgets
693
		 *
694
		 * @since 3.0.0
695
		 *
696
		 * @param array $posts Array of the most popular posts.
697
		 * @param array $post_ids Array of Post IDs.
698
		 * @param string $count Number of Top Posts we want to display.
699
		 */
700
		return apply_filters( 'jetpack_widget_get_top_posts', $posts, $post_ids, $count );
0 ignored issues
show
Unused Code introduced by
The call to apply_filters() has too many arguments starting with $post_ids.

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

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

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

Loading history...
701
	}
702
}
703
704
/**
705
 * Create a shortcode to display the widget anywhere.
706
 *
707
 * @since 3.9.2
708
 */
709
function jetpack_do_top_posts_widget( $instance ) {
710
	// Post Types can't be entered as an array in the shortcode parameters.
711
	if ( isset( $instance['types'] ) && is_array( $instance['types'] ) ) {
712
		$instance['types'] = implode( ',', $instance['types'] );
713
	}
714
715
	$instance = shortcode_atts(
716
		Jetpack_Top_Posts_Widget::defaults(),
717
		$instance,
718
		'jetpack_top_posts_widget'
719
	);
720
721
	// Add a class to allow styling
722
	$args = array(
723
		'before_widget' => sprintf( '<div class="%s">', 'jetpack_top_posts_widget' ),
724
	);
725
726
	ob_start();
727
	the_widget( 'Jetpack_Top_Posts_Widget', $instance, $args );
728
	$output = ob_get_clean();
729
730
	return $output;
731
}
732
add_shortcode( 'jetpack_top_posts_widget', 'jetpack_do_top_posts_widget' );
733