PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 14.2.2
Jetpack – WP Security, Backup, Speed, & Growth v14.2.2
12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 14.2.2 14.3.1 14.4.2 14.5.1 14.6.1 14.7.1 14.8.1 14.9.2 15.0.3 15.1.2 15.2.1 15.3.2 15.4.1 15.5.1 15.6.1 15.7.2 15.8.1 15.9.2 16.0.2 16.1.3 16.2-a.5 16.2-a.3 16.1.2 16.2-a.1 16.1.1 16.1 16.1-beta 16.1-beta.2 16.1-beta.3 16.1-a.5 16.1-a.3 16.0.1 16.1-a.1 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 8 years ago contact-info 5 years ago eu-cookie-law 1 year ago facebook-likebox 11 years ago flickr 2 years ago gallery 1 year ago goodreads 2 years ago google-translate 1 year ago image-widget 8 years ago instagram 6 years ago internet-defense-league 2 years ago milestone 1 year ago my-community 8 years ago simple-payments 1 year ago social-icons 1 year ago social-media-icons 5 years ago top-posts 8 years ago wordpress-post-widget 2 years ago authors.php 1 year ago blog-stats.php 2 years ago class-jetpack-eu-cookie-law-widget.php 1 year ago class-jetpack-instagram-widget.php 1 year ago contact-info.php 2 years ago customizer-controls.css 9 years ago customizer-utils.js 1 year ago facebook-likebox.php 2 years ago flickr.php 1 year ago gallery.php 2 years ago goodreads.php 1 year ago google-translate.php 2 years ago gravatar-profile.css 10 years ago gravatar-profile.php 2 years ago image-widget.php 1 year ago internet-defense-league.php 3 years ago mailchimp.php 3 years ago milestone.php 5 years ago my-community.php 1 year ago rsslinks-widget.php 3 years ago simple-payments.php 1 year ago social-icons.php 1 year ago social-media-icons.php 1 year ago top-posts.php 1 year ago twitter-timeline-admin.js 1 year ago twitter-timeline.php 3 years ago upcoming-events.php 2 years ago wordpress-post-widget.php 4 years ago
top-posts.php
878 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\Redirect;
16 use Automattic\Jetpack\Stats\WPCOM_Stats;
17 use Automattic\Jetpack\Status;
18 use Automattic\Jetpack\Status\Host;
19
20 // Register the widget for use in Appearance -> Widgets
21 add_action( 'widgets_init', 'jetpack_top_posts_widget_init' );
22
23 /**
24 * Register the widget, if the Stats module is active.
25 */
26 function jetpack_top_posts_widget_init() {
27 // Currently, this widget depends on the Stats Module.
28 if (
29 ! ( defined( 'IS_WPCOM' ) && IS_WPCOM )
30 && (
31 ! Jetpack::is_module_active( 'stats' )
32 || ( new Status() )->is_offline_mode()
33 )
34 ) {
35 return;
36 }
37
38 register_widget( 'Jetpack_Top_Posts_Widget' );
39 }
40
41 /**
42 * Widget class.
43 */
44 class Jetpack_Top_Posts_Widget extends WP_Widget {
45 /**
46 * Widget unique identifier.
47 *
48 * @var string
49 */
50 public $alt_option_name = 'widget_stats_topposts';
51
52 /**
53 * Widget default title.
54 *
55 * @var string
56 */
57 public $default_title = '';
58
59 /**
60 * Constructor.
61 */
62 public function __construct() {
63 parent::__construct(
64 'top-posts',
65 /** This filter is documented in modules/widgets/facebook-likebox.php */
66 apply_filters( 'jetpack_widget_name', __( 'Top Posts &amp; Pages', 'jetpack' ) ),
67 array(
68 'description' => __( 'Shows your most viewed posts and pages.', 'jetpack' ),
69 'customize_selective_refresh' => true,
70 'show_instance_in_rest' => true,
71 )
72 );
73
74 $this->default_title = __( 'Top Posts &amp; Pages', 'jetpack' );
75
76 /**
77 * Add explanation about how the statistics are calculated.
78 *
79 * @module widgets
80 *
81 * @since 3.9.3
82 */
83 add_action( 'jetpack_widget_top_posts_after_fields', array( $this, 'stats_explanation' ) );
84 add_filter( 'widget_types_to_hide_from_legacy_widget_block', array( $this, 'hide_widget_in_block_editor' ) );
85 }
86
87 /**
88 * Remove the "Top Posts and Pages" widget from the Legacy Widget block
89 *
90 * @param array $widget_types List of widgets that are currently removed from the Legacy Widget block.
91 * @return array $widget_types New list of widgets that will be removed.
92 */
93 public function hide_widget_in_block_editor( $widget_types ) {
94 // @TODO: Hide for Simple sites when the block API starts working.
95 if ( ! ( new Host() )->is_wpcom_simple() ) {
96 $widget_types[] = 'top-posts';
97 }
98 return $widget_types;
99 }
100
101 /**
102 * Enqueue stylesheet.
103 */
104 public function enqueue_style() {
105 wp_register_style( 'jetpack-top-posts-widget', plugins_url( 'top-posts/style.css', __FILE__ ), array(), '20141013' );
106 wp_enqueue_style( 'jetpack-top-posts-widget' );
107 }
108
109 /**
110 * Displays the form for this widget on the Widgets page of the WP Admin area.
111 *
112 * @param array $instance Instance configuration.
113 *
114 * @return void
115 */
116 public function form( $instance ) {
117 $instance = wp_parse_args( (array) $instance, static::defaults() );
118
119 if ( false === $instance['title'] ) {
120 $instance['title'] = $this->default_title;
121 }
122 $title = stripslashes( $instance['title'] );
123
124 $count = isset( $instance['count'] ) ? (int) $instance['count'] : 10;
125 if ( $count < 1 || 10 < $count ) {
126 $count = 10;
127 }
128
129 $allowed_post_types = array_values( get_post_types( array( 'public' => true ) ) );
130 $types = isset( $instance['types'] ) ? (array) $instance['types'] : array( 'post', 'page' );
131
132 // 'likes' are not available in Jetpack
133 $ordering = isset( $instance['ordering'] ) && 'likes' === $instance['ordering'] ? 'likes' : 'views';
134
135 if ( isset( $instance['display'] ) && in_array( $instance['display'], array( 'grid', 'list', 'text' ), true ) ) {
136 $display = $instance['display'];
137 } else {
138 $display = 'text';
139 }
140
141 ?>
142
143 <p>
144 <label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"><?php esc_html_e( 'Title:', 'jetpack' ); ?></label>
145 <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 ); ?>" />
146 </p>
147
148 <p>
149 <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>
150 <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 (int) $count; ?>" min="1" max="10" />
151 </p>
152
153 <?php if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) : ?>
154 <p>
155 <label><?php esc_html_e( 'Order Top Posts &amp; Pages By:', 'jetpack' ); ?></label>
156 <ul>
157 <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>
158 <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>
159 </ul>
160 </p>
161 <?php endif; ?>
162
163 <p>
164 <label for="<?php echo esc_attr( $this->get_field_id( 'types' ) ); ?>"><?php esc_html_e( 'Types of pages to display:', 'jetpack' ); ?></label>
165 <ul>
166 <?php
167 foreach ( $allowed_post_types as $type ) {
168 // Get the Post Type name to display next to the checkbox.
169 $post_type_object = get_post_type_object( $type );
170 $label = $post_type_object->labels->name;
171
172 $checked = '';
173 if ( in_array( $type, $types, true ) ) {
174 $checked = 'checked="checked" ';
175 }
176 ?>
177
178 <li><label>
179 <input
180 value="<?php echo esc_attr( $type ); ?>"
181 name="<?php echo esc_attr( $this->get_field_name( 'types' ) ); ?>[]"
182 id="<?php echo esc_attr( $this->get_field_id( 'types' ) . '-' . $type ); ?>"
183 type="checkbox"
184 <?php echo $checked; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
185 >
186 <?php echo esc_html( $label ); ?>
187 </label></li>
188
189 <?php } // End foreach ?>
190 </ul>
191 </p>
192
193 <p>
194 <label><?php esc_html_e( 'Display as:', 'jetpack' ); ?></label>
195 <ul>
196 <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>
197 <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>
198 <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>
199 </ul>
200 </p>
201 <?php
202
203 /**
204 * Fires after the fields are displayed in the Top Posts Widget settings in wp-admin.
205 *
206 * Allow adding extra content after the fields are displayed.
207 *
208 * @module widgets
209 *
210 * @since 3.9.3
211 *
212 * @param array $args {
213 * @param array $instance The widget instance.
214 * @param object $this The class object.
215 * }
216 */
217 do_action( 'jetpack_widget_top_posts_after_fields', array( $instance, $this ) );
218 }
219
220 /**
221 * Explains how the statics are calculated.
222 */
223 public function stats_explanation() {
224 echo '<p>';
225 esc_html_e( 'Top Posts &amp; Pages by views are calculated from 24-48 hours of stats. They take a while to change.', 'jetpack' );
226 echo '</p>';
227 }
228
229 /**
230 * Deals with the settings when they are saved by the admin.
231 *
232 * @param array $new_instance New configuration values.
233 * @param array $old_instance Old configuration values.
234 *
235 * @return array
236 */
237 public function update( $new_instance, $old_instance ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
238 $instance = array();
239 $instance['title'] = wp_kses( $new_instance['title'], array() );
240 if ( $instance['title'] === $this->default_title ) {
241 $instance['title'] = false; // Store as false in case of language change.
242 }
243
244 $instance['count'] = (int) $new_instance['count'];
245 if ( $instance['count'] < 1 || 10 < $instance['count'] ) {
246 $instance['count'] = 10;
247 }
248
249 // 'likes' are not available in Jetpack
250 $instance['ordering'] = isset( $new_instance['ordering'] ) && 'likes' === $new_instance['ordering']
251 ? 'likes'
252 : 'views';
253
254 $allowed_post_types = array_values( get_post_types( array( 'public' => true ) ) );
255 $instance['types'] = $new_instance['types'];
256 foreach ( $new_instance['types'] as $key => $type ) {
257 if ( ! in_array( $type, $allowed_post_types, true ) ) {
258 unset( $new_instance['types'][ $key ] );
259 }
260 }
261
262 if ( isset( $new_instance['display'] ) && in_array( $new_instance['display'], array( 'grid', 'list', 'text' ), true ) ) {
263 $instance['display'] = $new_instance['display'];
264 } else {
265 $instance['display'] = 'text';
266 }
267
268 /**
269 * Filters Top Posts Widget settings before they're saved.
270 *
271 * @module widgets
272 *
273 * @since 3.9.3
274 *
275 * @param array $instance The santized widget instance. Only contains data processed by the current widget.
276 * @param array $new_instance The new widget instance before sanitization.
277 */
278 $instance = apply_filters( 'jetpack_top_posts_saving', $instance, $new_instance );
279
280 return $instance;
281 }
282
283 /**
284 * Outputs the HTML for this widget.
285 *
286 * @param array $args An array of standard parameters for widgets in this theme.
287 * @param array $instance An array of settings for this widget instance.
288 *
289 * @return void Echoes it's output
290 */
291 public function widget( $args, $instance ) {
292 /** This action is documented in modules/widgets/gravatar-profile.php */
293 do_action( 'jetpack_stats_extra', 'widget_view', 'top_posts' );
294
295 $instance = wp_parse_args( (array) $instance, static::defaults() );
296
297 $title = isset( $instance['title'] ) ? $instance['title'] : false;
298 if ( false === $title ) {
299 $title = $this->default_title;
300 }
301
302 /** This filter is documented in core/src/wp-includes/default-widgets.php */
303 $title = apply_filters( 'widget_title', $title );
304
305 // Enqueue front end assets.
306 $this->enqueue_style();
307
308 $count = isset( $instance['count'] ) ? (int) $instance['count'] : false;
309 if ( $count < 1 || 10 < $count ) {
310 $count = 10;
311 }
312 /**
313 * Control the number of displayed posts.
314 *
315 * @module widgets
316 *
317 * @since 3.3.0
318 *
319 * @param string $count Number of Posts displayed in the Top Posts widget. Default is 10.
320 */
321 $count = apply_filters( 'jetpack_top_posts_widget_count', $count );
322
323 $types = isset( $instance['types'] ) ? (array) $instance['types'] : array( 'post', 'page' );
324
325 // 'likes' are not available in Jetpack
326 $ordering = isset( $instance['ordering'] ) && 'likes' === $instance['ordering']
327 ? 'likes'
328 : 'views';
329
330 if (
331 isset( $instance['display'] )
332 && in_array( $instance['display'], array( 'grid', 'list', 'text' ), true )
333 ) {
334 $display = $instance['display'];
335 } else {
336 $display = 'text';
337 }
338
339 $get_image_options = array();
340 if ( 'text' !== $display ) {
341 $get_image_options = array(
342 'fallback_to_avatars' => true,
343 /** This filter is documented in modules/stats.php */
344 'gravatar_default' => apply_filters( 'jetpack_static_url', set_url_scheme( 'https://en.wordpress.com/i/logo/white-gray-80.png' ) ),
345 'avatar_size' => 40,
346 'width' => null,
347 'height' => null,
348 );
349 if ( 'grid' === $display ) {
350 $get_image_options['avatar_size'] = 200;
351 }
352 /**
353 * Top Posts Widget Image options.
354 *
355 * @module widgets
356 *
357 * @since 1.8.0
358 *
359 * @param array $get_image_options {
360 * Array of Image options.
361 * @type bool true Should we default to Gravatars when no image is found? Default is true.
362 * @type string $gravatar_default Default Image URL if no Gravatar is found.
363 * @type int $avatar_size Default Image size.
364 * @type mixed $width Image width, not set by default and $avatar_size is used instead.
365 * @type mixed $height Image height, not set by default and $avatar_size is used instead.
366 * }
367 */
368 $get_image_options = apply_filters( 'jetpack_top_posts_widget_image_options', $get_image_options );
369 }
370
371 if ( function_exists( 'wpl_get_blogs_most_liked_posts' ) && 'likes' === $ordering ) {
372 $posts = $this->get_by_likes( $count, $types );
373 } else {
374 $posts = $this->get_by_views( $count, $args, $types );
375 }
376
377 echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
378
379 if ( ! empty( $title ) ) {
380 echo $args['before_title'] . $title . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
381 }
382
383 /*
384 * If we have no posts, add some fallback posts
385 * and display a fallback message for admins.
386 */
387 if ( ! $posts ) {
388 if ( current_user_can( 'edit_theme_options' ) ) {
389 echo self::fallback_message(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
390 }
391
392 $posts = $this->get_fallback_posts( $count, $types );
393 }
394
395 /*
396 * Display our posts.
397 */
398
399 /**
400 * Filter the layout of the Top Posts Widget
401 *
402 * @module widgets
403 *
404 * @since 6.4.0
405 *
406 * @param string $layout layout of the Top Posts Widget (empty string).
407 * @param array $posts IDs of the posts to be displayed.
408 * @param array $display Display option from widget form.
409 */
410 $layout = apply_filters( 'jetpack_top_posts_widget_layout', '', $posts, $display );
411 if ( ! empty( $layout ) ) {
412 echo $layout; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
413 }
414
415 switch ( $display ) {
416 case 'list':
417 case 'grid':
418 // Keep the avatar_size as default dimensions for backward compatibility.
419 $width = (int) $get_image_options['avatar_size'];
420 $height = (int) $get_image_options['avatar_size'];
421
422 // Check if the user has changed the width.
423 if ( ! empty( $get_image_options['width'] ) ) {
424 $width = (int) $get_image_options['width'];
425 }
426
427 // Check if the user has changed the height.
428 if ( ! empty( $get_image_options['height'] ) ) {
429 $height = (int) $get_image_options['height'];
430 }
431
432 foreach ( $posts as &$post ) {
433 $image = Jetpack_PostImages::get_image(
434 $post['post_id'],
435 array(
436 'fallback_to_avatars' => (bool) $get_image_options['fallback_to_avatars'],
437 'width' => (int) $width,
438 'height' => (int) $height,
439 'avatar_size' => (int) $get_image_options['avatar_size'],
440 )
441 );
442
443 if ( $image ) {
444 $post['image'] = Jetpack_PostImages::fit_image_url(
445 $image['src'],
446 $width,
447 $height
448 );
449
450 $post['image_srcset'] = Jetpack_PostImages::generate_cropped_srcset(
451 $image,
452 $width,
453 $height
454 );
455
456 if ( empty( $post['image_srcset'] ) ) {
457 $post['image_srcset'] = "{$post['image']} 1x";
458 }
459 }
460 }
461 unset( $post );
462
463 if ( 'grid' === $display ) {
464 echo "<div class='widgets-grid-layout no-grav'>\n";
465 foreach ( $posts as $post ) {
466 echo '<div class="widget-grid-view-image">';
467
468 /**
469 * Fires before each Top Post result, inside <li>.
470 *
471 * @module widgets
472 *
473 * @since 3.2.0
474 *
475 * @param string $post['post_id'] Post ID.
476 */
477 do_action( 'jetpack_widget_top_posts_before_post', $post['post_id'] );
478
479 /**
480 * Filter the permalink of items in the Top Posts widget.
481 *
482 * @module widgets
483 *
484 * @since 4.4.0
485 *
486 * @param string $post['permalink'] Post permalink.
487 * @param array $post Post array.
488 */
489 $filtered_permalink = apply_filters( 'jetpack_top_posts_widget_permalink', $post['permalink'], $post );
490
491 if ( $post['image'] ) {
492 printf(
493 '<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>',
494 esc_url( $filtered_permalink ),
495 esc_attr( wp_kses( $post['title'], array() ) ),
496 ( get_queried_object_id() === $post['post_id'] ? ' aria-current="page"' : '' ),
497 absint( $width ),
498 absint( $height ),
499 esc_url( $post['image'] ),
500 esc_attr( $post['image_srcset'] )
501 );
502 }
503
504 /**
505 * Fires after each Top Post result, inside <li>.
506 *
507 * @module widgets
508 *
509 * @since 3.2.0
510 *
511 * @param string $post['post_id'] Post ID.
512 */
513 do_action( 'jetpack_widget_top_posts_after_post', $post['post_id'] );
514
515 echo '</div>';
516 }
517 echo "</div>\n";
518 } else {
519 echo "<ul class='widgets-list-layout no-grav'>\n";
520 foreach ( $posts as $post ) {
521 echo '<li>';
522
523 /** This action is documented in modules/widgets/top-posts.php */
524 do_action( 'jetpack_widget_top_posts_before_post', $post['post_id'] );
525
526 /** This filter is documented in modules/widgets/top-posts.php */
527 $filtered_permalink = apply_filters( 'jetpack_top_posts_widget_permalink', $post['permalink'], $post );
528
529 if ( $post['image'] ) {
530 printf(
531 '<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>',
532 esc_url( $filtered_permalink ),
533 esc_attr( wp_kses( $post['title'], array() ) ),
534 ( get_queried_object_id() === $post['post_id'] ? ' aria-current="page"' : '' ),
535 absint( $width ),
536 absint( $height ),
537 esc_url( $post['image'] ),
538 esc_attr( $post['image_srcset'] )
539 );
540 }
541
542 printf(
543 '<div class="widgets-list-layout-links">
544 <a href="%1$s" title="%2$s" class="bump-view" data-bump-view="tp"%3$s>%4$s</a>
545 </div>
546 ',
547 esc_url( $filtered_permalink ),
548 esc_attr( wp_kses( $post['title'], array() ) ),
549 ( get_queried_object_id() === $post['post_id'] ? ' aria-current="page"' : '' ),
550 esc_html( wp_kses( $post['title'], array() ) )
551 );
552
553 /** This action is documented in modules/widgets/top-posts.php */
554 do_action( 'jetpack_widget_top_posts_after_post', $post['post_id'] );
555
556 echo '</li>';
557 }
558 echo "</ul>\n";
559 }
560 break;
561 default:
562 echo '<ul>';
563
564 foreach ( $posts as $post ) {
565 echo '<li>';
566
567 /** This action is documented in modules/widgets/top-posts.php */
568 do_action( 'jetpack_widget_top_posts_before_post', $post['post_id'] );
569
570 /** This filter is documented in modules/widgets/top-posts.php */
571 $filtered_permalink = apply_filters( 'jetpack_top_posts_widget_permalink', $post['permalink'], $post );
572
573 printf(
574 '<a href="%1$s" class="bump-view" data-bump-view="tp"%2$s>%3$s</a>',
575 esc_url( $filtered_permalink ),
576 ( get_queried_object_id() === $post['post_id'] ? ' aria-current="page"' : '' ),
577 esc_html( wp_kses( $post['title'], array() ) )
578 );
579
580 /** This action is documented in modules/widgets/top-posts.php */
581 do_action( 'jetpack_widget_top_posts_after_post', $post['post_id'] );
582
583 echo '</li>';
584 }
585
586 echo '</ul>';
587 break;
588 }
589
590 echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
591 }
592
593 /**
594 * Display a message with recommendations when there are no recorded top posts.
595 *
596 * @return string $fallback_message
597 */
598 private static function fallback_message() {
599 $link = esc_url( Redirect::get_url( 'jetpack-support-getting-more-views-and-traffic' ) );
600 if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
601 $link = 'https://en.support.wordpress.com/getting-more-site-traffic/';
602 }
603
604 $fallback_message = '<p>';
605 $fallback_message .= sprintf(
606 wp_kses(
607 /* Translators: Placeholder: link to the Jetpack support article. */
608 __( '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' ),
609 array(
610 'a' => array(
611 'href' => array(),
612 'target' => array(),
613 ),
614 )
615 ),
616 esc_url( $link )
617 );
618 $fallback_message .= '<p>';
619
620 return $fallback_message;
621 }
622
623 /**
624 * Widget default option values.
625 */
626 public static function defaults() {
627 return array(
628 'title' => esc_html__( 'Top Posts &amp; Pages', 'jetpack' ),
629 'count' => absint( 10 ),
630 'types' => array( 'post', 'page' ),
631 'ordering' => 'views',
632 'display' => 'text',
633 );
634 }
635
636 /**
637 * Get most liked posts
638 *
639 * ONLY TO BE USED IN WPCOM
640 *
641 * @since 8.4.0 Added $types param
642 *
643 * @param int $count The maximum number of posts to be returned.
644 * @param array $types The post types that should be returned. Optional. Defaults to 'post' and 'page'.
645 *
646 * @return array array of posts.
647 */
648 public function get_by_likes( $count, $types = array( 'post', 'page' ) ) {
649 $post_likes = wpl_get_blogs_most_liked_posts();
650 if ( ! $post_likes ) {
651 return array();
652 }
653
654 return $this->get_posts( array_keys( $post_likes ), $count, $types );
655 }
656
657 /**
658 * Get the top posts based on views
659 *
660 * @since 8.4.0 Added $types param
661 *
662 * @param int $count The maximum number of posts to be returned.
663 * @param array $args The widget arguments.
664 * @param array $types The post types that should be returned.
665 *
666 * @return array array of posts. Defaults to 'post' and 'page'.
667 */
668 public function get_by_views( $count, $args, $types = array( 'post', 'page' ) ) {
669 if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
670 $post_views = wp_cache_get( "get_top_posts_$count", 'stats' );
671 if ( false === $post_views ) {
672 $stats_get_daily_history = stats_get_daily_history(
673 false,
674 get_current_blog_id(),
675 'postviews',
676 'post_id',
677 false,
678 2,
679 '',
680 $count * 2 + 10,
681 true
682 );
683 $post_views = array_shift( $stats_get_daily_history );
684 unset( $post_views[0] );
685 wp_cache_add( "get_top_posts_$count", $post_views, 'stats', 1200 );
686 }
687
688 return $this->get_posts( array_keys( $post_views ), $count, $types );
689 }
690
691 /**
692 * Filter the number of days used to calculate Top Posts for the Top Posts widget.
693 * We do not recommend accessing more than 10 days of results at one.
694 * When more than 10 days of results are accessed at once, results should be cached via the WordPress transients API.
695 * Querying for -1 days will give results for an infinite number of days.
696 *
697 * @module widgets
698 *
699 * @since 3.9.3
700 *
701 * @param int 2 Number of days. Default is 2.
702 * @param array $args The widget arguments.
703 */
704 $days = (int) apply_filters( 'jetpack_top_posts_days', 2, $args );
705
706 /** Handling situations where the number of days makes no sense - allows for unlimited days where $days = -1 */
707 if ( 0 === $days || false === $days ) {
708 $days = 2;
709 }
710
711 $query_args = array(
712 'max' => 11,
713 'summarize' => 1,
714 'num' => (int) $days,
715 );
716 $wpcom_stats = new WPCOM_Stats();
717 $post_view_posts = $wpcom_stats->convert_stats_array_to_object(
718 $wpcom_stats->get_top_posts( $query_args )
719 );
720
721 if ( ! isset( $post_view_posts->summary ) || empty( $post_view_posts->summary->postviews ) ) {
722 return array();
723 }
724
725 $post_view_ids = array_filter( wp_list_pluck( $post_view_posts->summary->postviews, 'id' ) );
726
727 if ( ! $post_view_ids ) {
728 return array();
729 }
730
731 return $this->get_posts( $post_view_ids, $count, $types );
732 }
733
734 /**
735 * Get some posts if no posts are found in the stats API
736 *
737 * @since 8.4.0 Added $count and $types
738 *
739 * @param int $count The maximum number of posts to be returned.
740 * @param array $types The post types that should be returned.
741 * @return array
742 */
743 public function get_fallback_posts( $count = 10, $types = array( 'post', 'page' ) ) {
744 $post_query = new WP_Query();
745
746 if ( ! is_array( $types ) || empty( $types ) ) {
747 $types = array( 'post', 'page' );
748 }
749
750 $posts = $post_query->query(
751 array(
752 'posts_per_page' => $count,
753 'post_status' => 'publish',
754 'post_type' => $types,
755 'no_found_rows' => true,
756 'fields' => 'ids',
757 )
758 );
759
760 if ( ! $posts ) {
761 return array();
762 }
763
764 return $this->get_posts( $posts, $count, $types );
765 }
766
767 /**
768 * Get posts from an array of IDs
769 *
770 * @since 8.4.0 Added $types parameters
771 *
772 * @param array $post_ids The post IDs.
773 * @param int $count The maximum number of posts to return.
774 * @param array $types The post types that should be returned. Optional. Defaults to 'post', 'page'.
775 * @return array
776 */
777 public function get_posts( $post_ids, $count, $types = array( 'post', 'page' ) ) {
778 $counter = 0;
779
780 if ( ! is_array( $types ) || empty( $types ) ) {
781 $types = array( 'post', 'page' );
782 }
783
784 $posts = array();
785 foreach ( (array) $post_ids as $post_id ) {
786 $post = get_post( $post_id );
787
788 if ( ! $post ) {
789 continue;
790 }
791
792 /**
793 * Attachment pages use the 'inherit' post status by default.
794 * To be able to remove attachment pages from private and password protect posts,
795 * we need to replace their post status by the parent post' status.
796 */
797 if ( 'inherit' === $post->post_status && 'attachment' === $post->post_type ) {
798 $post->post_status = get_post_status( $post_id );
799 }
800
801 // hide private and password protected posts.
802 if ( 'publish' !== $post->post_status || ! empty( $post->post_password ) ) {
803 continue;
804 }
805
806 // Filter by chosen Post Types.
807 if ( ! in_array( $post->post_type, $types, true ) ) {
808 continue;
809 }
810
811 // Both get HTML stripped etc on display.
812 if ( empty( $post->post_title ) ) {
813 $title_source = $post->post_content;
814 $title = wp_html_excerpt( $title_source, 50 );
815 $title .= '&hellip;';
816 } else {
817 $title = $post->post_title;
818 }
819
820 $permalink = get_permalink( $post->ID );
821
822 $post_type = $post->post_type;
823
824 $posts[] = compact( 'title', 'permalink', 'post_id', 'post_type' );
825 ++$counter;
826
827 if ( $counter == $count ) { // phpcs:ignore Universal.Operators.StrictComparisons.LooseEqual
828 break; // only need to load and show x number of likes.
829 }
830 }
831
832 /**
833 * Filter the Top Posts and Pages.
834 *
835 * @module widgets
836 *
837 * @since 3.0.0
838 *
839 * @param array $posts Array of the most popular posts.
840 * @param array $post_ids Array of Post IDs.
841 * @param string $count Number of Top Posts we want to display.
842 */
843 return apply_filters( 'jetpack_widget_get_top_posts', $posts, $post_ids, $count );
844 }
845 }
846
847 /**
848 * Create a shortcode to display the widget anywhere.
849 *
850 * @since 3.9.2
851 *
852 * @param array $instance Saved values from database.
853 */
854 function jetpack_do_top_posts_widget( $instance ) {
855 // Post Types can't be entered as an array in the shortcode parameters.
856 if ( isset( $instance['types'] ) && is_string( $instance['types'] ) ) {
857 $instance['types'] = explode( ',', $instance['types'] );
858 }
859
860 $instance = shortcode_atts(
861 Jetpack_Top_Posts_Widget::defaults(),
862 $instance,
863 'jetpack_top_posts_widget'
864 );
865
866 // Add a class to allow styling.
867 $args = array(
868 'before_widget' => sprintf( '<div class="%s">', 'jetpack_top_posts_widget' ),
869 );
870
871 ob_start();
872 the_widget( 'Jetpack_Top_Posts_Widget', $instance, $args );
873 $output = ob_get_clean();
874
875 return $output;
876 }
877 add_shortcode( 'jetpack_top_posts_widget', 'jetpack_do_top_posts_widget' );
878