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