PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 16.0-beta
Jetpack – WP Security, Backup, Speed, & Growth v16.0-beta
16.0 16.0-beta 16.0-a.7 16.0-a.5 15.9.1 16.0-a.3 16.0-a.1 15.9 15.9-beta 15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / modules / widgets / top-posts.php
jetpack / modules / widgets Last commit date
authors 7 months ago contact-info 7 months ago eu-cookie-law 7 months ago facebook-likebox 11 years ago flickr 7 months ago gallery 7 months ago goodreads 7 months ago google-translate 1 year ago image-widget 7 months ago instagram 4 months ago internet-defense-league 1 year ago milestone 4 months ago my-community 7 months ago social-icons 4 months ago social-media-icons 7 months ago top-posts 7 months ago wordpress-post-widget 2 weeks ago authors.php 7 months ago blog-stats.php 7 months ago class-jetpack-eu-cookie-law-widget.php 1 month ago class-jetpack-instagram-widget.php 6 months ago contact-info.php 1 month ago customizer-controls.css 7 months ago customizer-utils.js 1 year ago facebook-likebox.php 1 month ago flickr.php 7 months ago gallery.php 1 month ago goodreads.php 1 month ago google-translate.php 1 month ago gravatar-profile.css 7 months ago gravatar-profile.php 1 month ago image-widget.php 7 months ago internet-defense-league.php 7 months ago mailchimp.php 7 months ago milestone.php 7 months ago my-community.php 1 month ago rsslinks-widget.php 2 weeks ago simple-payments.php 7 months ago social-icons.php 1 month ago social-media-icons.php 7 months ago top-posts.php 1 month ago twitter-timeline-admin.js 1 year ago twitter-timeline.php 2 weeks ago upcoming-events.php 7 months ago wordpress-post-widget.php 7 months ago
top-posts.php
883 lines
1 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2 /**
3 * Top Posts widget.
4 *
5 * Currently, this widget depends on the Stats Module. To not load this file
6 * when the Stats Module is not active would potentially bypass Jetpack's
7 * fatal error detection on module activation, so we always load this file.
8 * Instead, we don't register the widget if the Stats Module isn't active.
9 *
10 * @package automattic/jetpack
11 */
12
13 // phpcs:disable Universal.Files.SeparateFunctionsFromOO.Mixed -- TODO: Move classes to appropriately-named class files.
14
15 use Automattic\Jetpack\Post_Media\Images;
16 use Automattic\Jetpack\Redirect;
17 use Automattic\Jetpack\Stats\WPCOM_Stats;
18 use Automattic\Jetpack\Status;
19 use Automattic\Jetpack\Status\Host;
20
21 if ( ! defined( 'ABSPATH' ) ) {
22 exit( 0 );
23 }
24
25 // Register the widget for use in Appearance -> Widgets
26 add_action( 'widgets_init', 'jetpack_top_posts_widget_init' );
27
28 /**
29 * Register the widget, if the Stats module is active.
30 */
31 function jetpack_top_posts_widget_init() {
32 // Currently, this widget depends on the Stats Module.
33 if (
34 ! ( defined( 'IS_WPCOM' ) && IS_WPCOM )
35 && (
36 ! Jetpack::is_module_active( 'stats' )
37 || ( new Status() )->is_offline_mode()
38 )
39 ) {
40 return;
41 }
42
43 register_widget( 'Jetpack_Top_Posts_Widget' );
44 }
45
46 /**
47 * Widget class.
48 */
49 class Jetpack_Top_Posts_Widget extends WP_Widget {
50 /**
51 * Widget unique identifier.
52 *
53 * @var string
54 */
55 public $alt_option_name = 'widget_stats_topposts';
56
57 /**
58 * Widget default title.
59 *
60 * @var string
61 */
62 public $default_title = '';
63
64 /**
65 * Constructor.
66 */
67 public function __construct() {
68 parent::__construct(
69 'top-posts',
70 /** This filter is documented in modules/widgets/facebook-likebox.php */
71 apply_filters( 'jetpack_widget_name', __( 'Top Posts &amp; Pages', 'jetpack' ) ),
72 array(
73 'description' => __( 'Shows your most viewed posts and pages.', 'jetpack' ),
74 'customize_selective_refresh' => true,
75 'show_instance_in_rest' => true,
76 )
77 );
78
79 $this->default_title = __( 'Top Posts &amp; Pages', 'jetpack' );
80
81 /**
82 * Add explanation about how the statistics are calculated.
83 *
84 * @module widgets
85 *
86 * @since 3.9.3
87 */
88 add_action( 'jetpack_widget_top_posts_after_fields', array( $this, 'stats_explanation' ) );
89 add_filter( 'widget_types_to_hide_from_legacy_widget_block', array( $this, 'hide_widget_in_block_editor' ) );
90 }
91
92 /**
93 * Remove the "Top Posts and Pages" widget from the Legacy Widget block
94 *
95 * @param array $widget_types List of widgets that are currently removed from the Legacy Widget block.
96 * @return array $widget_types New list of widgets that will be removed.
97 */
98 public function hide_widget_in_block_editor( $widget_types ) {
99 // @TODO: Hide for Simple sites when the block API starts working.
100 if ( ! ( new Host() )->is_wpcom_simple() ) {
101 $widget_types[] = 'top-posts';
102 }
103 return $widget_types;
104 }
105
106 /**
107 * Enqueue stylesheet.
108 */
109 public function enqueue_style() {
110 wp_register_style( 'jetpack-top-posts-widget', plugins_url( 'top-posts/style.css', __FILE__ ), array(), '20141013' );
111 wp_enqueue_style( 'jetpack-top-posts-widget' );
112 }
113
114 /**
115 * Displays the form for this widget on the Widgets page of the WP Admin area.
116 *
117 * @param array $instance Instance configuration.
118 *
119 * @return string|void
120 */
121 public function form( $instance ) {
122 $instance = wp_parse_args( (array) $instance, static::defaults() );
123
124 if ( false === $instance['title'] ) {
125 $instance['title'] = $this->default_title;
126 }
127 $title = stripslashes( $instance['title'] );
128
129 $count = isset( $instance['count'] ) ? (int) $instance['count'] : 10;
130 if ( $count < 1 || 10 < $count ) {
131 $count = 10;
132 }
133
134 $allowed_post_types = array_values( get_post_types( array( 'public' => true ) ) );
135 $types = isset( $instance['types'] ) ? (array) $instance['types'] : array( 'post', 'page' );
136
137 // 'likes' are not available in Jetpack
138 $ordering = isset( $instance['ordering'] ) && 'likes' === $instance['ordering'] ? 'likes' : 'views';
139
140 if ( isset( $instance['display'] ) && in_array( $instance['display'], array( 'grid', 'list', 'text' ), true ) ) {
141 $display = $instance['display'];
142 } else {
143 $display = 'text';
144 }
145
146 ?>
147
148 <p>
149 <label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"><?php esc_html_e( 'Title:', 'jetpack' ); ?></label>
150 <input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>" />
151 </p>
152
153 <p>
154 <label for="<?php echo esc_attr( $this->get_field_id( 'count' ) ); ?>"><?php esc_html_e( 'Maximum number of posts to show (no more than 10):', 'jetpack' ); ?></label>
155 <input id="<?php echo esc_attr( $this->get_field_id( 'count' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'count' ) ); ?>" type="number" value="<?php echo esc_attr( (string) $count ); ?>" min="1" max="10" />
156 </p>
157
158 <?php if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) : ?>
159 <p>
160 <label><?php esc_html_e( 'Order Top Posts &amp; Pages By:', 'jetpack' ); ?></label>
161 <ul>
162 <li><label><input id="<?php echo esc_attr( $this->get_field_id( 'ordering' ) ); ?>-likes" name="<?php echo esc_attr( $this->get_field_name( 'ordering' ) ); ?>" type="radio" value="likes" <?php checked( 'likes', $ordering ); ?> /> <?php esc_html_e( 'Likes', 'jetpack' ); ?></label></li>
163 <li><label><input id="<?php echo esc_attr( $this->get_field_id( 'ordering' ) ); ?>-views" name="<?php echo esc_attr( $this->get_field_name( 'ordering' ) ); ?>" type="radio" value="views" <?php checked( 'views', $ordering ); ?> /> <?php esc_html_e( 'Views', 'jetpack' ); ?></label></li>
164 </ul>
165 </p>
166 <?php endif; ?>
167
168 <p>
169 <label for="<?php echo esc_attr( $this->get_field_id( 'types' ) ); ?>"><?php esc_html_e( 'Types of pages to display:', 'jetpack' ); ?></label>
170 <ul>
171 <?php
172 foreach ( $allowed_post_types as $type ) {
173 // Get the Post Type name to display next to the checkbox.
174 $post_type_object = get_post_type_object( $type );
175 $label = $post_type_object->labels->name;
176
177 $checked = '';
178 if ( in_array( $type, $types, true ) ) {
179 $checked = 'checked="checked" ';
180 }
181 ?>
182
183 <li><label>
184 <input
185 value="<?php echo esc_attr( $type ); ?>"
186 name="<?php echo esc_attr( $this->get_field_name( 'types' ) ); ?>[]"
187 id="<?php echo esc_attr( $this->get_field_id( 'types' ) . '-' . $type ); ?>"
188 type="checkbox"
189 <?php echo $checked; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
190 >
191 <?php echo esc_html( $label ); ?>
192 </label></li>
193
194 <?php } // End foreach ?>
195 </ul>
196 </p>
197
198 <p>
199 <label><?php esc_html_e( 'Display as:', 'jetpack' ); ?></label>
200 <ul>
201 <li><label><input id="<?php echo esc_attr( $this->get_field_id( 'display' ) ); ?>-text" name="<?php echo esc_attr( $this->get_field_name( 'display' ) ); ?>" type="radio" value="text" <?php checked( 'text', $display ); ?> /> <?php esc_html_e( 'Text List', 'jetpack' ); ?></label></li>
202 <li><label><input id="<?php echo esc_attr( $this->get_field_id( 'display' ) ); ?>-list" name="<?php echo esc_attr( $this->get_field_name( 'display' ) ); ?>" type="radio" value="list" <?php checked( 'list', $display ); ?> /> <?php esc_html_e( 'Image List', 'jetpack' ); ?></label></li>
203 <li><label><input id="<?php echo esc_attr( $this->get_field_id( 'display' ) ); ?>-grid" name="<?php echo esc_attr( $this->get_field_name( 'display' ) ); ?>" type="radio" value="grid" <?php checked( 'grid', $display ); ?> /> <?php esc_html_e( 'Image Grid', 'jetpack' ); ?></label></li>
204 </ul>
205 </p>
206 <?php
207
208 /**
209 * Fires after the fields are displayed in the Top Posts Widget settings in wp-admin.
210 *
211 * Allow adding extra content after the fields are displayed.
212 *
213 * @module widgets
214 *
215 * @since 3.9.3
216 *
217 * @param array $args {
218 * @param array $instance The widget instance.
219 * @param object $this The class object.
220 * }
221 */
222 do_action( 'jetpack_widget_top_posts_after_fields', array( $instance, $this ) );
223 }
224
225 /**
226 * Explains how the statics are calculated.
227 */
228 public function stats_explanation() {
229 echo '<p>';
230 esc_html_e( 'Top Posts &amp; Pages by views are calculated from 24-48 hours of stats. They take a while to change.', 'jetpack' );
231 echo '</p>';
232 }
233
234 /**
235 * Deals with the settings when they are saved by the admin.
236 *
237 * @param array $new_instance New configuration values.
238 * @param array $old_instance Old configuration values.
239 *
240 * @return array
241 */
242 public function update( $new_instance, $old_instance ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
243 $instance = array();
244 $instance['title'] = wp_kses( $new_instance['title'], array() );
245 if ( $instance['title'] === $this->default_title ) {
246 $instance['title'] = false; // Store as false in case of language change.
247 }
248
249 $instance['count'] = (int) $new_instance['count'];
250 if ( $instance['count'] < 1 || 10 < $instance['count'] ) {
251 $instance['count'] = 10;
252 }
253
254 // 'likes' are not available in Jetpack
255 $instance['ordering'] = isset( $new_instance['ordering'] ) && 'likes' === $new_instance['ordering']
256 ? 'likes'
257 : 'views';
258
259 $allowed_post_types = array_values( get_post_types( array( 'public' => true ) ) );
260 $instance['types'] = $new_instance['types'];
261 foreach ( $new_instance['types'] as $key => $type ) {
262 if ( ! in_array( $type, $allowed_post_types, true ) ) {
263 unset( $new_instance['types'][ $key ] );
264 }
265 }
266
267 if ( isset( $new_instance['display'] ) && in_array( $new_instance['display'], array( 'grid', 'list', 'text' ), true ) ) {
268 $instance['display'] = $new_instance['display'];
269 } else {
270 $instance['display'] = 'text';
271 }
272
273 /**
274 * Filters Top Posts Widget settings before they're saved.
275 *
276 * @module widgets
277 *
278 * @since 3.9.3
279 *
280 * @param array $instance The santized widget instance. Only contains data processed by the current widget.
281 * @param array $new_instance The new widget instance before sanitization.
282 */
283 $instance = apply_filters( 'jetpack_top_posts_saving', $instance, $new_instance );
284
285 return $instance;
286 }
287
288 /**
289 * Outputs the HTML for this widget.
290 *
291 * @param array $args An array of standard parameters for widgets in this theme.
292 * @param array $instance An array of settings for this widget instance.
293 *
294 * @return void Echoes it's output
295 */
296 public function widget( $args, $instance ) {
297 /** This action is documented in modules/widgets/gravatar-profile.php */
298 do_action( 'jetpack_stats_extra', 'widget_view', 'top_posts' );
299
300 $instance = wp_parse_args( (array) $instance, static::defaults() );
301
302 $title = $instance['title'] ?? false;
303 if ( false === $title ) {
304 $title = $this->default_title;
305 }
306
307 /** This filter is documented in core/src/wp-includes/default-widgets.php */
308 $title = apply_filters( 'widget_title', $title );
309
310 // Enqueue front end assets.
311 $this->enqueue_style();
312
313 $count = isset( $instance['count'] ) ? (int) $instance['count'] : false;
314 if ( $count < 1 || 10 < $count ) {
315 $count = 10;
316 }
317 /**
318 * Control the number of displayed posts.
319 *
320 * @module widgets
321 *
322 * @since 3.3.0
323 *
324 * @param string $count Number of Posts displayed in the Top Posts widget. Default is 10.
325 */
326 $count = apply_filters( 'jetpack_top_posts_widget_count', $count );
327
328 $types = isset( $instance['types'] ) ? (array) $instance['types'] : array( 'post', 'page' );
329
330 // 'likes' are not available in Jetpack
331 $ordering = isset( $instance['ordering'] ) && 'likes' === $instance['ordering']
332 ? 'likes'
333 : 'views';
334
335 if (
336 isset( $instance['display'] )
337 && in_array( $instance['display'], array( 'grid', 'list', 'text' ), true )
338 ) {
339 $display = $instance['display'];
340 } else {
341 $display = 'text';
342 }
343
344 $get_image_options = array();
345 if ( 'text' !== $display ) {
346 $get_image_options = array(
347 'fallback_to_avatars' => true,
348 /** This filter is documented in modules/stats.php */
349 'gravatar_default' => apply_filters( 'jetpack_static_url', set_url_scheme( 'https://en.wordpress.com/i/logo/white-gray-80.png' ) ),
350 'avatar_size' => 40,
351 'width' => null,
352 'height' => null,
353 );
354 if ( 'grid' === $display ) {
355 $get_image_options['avatar_size'] = 200;
356 }
357 /**
358 * Top Posts Widget Image options.
359 *
360 * @module widgets
361 *
362 * @since 1.8.0
363 *
364 * @param array $get_image_options {
365 * Array of Image options.
366 * @type bool true Should we default to Gravatars when no image is found? Default is true.
367 * @type string $gravatar_default Default Image URL if no Gravatar is found.
368 * @type int $avatar_size Default Image size.
369 * @type mixed $width Image width, not set by default and $avatar_size is used instead.
370 * @type mixed $height Image height, not set by default and $avatar_size is used instead.
371 * }
372 */
373 $get_image_options = apply_filters( 'jetpack_top_posts_widget_image_options', $get_image_options );
374 }
375
376 if ( function_exists( 'wpl_get_blogs_most_liked_posts' ) && 'likes' === $ordering ) {
377 $posts = $this->get_by_likes( $count, $types );
378 } else {
379 $posts = $this->get_by_views( $count, $args, $types );
380 }
381
382 echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
383
384 if ( ! empty( $title ) ) {
385 echo $args['before_title'] . $title . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
386 }
387
388 /*
389 * If we have no posts, add some fallback posts
390 * and display a fallback message for admins.
391 */
392 if ( ! $posts ) {
393 if ( current_user_can( 'edit_theme_options' ) ) {
394 echo self::fallback_message(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
395 }
396
397 $posts = $this->get_fallback_posts( $count, $types );
398 }
399
400 /*
401 * Display our posts.
402 */
403
404 /**
405 * Filter the layout of the Top Posts Widget
406 *
407 * @module widgets
408 *
409 * @since 6.4.0
410 *
411 * @param string $layout layout of the Top Posts Widget (empty string).
412 * @param array $posts IDs of the posts to be displayed.
413 * @param array $display Display option from widget form.
414 */
415 $layout = apply_filters( 'jetpack_top_posts_widget_layout', '', $posts, $display );
416 if ( ! empty( $layout ) ) {
417 echo $layout; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
418 }
419
420 switch ( $display ) {
421 case 'list':
422 case 'grid':
423 // Keep the avatar_size as default dimensions for backward compatibility.
424 $width = (int) $get_image_options['avatar_size'];
425 $height = (int) $get_image_options['avatar_size'];
426
427 // Check if the user has changed the width.
428 if ( ! empty( $get_image_options['width'] ) ) {
429 $width = (int) $get_image_options['width'];
430 }
431
432 // Check if the user has changed the height.
433 if ( ! empty( $get_image_options['height'] ) ) {
434 $height = (int) $get_image_options['height'];
435 }
436
437 foreach ( $posts as &$post ) {
438 $image = Images::get_image(
439 $post['post_id'],
440 array(
441 'fallback_to_avatars' => (bool) $get_image_options['fallback_to_avatars'],
442 'width' => $width,
443 'height' => $height,
444 'avatar_size' => (int) $get_image_options['avatar_size'],
445 )
446 );
447
448 if ( $image ) {
449 $post['image'] = Images::fit_image_url(
450 $image['src'],
451 $width,
452 $height
453 );
454
455 $post['image_srcset'] = Images::generate_cropped_srcset(
456 $image,
457 $width,
458 $height
459 );
460
461 if ( empty( $post['image_srcset'] ) ) {
462 $post['image_srcset'] = "{$post['image']} 1x";
463 }
464 }
465 }
466 unset( $post );
467
468 if ( 'grid' === $display ) {
469 echo "<div class='widgets-grid-layout no-grav'>\n";
470 foreach ( $posts as $post ) {
471 echo '<div class="widget-grid-view-image">';
472
473 /**
474 * Fires before each Top Post result, inside <li>.
475 *
476 * @module widgets
477 *
478 * @since 3.2.0
479 *
480 * @param string $post['post_id'] Post ID.
481 */
482 do_action( 'jetpack_widget_top_posts_before_post', $post['post_id'] );
483
484 /**
485 * Filter the permalink of items in the Top Posts widget.
486 *
487 * @module widgets
488 *
489 * @since 4.4.0
490 *
491 * @param string $post['permalink'] Post permalink.
492 * @param array $post Post array.
493 */
494 $filtered_permalink = apply_filters( 'jetpack_top_posts_widget_permalink', $post['permalink'], $post );
495
496 if ( $post['image'] ) {
497 printf(
498 '<a href="%1$s" title="%2$s" class="bump-view" data-bump-view="tp"%3$s><img loading="lazy" width="%4$d" height="%5$d" src="%6$s" srcset="%7$s" alt="%2$s" data-pin-nopin="true"/></a>',
499 esc_url( $filtered_permalink ),
500 esc_attr( wp_kses( $post['title'], array() ) ),
501 ( get_queried_object_id() === $post['post_id'] ? ' aria-current="page"' : '' ),
502 absint( $width ),
503 absint( $height ),
504 esc_url( $post['image'] ),
505 esc_attr( $post['image_srcset'] )
506 );
507 }
508
509 /**
510 * Fires after each Top Post result, inside <li>.
511 *
512 * @module widgets
513 *
514 * @since 3.2.0
515 *
516 * @param string $post['post_id'] Post ID.
517 */
518 do_action( 'jetpack_widget_top_posts_after_post', $post['post_id'] );
519
520 echo '</div>';
521 }
522 echo "</div>\n";
523 } else {
524 echo "<ul class='widgets-list-layout no-grav'>\n";
525 foreach ( $posts as $post ) {
526 echo '<li>';
527
528 /** This action is documented in modules/widgets/top-posts.php */
529 do_action( 'jetpack_widget_top_posts_before_post', $post['post_id'] );
530
531 /** This filter is documented in modules/widgets/top-posts.php */
532 $filtered_permalink = apply_filters( 'jetpack_top_posts_widget_permalink', $post['permalink'], $post );
533
534 if ( $post['image'] ) {
535 printf(
536 '<a href="%1$s" title="%2$s" class="bump-view" data-bump-view="tp"%3$s><img loading="lazy" width="%4$d" height="%5$d" src="%6$s" srcset="%7$s" alt="%2$s" data-pin-nopin="true" class="widgets-list-layout-blavatar" /></a>',
537 esc_url( $filtered_permalink ),
538 esc_attr( wp_kses( $post['title'], array() ) ),
539 ( get_queried_object_id() === $post['post_id'] ? ' aria-current="page"' : '' ),
540 absint( $width ),
541 absint( $height ),
542 esc_url( $post['image'] ),
543 esc_attr( $post['image_srcset'] )
544 );
545 }
546
547 printf(
548 '<div class="widgets-list-layout-links">
549 <a href="%1$s" title="%2$s" class="bump-view" data-bump-view="tp"%3$s>%4$s</a>
550 </div>
551 ',
552 esc_url( $filtered_permalink ),
553 esc_attr( wp_kses( $post['title'], array() ) ),
554 ( get_queried_object_id() === $post['post_id'] ? ' aria-current="page"' : '' ),
555 esc_html( wp_kses( $post['title'], array() ) )
556 );
557
558 /** This action is documented in modules/widgets/top-posts.php */
559 do_action( 'jetpack_widget_top_posts_after_post', $post['post_id'] );
560
561 echo '</li>';
562 }
563 echo "</ul>\n";
564 }
565 break;
566 default:
567 echo '<ul>';
568
569 foreach ( $posts as $post ) {
570 echo '<li>';
571
572 /** This action is documented in modules/widgets/top-posts.php */
573 do_action( 'jetpack_widget_top_posts_before_post', $post['post_id'] );
574
575 /** This filter is documented in modules/widgets/top-posts.php */
576 $filtered_permalink = apply_filters( 'jetpack_top_posts_widget_permalink', $post['permalink'], $post );
577
578 printf(
579 '<a href="%1$s" class="bump-view" data-bump-view="tp"%2$s>%3$s</a>',
580 esc_url( $filtered_permalink ),
581 ( get_queried_object_id() === $post['post_id'] ? ' aria-current="page"' : '' ),
582 esc_html( wp_kses( $post['title'], array() ) )
583 );
584
585 /** This action is documented in modules/widgets/top-posts.php */
586 do_action( 'jetpack_widget_top_posts_after_post', $post['post_id'] );
587
588 echo '</li>';
589 }
590
591 echo '</ul>';
592 break;
593 }
594
595 echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
596 }
597
598 /**
599 * Display a message with recommendations when there are no recorded top posts.
600 *
601 * @return string $fallback_message
602 */
603 private static function fallback_message() {
604 $link = esc_url( Redirect::get_url( 'jetpack-support-getting-more-views-and-traffic' ) );
605 if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
606 $link = 'https://en.support.wordpress.com/getting-more-site-traffic/';
607 }
608
609 $fallback_message = '<p>';
610 $fallback_message .= sprintf(
611 wp_kses(
612 /* Translators: Placeholder: link to the Jetpack support article. */
613 __( 'There are no popular posts to display. Instead, your visitors will see a list of your recent posts below. <a href="%s" target="_blank">Want more traffic?</a>', 'jetpack' ),
614 array(
615 'a' => array(
616 'href' => array(),
617 'target' => array(),
618 ),
619 )
620 ),
621 esc_url( $link )
622 );
623 $fallback_message .= '<p>';
624
625 return $fallback_message;
626 }
627
628 /**
629 * Widget default option values.
630 */
631 public static function defaults() {
632 return array(
633 'title' => esc_html__( 'Top Posts &amp; Pages', 'jetpack' ),
634 'count' => absint( 10 ),
635 'types' => array( 'post', 'page' ),
636 'ordering' => 'views',
637 'display' => 'text',
638 );
639 }
640
641 /**
642 * Get most liked posts
643 *
644 * ONLY TO BE USED IN WPCOM
645 *
646 * @since 8.4.0 Added $types param
647 *
648 * @param int $count The maximum number of posts to be returned.
649 * @param array $types The post types that should be returned. Optional. Defaults to 'post' and 'page'.
650 *
651 * @return array array of posts.
652 */
653 public function get_by_likes( $count, $types = array( 'post', 'page' ) ) {
654 $post_likes = wpl_get_blogs_most_liked_posts();
655 if ( ! $post_likes ) {
656 return array();
657 }
658
659 return $this->get_posts( array_keys( $post_likes ), $count, $types );
660 }
661
662 /**
663 * Get the top posts based on views
664 *
665 * @since 8.4.0 Added $types param
666 *
667 * @param int $count The maximum number of posts to be returned.
668 * @param array $args The widget arguments.
669 * @param array $types The post types that should be returned.
670 *
671 * @return array array of posts. Defaults to 'post' and 'page'.
672 */
673 public function get_by_views( $count, $args, $types = array( 'post', 'page' ) ) {
674 if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
675 $post_views = wp_cache_get( "get_top_posts_$count", 'stats' );
676 if ( false === $post_views ) {
677 $stats_get_daily_history = stats_get_daily_history(
678 false,
679 get_current_blog_id(),
680 'postviews',
681 'post_id',
682 false,
683 2,
684 '',
685 $count * 2 + 10,
686 true
687 );
688 $post_views = array_shift( $stats_get_daily_history );
689 unset( $post_views[0] );
690 wp_cache_add( "get_top_posts_$count", $post_views, 'stats', 1200 );
691 }
692
693 return $this->get_posts( array_keys( $post_views ), $count, $types );
694 }
695
696 /**
697 * Filter the number of days used to calculate Top Posts for the Top Posts widget.
698 * We do not recommend accessing more than 10 days of results at one.
699 * When more than 10 days of results are accessed at once, results should be cached via the WordPress transients API.
700 * Querying for -1 days will give results for an infinite number of days.
701 *
702 * @module widgets
703 *
704 * @since 3.9.3
705 *
706 * @param int 2 Number of days. Default is 2.
707 * @param array $args The widget arguments.
708 */
709 $days = (int) apply_filters( 'jetpack_top_posts_days', 2, $args );
710
711 /** Handling situations where the number of days makes no sense - allows for unlimited days where $days = -1 */
712 if ( 0 === $days ) {
713 $days = 2;
714 }
715
716 $query_args = array(
717 'max' => 11,
718 'summarize' => 1,
719 'num' => $days,
720 );
721 $wpcom_stats = new WPCOM_Stats();
722 $post_view_posts = $wpcom_stats->convert_stats_array_to_object(
723 $wpcom_stats->get_top_posts( $query_args )
724 );
725
726 if ( ! isset( $post_view_posts->summary ) || empty( $post_view_posts->summary->postviews ) ) {
727 return array();
728 }
729
730 $post_view_ids = array_filter( wp_list_pluck( $post_view_posts->summary->postviews, 'id' ) );
731
732 if ( ! $post_view_ids ) {
733 return array();
734 }
735
736 return $this->get_posts( $post_view_ids, $count, $types );
737 }
738
739 /**
740 * Get some posts if no posts are found in the stats API
741 *
742 * @since 8.4.0 Added $count and $types
743 *
744 * @param int $count The maximum number of posts to be returned.
745 * @param array $types The post types that should be returned.
746 * @return array
747 */
748 public function get_fallback_posts( $count = 10, $types = array( 'post', 'page' ) ) {
749 $post_query = new WP_Query();
750
751 if ( ! is_array( $types ) || empty( $types ) ) {
752 $types = array( 'post', 'page' );
753 }
754
755 $posts = $post_query->query(
756 array(
757 'posts_per_page' => $count,
758 'post_status' => 'publish',
759 'post_type' => $types,
760 'no_found_rows' => true,
761 'fields' => 'ids',
762 )
763 );
764
765 if ( ! $posts ) {
766 return array();
767 }
768
769 return $this->get_posts( $posts, $count, $types );
770 }
771
772 /**
773 * Get posts from an array of IDs
774 *
775 * @since 8.4.0 Added $types parameters
776 *
777 * @param array $post_ids The post IDs.
778 * @param int $count The maximum number of posts to return.
779 * @param array $types The post types that should be returned. Optional. Defaults to 'post', 'page'.
780 * @return array
781 */
782 public function get_posts( $post_ids, $count, $types = array( 'post', 'page' ) ) {
783 $counter = 0;
784
785 if ( ! is_array( $types ) || empty( $types ) ) {
786 $types = array( 'post', 'page' );
787 }
788
789 $posts = array();
790 foreach ( (array) $post_ids as $post_id ) {
791 $post = get_post( $post_id );
792
793 if ( ! $post ) {
794 continue;
795 }
796
797 /**
798 * Attachment pages use the 'inherit' post status by default.
799 * To be able to remove attachment pages from private and password protect posts,
800 * we need to replace their post status by the parent post' status.
801 */
802 if ( 'inherit' === $post->post_status && 'attachment' === $post->post_type ) {
803 $post->post_status = get_post_status( $post_id );
804 }
805
806 // hide private and password protected posts.
807 if ( 'publish' !== $post->post_status || ! empty( $post->post_password ) ) {
808 continue;
809 }
810
811 // Filter by chosen Post Types.
812 if ( ! in_array( $post->post_type, $types, true ) ) {
813 continue;
814 }
815
816 // Both get HTML stripped etc on display.
817 if ( empty( $post->post_title ) ) {
818 $title_source = $post->post_content;
819 $title = wp_html_excerpt( $title_source, 50 );
820 $title .= '&hellip;';
821 } else {
822 $title = $post->post_title;
823 }
824
825 $permalink = get_permalink( $post->ID );
826
827 $post_type = $post->post_type;
828
829 $posts[] = compact( 'title', 'permalink', 'post_id', 'post_type' );
830 ++$counter;
831
832 if ( $counter == $count ) { // phpcs:ignore Universal.Operators.StrictComparisons.LooseEqual
833 break; // only need to load and show x number of likes.
834 }
835 }
836
837 /**
838 * Filter the Top Posts and Pages.
839 *
840 * @module widgets
841 *
842 * @since 3.0.0
843 *
844 * @param array $posts Array of the most popular posts.
845 * @param array $post_ids Array of Post IDs.
846 * @param string $count Number of Top Posts we want to display.
847 */
848 return apply_filters( 'jetpack_widget_get_top_posts', $posts, $post_ids, $count );
849 }
850 }
851
852 /**
853 * Create a shortcode to display the widget anywhere.
854 *
855 * @since 3.9.2
856 *
857 * @param array $instance Saved values from database.
858 */
859 function jetpack_do_top_posts_widget( $instance ) {
860 // Post Types can't be entered as an array in the shortcode parameters.
861 if ( isset( $instance['types'] ) && is_string( $instance['types'] ) ) {
862 $instance['types'] = explode( ',', $instance['types'] );
863 }
864
865 $instance = shortcode_atts(
866 Jetpack_Top_Posts_Widget::defaults(),
867 $instance,
868 'jetpack_top_posts_widget'
869 );
870
871 // Add a class to allow styling.
872 $args = array(
873 'before_widget' => sprintf( '<div class="%s">', 'jetpack_top_posts_widget' ),
874 );
875
876 ob_start();
877 the_widget( 'Jetpack_Top_Posts_Widget', $instance, $args );
878 $output = ob_get_clean();
879
880 return $output;
881 }
882 add_shortcode( 'jetpack_top_posts_widget', 'jetpack_do_top_posts_widget' );
883