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