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