PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 13.9.2
Jetpack – WP Security, Backup, Speed, & Growth v13.9.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 2 years ago goodreads 2 years ago google-translate 4 years ago image-widget 8 years ago instagram 6 years ago internet-defense-league 2 years ago migrate-to-core 3 years ago milestone 2 years ago my-community 8 years ago simple-payments 2 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 2 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 2 years ago goodreads.php 2 years ago google-translate.php 2 years ago gravatar-profile.css 10 years ago gravatar-profile.php 2 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 2 years ago wordpress-post-widget.php 4 years ago
top-posts.php
879 lines
1 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2 /**
3 * Top Posts widget.
4 *
5 * Currently, this widget depends on the Stats Module. To not load this file
6 * when the Stats Module is not active would potentially bypass Jetpack's
7 * fatal error detection on module activation, so we always load this file.
8 * Instead, we don't register the widget if the Stats Module isn't active.
9 *
10 * @package automattic/jetpack
11 */
12
13 // phpcs:disable Universal.Files.SeparateFunctionsFromOO.Mixed -- TODO: Move classes to appropriately-named class files.
14
15 use Automattic\Jetpack\Redirect;
16 use Automattic\Jetpack\Stats\WPCOM_Stats;
17 use Automattic\Jetpack\Status;
18 use Automattic\Jetpack\Status\Host;
19
20 // Register the widget for use in Appearance -> Widgets
21 add_action( 'widgets_init', 'jetpack_top_posts_widget_init' );
22
23 /**
24 * Register the widget, if the Stats module is active.
25 */
26 function jetpack_top_posts_widget_init() {
27 // Currently, this widget depends on the Stats Module.
28 if (
29 ! ( defined( 'IS_WPCOM' ) && IS_WPCOM )
30 && (
31 ! Jetpack::is_module_active( 'stats' )
32 || ( new Status() )->is_offline_mode()
33 )
34 ) {
35 return;
36 }
37
38 register_widget( 'Jetpack_Top_Posts_Widget' );
39 }
40
41 /**
42 * Widget class.
43 */
44 class Jetpack_Top_Posts_Widget extends WP_Widget {
45 /**
46 * Widget unique identifier.
47 *
48 * @var string
49 */
50 public $alt_option_name = 'widget_stats_topposts';
51
52 /**
53 * Widget default title.
54 *
55 * @var string
56 */
57 public $default_title = '';
58
59 /**
60 * Constructor.
61 */
62 public function __construct() {
63 parent::__construct(
64 'top-posts',
65 /** This filter is documented in modules/widgets/facebook-likebox.php */
66 apply_filters( 'jetpack_widget_name', __( 'Top Posts &amp; Pages', 'jetpack' ) ),
67 array(
68 'description' => __( 'Shows your most viewed posts and pages.', 'jetpack' ),
69 'customize_selective_refresh' => true,
70 'show_instance_in_rest' => true,
71 )
72 );
73
74 $this->default_title = __( 'Top Posts &amp; Pages', 'jetpack' );
75
76 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 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 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 (int) $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 $count = isset( $instance['count'] ) ? (int) $instance['count'] : false;
310 if ( $count < 1 || 10 < $count ) {
311 $count = 10;
312 }
313 /**
314 * Control the number of displayed posts.
315 *
316 * @module widgets
317 *
318 * @since 3.3.0
319 *
320 * @param string $count Number of Posts displayed in the Top Posts widget. Default is 10.
321 */
322 $count = apply_filters( 'jetpack_top_posts_widget_count', $count );
323
324 $types = isset( $instance['types'] ) ? (array) $instance['types'] : array( 'post', 'page' );
325
326 // 'likes' are not available in Jetpack
327 $ordering = isset( $instance['ordering'] ) && 'likes' === $instance['ordering']
328 ? 'likes'
329 : 'views';
330
331 if (
332 isset( $instance['display'] )
333 && in_array( $instance['display'], array( 'grid', 'list', 'text' ), true )
334 ) {
335 $display = $instance['display'];
336 } else {
337 $display = 'text';
338 }
339
340 $get_image_options = array();
341 if ( 'text' !== $display ) {
342 $get_image_options = array(
343 'fallback_to_avatars' => true,
344 /** This filter is documented in modules/stats.php */
345 'gravatar_default' => apply_filters( 'jetpack_static_url', set_url_scheme( 'https://en.wordpress.com/i/logo/white-gray-80.png' ) ),
346 'avatar_size' => 40,
347 'width' => null,
348 'height' => null,
349 );
350 if ( 'grid' === $display ) {
351 $get_image_options['avatar_size'] = 200;
352 }
353 /**
354 * Top Posts Widget Image options.
355 *
356 * @module widgets
357 *
358 * @since 1.8.0
359 *
360 * @param array $get_image_options {
361 * Array of Image options.
362 * @type bool true Should we default to Gravatars when no image is found? Default is true.
363 * @type string $gravatar_default Default Image URL if no Gravatar is found.
364 * @type int $avatar_size Default Image size.
365 * @type mixed $width Image width, not set by default and $avatar_size is used instead.
366 * @type mixed $height Image height, not set by default and $avatar_size is used instead.
367 * }
368 */
369 $get_image_options = apply_filters( 'jetpack_top_posts_widget_image_options', $get_image_options );
370 }
371
372 if ( function_exists( 'wpl_get_blogs_most_liked_posts' ) && 'likes' === $ordering ) {
373 $posts = $this->get_by_likes( $count, $types );
374 } else {
375 $posts = $this->get_by_views( $count, $args, $types );
376 }
377
378 echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
379
380 if ( ! empty( $title ) ) {
381 echo $args['before_title'] . $title . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
382 }
383
384 /*
385 * If we have no posts, add some fallback posts
386 * and display a fallback message for admins.
387 */
388 if ( ! $posts ) {
389 if ( current_user_can( 'edit_theme_options' ) ) {
390 echo self::fallback_message(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
391 }
392
393 $posts = $this->get_fallback_posts( $count, $types );
394 }
395
396 /*
397 * Display our posts.
398 */
399
400 /**
401 * Filter the layout of the Top Posts Widget
402 *
403 * @module widgets
404 *
405 * @since 6.4.0
406 *
407 * @param string $layout layout of the Top Posts Widget (empty string).
408 * @param array $posts IDs of the posts to be displayed.
409 * @param array $display Display option from widget form.
410 */
411 $layout = apply_filters( 'jetpack_top_posts_widget_layout', '', $posts, $display );
412 if ( ! empty( $layout ) ) {
413 echo $layout; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
414 }
415
416 switch ( $display ) {
417 case 'list':
418 case 'grid':
419 // Keep the avatar_size as default dimensions for backward compatibility.
420 $width = (int) $get_image_options['avatar_size'];
421 $height = (int) $get_image_options['avatar_size'];
422
423 // Check if the user has changed the width.
424 if ( ! empty( $get_image_options['width'] ) ) {
425 $width = (int) $get_image_options['width'];
426 }
427
428 // Check if the user has changed the height.
429 if ( ! empty( $get_image_options['height'] ) ) {
430 $height = (int) $get_image_options['height'];
431 }
432
433 foreach ( $posts as &$post ) {
434 $image = Jetpack_PostImages::get_image(
435 $post['post_id'],
436 array(
437 'fallback_to_avatars' => (bool) $get_image_options['fallback_to_avatars'],
438 'width' => (int) $width,
439 'height' => (int) $height,
440 'avatar_size' => (int) $get_image_options['avatar_size'],
441 )
442 );
443
444 if ( $image ) {
445 $post['image'] = Jetpack_PostImages::fit_image_url(
446 $image['src'],
447 $width,
448 $height
449 );
450
451 $post['image_srcset'] = Jetpack_PostImages::generate_cropped_srcset(
452 $image,
453 $width,
454 $height
455 );
456
457 if ( empty( $post['image_srcset'] ) ) {
458 $post['image_srcset'] = "{$post['image']} 1x";
459 }
460 }
461 }
462 unset( $post );
463
464 if ( 'grid' === $display ) {
465 echo "<div class='widgets-grid-layout no-grav'>\n";
466 foreach ( $posts as $post ) {
467 echo '<div class="widget-grid-view-image">';
468
469 /**
470 * Fires before each Top Post result, inside <li>.
471 *
472 * @module widgets
473 *
474 * @since 3.2.0
475 *
476 * @param string $post['post_id'] Post ID.
477 */
478 do_action( 'jetpack_widget_top_posts_before_post', $post['post_id'] );
479
480 /**
481 * Filter the permalink of items in the Top Posts widget.
482 *
483 * @module widgets
484 *
485 * @since 4.4.0
486 *
487 * @param string $post['permalink'] Post permalink.
488 * @param array $post Post array.
489 */
490 $filtered_permalink = apply_filters( 'jetpack_top_posts_widget_permalink', $post['permalink'], $post );
491
492 if ( $post['image'] ) {
493 printf(
494 '<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>',
495 esc_url( $filtered_permalink ),
496 esc_attr( wp_kses( $post['title'], array() ) ),
497 ( get_queried_object_id() === $post['post_id'] ? ' aria-current="page"' : '' ),
498 absint( $width ),
499 absint( $height ),
500 esc_url( $post['image'] ),
501 esc_attr( $post['image_srcset'] )
502 );
503 }
504
505 /**
506 * Fires after each Top Post result, inside <li>.
507 *
508 * @module widgets
509 *
510 * @since 3.2.0
511 *
512 * @param string $post['post_id'] Post ID.
513 */
514 do_action( 'jetpack_widget_top_posts_after_post', $post['post_id'] );
515
516 echo '</div>';
517 }
518 echo "</div>\n";
519 } else {
520 echo "<ul class='widgets-list-layout no-grav'>\n";
521 foreach ( $posts as $post ) {
522 echo '<li>';
523
524 /** This action is documented in modules/widgets/top-posts.php */
525 do_action( 'jetpack_widget_top_posts_before_post', $post['post_id'] );
526
527 /** This filter is documented in modules/widgets/top-posts.php */
528 $filtered_permalink = apply_filters( 'jetpack_top_posts_widget_permalink', $post['permalink'], $post );
529
530 if ( $post['image'] ) {
531 printf(
532 '<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>',
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 absint( $width ),
537 absint( $height ),
538 esc_url( $post['image'] ),
539 esc_attr( $post['image_srcset'] )
540 );
541 }
542
543 printf(
544 '<div class="widgets-list-layout-links">
545 <a href="%1$s" title="%2$s" class="bump-view" data-bump-view="tp"%3$s>%4$s</a>
546 </div>
547 ',
548 esc_url( $filtered_permalink ),
549 esc_attr( wp_kses( $post['title'], array() ) ),
550 ( get_queried_object_id() === $post['post_id'] ? ' aria-current="page"' : '' ),
551 esc_html( wp_kses( $post['title'], array() ) )
552 );
553
554 /** This action is documented in modules/widgets/top-posts.php */
555 do_action( 'jetpack_widget_top_posts_after_post', $post['post_id'] );
556
557 echo '</li>';
558 }
559 echo "</ul>\n";
560 }
561 break;
562 default:
563 echo '<ul>';
564
565 foreach ( $posts as $post ) {
566 echo '<li>';
567
568 /** This action is documented in modules/widgets/top-posts.php */
569 do_action( 'jetpack_widget_top_posts_before_post', $post['post_id'] );
570
571 /** This filter is documented in modules/widgets/top-posts.php */
572 $filtered_permalink = apply_filters( 'jetpack_top_posts_widget_permalink', $post['permalink'], $post );
573
574 printf(
575 '<a href="%1$s" class="bump-view" data-bump-view="tp"%2$s>%3$s</a>',
576 esc_url( $filtered_permalink ),
577 ( get_queried_object_id() === $post['post_id'] ? ' aria-current="page"' : '' ),
578 esc_html( wp_kses( $post['title'], array() ) )
579 );
580
581 /** This action is documented in modules/widgets/top-posts.php */
582 do_action( 'jetpack_widget_top_posts_after_post', $post['post_id'] );
583
584 echo '</li>';
585 }
586
587 echo '</ul>';
588 break;
589 }
590
591 echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
592 }
593
594 /**
595 * Display a message with recommendations when there are no recorded top posts.
596 *
597 * @return string $fallback_message
598 */
599 private static function fallback_message() {
600 $link = esc_url( Redirect::get_url( 'jetpack-support-getting-more-views-and-traffic' ) );
601 if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
602 $link = 'https://en.support.wordpress.com/getting-more-site-traffic/';
603 }
604
605 $fallback_message = '<p>';
606 $fallback_message .= sprintf(
607 wp_kses(
608 /* Translators: Placeholder: link to the Jetpack support article. */
609 __( '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' ),
610 array(
611 'a' => array(
612 'href' => array(),
613 'target' => array(),
614 ),
615 )
616 ),
617 esc_url( $link )
618 );
619 $fallback_message .= '<p>';
620
621 return $fallback_message;
622 }
623
624 /**
625 * Widget default option values.
626 */
627 public static function defaults() {
628 return array(
629 'title' => esc_html__( 'Top Posts &amp; Pages', 'jetpack' ),
630 'count' => absint( 10 ),
631 'types' => array( 'post', 'page' ),
632 'ordering' => 'views',
633 'display' => 'text',
634 );
635 }
636
637 /**
638 * Get most liked posts
639 *
640 * ONLY TO BE USED IN WPCOM
641 *
642 * @since 8.4.0 Added $types param
643 *
644 * @param int $count The maximum number of posts to be returned.
645 * @param array $types The post types that should be returned. Optional. Defaults to 'post' and 'page'.
646 *
647 * @return array array of posts.
648 */
649 public function get_by_likes( $count, $types = array( 'post', 'page' ) ) {
650 $post_likes = wpl_get_blogs_most_liked_posts();
651 if ( ! $post_likes ) {
652 return array();
653 }
654
655 return $this->get_posts( array_keys( $post_likes ), $count, $types );
656 }
657
658 /**
659 * Get the top posts based on views
660 *
661 * @since 8.4.0 Added $types param
662 *
663 * @param int $count The maximum number of posts to be returned.
664 * @param array $args The widget arguments.
665 * @param array $types The post types that should be returned.
666 *
667 * @return array array of posts. Defaults to 'post' and 'page'.
668 */
669 public function get_by_views( $count, $args, $types = array( 'post', 'page' ) ) {
670 if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
671 $post_views = wp_cache_get( "get_top_posts_$count", 'stats' );
672 if ( false === $post_views ) {
673 $stats_get_daily_history = stats_get_daily_history(
674 false,
675 get_current_blog_id(),
676 'postviews',
677 'post_id',
678 false,
679 2,
680 '',
681 $count * 2 + 10,
682 true
683 );
684 $post_views = array_shift( $stats_get_daily_history );
685 unset( $post_views[0] );
686 wp_cache_add( "get_top_posts_$count", $post_views, 'stats', 1200 );
687 }
688
689 return $this->get_posts( array_keys( $post_views ), $count, $types );
690 }
691
692 /**
693 * Filter the number of days used to calculate Top Posts for the Top Posts widget.
694 * We do not recommend accessing more than 10 days of results at one.
695 * When more than 10 days of results are accessed at once, results should be cached via the WordPress transients API.
696 * Querying for -1 days will give results for an infinite number of days.
697 *
698 * @module widgets
699 *
700 * @since 3.9.3
701 *
702 * @param int 2 Number of days. Default is 2.
703 * @param array $args The widget arguments.
704 */
705 $days = (int) apply_filters( 'jetpack_top_posts_days', 2, $args );
706
707 /** Handling situations where the number of days makes no sense - allows for unlimited days where $days = -1 */
708 if ( 0 === $days || false === $days ) {
709 $days = 2;
710 }
711
712 $query_args = array(
713 'max' => 11,
714 'summarize' => 1,
715 'num' => (int) $days,
716 );
717 $wpcom_stats = new WPCOM_Stats();
718 $post_view_posts = $wpcom_stats->convert_stats_array_to_object(
719 $wpcom_stats->get_top_posts( $query_args )
720 );
721
722 if ( ! isset( $post_view_posts->summary ) || empty( $post_view_posts->summary->postviews ) ) {
723 return array();
724 }
725
726 $post_view_ids = array_filter( wp_list_pluck( $post_view_posts->summary->postviews, 'id' ) );
727
728 if ( ! $post_view_ids ) {
729 return array();
730 }
731
732 return $this->get_posts( $post_view_ids, $count, $types );
733 }
734
735 /**
736 * Get some posts if no posts are found in the stats API
737 *
738 * @since 8.4.0 Added $count and $types
739 *
740 * @param int $count The maximum number of posts to be returned.
741 * @param array $types The post types that should be returned.
742 * @return array
743 */
744 public function get_fallback_posts( $count = 10, $types = array( 'post', 'page' ) ) {
745 $post_query = new WP_Query();
746
747 if ( ! is_array( $types ) || empty( $types ) ) {
748 $types = array( 'post', 'page' );
749 }
750
751 $posts = $post_query->query(
752 array(
753 'posts_per_page' => $count,
754 'post_status' => 'publish',
755 'post_type' => $types,
756 'no_found_rows' => true,
757 'fields' => 'ids',
758 )
759 );
760
761 if ( ! $posts ) {
762 return array();
763 }
764
765 return $this->get_posts( $posts, $count, $types );
766 }
767
768 /**
769 * Get posts from an array of IDs
770 *
771 * @since 8.4.0 Added $types parameters
772 *
773 * @param array $post_ids The post IDs.
774 * @param int $count The maximum number of posts to return.
775 * @param array $types The post types that should be returned. Optional. Defaults to 'post', 'page'.
776 * @return array
777 */
778 public function get_posts( $post_ids, $count, $types = array( 'post', 'page' ) ) {
779 $counter = 0;
780
781 if ( ! is_array( $types ) || empty( $types ) ) {
782 $types = array( 'post', 'page' );
783 }
784
785 $posts = array();
786 foreach ( (array) $post_ids as $post_id ) {
787 $post = get_post( $post_id );
788
789 if ( ! $post ) {
790 continue;
791 }
792
793 /**
794 * Attachment pages use the 'inherit' post status by default.
795 * To be able to remove attachment pages from private and password protect posts,
796 * we need to replace their post status by the parent post' status.
797 */
798 if ( 'inherit' === $post->post_status && 'attachment' === $post->post_type ) {
799 $post->post_status = get_post_status( $post_id );
800 }
801
802 // hide private and password protected posts.
803 if ( 'publish' !== $post->post_status || ! empty( $post->post_password ) ) {
804 continue;
805 }
806
807 // Filter by chosen Post Types.
808 if ( ! in_array( $post->post_type, $types, true ) ) {
809 continue;
810 }
811
812 // Both get HTML stripped etc on display.
813 if ( empty( $post->post_title ) ) {
814 $title_source = $post->post_content;
815 $title = wp_html_excerpt( $title_source, 50 );
816 $title .= '&hellip;';
817 } else {
818 $title = $post->post_title;
819 }
820
821 $permalink = get_permalink( $post->ID );
822
823 $post_type = $post->post_type;
824
825 $posts[] = compact( 'title', 'permalink', 'post_id', 'post_type' );
826 ++$counter;
827
828 if ( $counter == $count ) { // phpcs:ignore Universal.Operators.StrictComparisons.LooseEqual
829 break; // only need to load and show x number of likes.
830 }
831 }
832
833 /**
834 * Filter the Top Posts and Pages.
835 *
836 * @module widgets
837 *
838 * @since 3.0.0
839 *
840 * @param array $posts Array of the most popular posts.
841 * @param array $post_ids Array of Post IDs.
842 * @param string $count Number of Top Posts we want to display.
843 */
844 return apply_filters( 'jetpack_widget_get_top_posts', $posts, $post_ids, $count );
845 }
846 }
847
848 /**
849 * Create a shortcode to display the widget anywhere.
850 *
851 * @since 3.9.2
852 *
853 * @param array $instance Saved values from database.
854 */
855 function jetpack_do_top_posts_widget( $instance ) {
856 // Post Types can't be entered as an array in the shortcode parameters.
857 if ( isset( $instance['types'] ) && is_string( $instance['types'] ) ) {
858 $instance['types'] = explode( ',', $instance['types'] );
859 }
860
861 $instance = shortcode_atts(
862 Jetpack_Top_Posts_Widget::defaults(),
863 $instance,
864 'jetpack_top_posts_widget'
865 );
866
867 // Add a class to allow styling.
868 $args = array(
869 'before_widget' => sprintf( '<div class="%s">', 'jetpack_top_posts_widget' ),
870 );
871
872 ob_start();
873 the_widget( 'Jetpack_Top_Posts_Widget', $instance, $args );
874 $output = ob_get_clean();
875
876 return $output;
877 }
878 add_shortcode( 'jetpack_top_posts_widget', 'jetpack_do_top_posts_widget' );
879