PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 12.1.3
Jetpack – WP Security, Backup, Speed, & Growth v12.1.3
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
850 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, $this->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, $this->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 $this->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
431 $post['image'] = $image['src'];
432 if ( 'blavatar' !== $image['from'] && 'gravatar' !== $image['from'] ) {
433 $post['image'] = jetpack_photon_url( $post['image'], array( 'resize' => "$width,$height" ) );
434 }
435 }
436 }
437 unset( $post );
438
439 if ( 'grid' === $display ) {
440 echo "<div class='widgets-grid-layout no-grav'>\n";
441 foreach ( $posts as $post ) {
442 echo '<div class="widget-grid-view-image">';
443
444 /**
445 * Fires before each Top Post result, inside <li>.
446 *
447 * @module widgets
448 *
449 * @since 3.2.0
450 *
451 * @param string $post['post_id'] Post ID.
452 */
453 do_action( 'jetpack_widget_top_posts_before_post', $post['post_id'] );
454
455 /**
456 * Filter the permalink of items in the Top Posts widget.
457 *
458 * @module widgets
459 *
460 * @since 4.4.0
461 *
462 * @param string $post['permalink'] Post permalink.
463 * @param array $post Post array.
464 */
465 $filtered_permalink = apply_filters( 'jetpack_top_posts_widget_permalink', $post['permalink'], $post );
466
467 if ( $post['image'] ) {
468 printf(
469 '<a href="%1$s" title="%2$s" class="bump-view" data-bump-view="tp"%3$s><img width="%4$d" height="%5$d" src="%6$s" alt="%2$s" data-pin-nopin="true"/></a>',
470 esc_url( $filtered_permalink ),
471 esc_attr( wp_kses( $post['title'], array() ) ),
472 ( get_queried_object_id() === $post['post_id'] ? ' aria-current="page"' : '' ),
473 absint( $width ),
474 absint( $height ),
475 esc_url( $post['image'] )
476 );
477 }
478
479 /**
480 * Fires after each Top Post result, inside <li>.
481 *
482 * @module widgets
483 *
484 * @since 3.2.0
485 *
486 * @param string $post['post_id'] Post ID.
487 */
488 do_action( 'jetpack_widget_top_posts_after_post', $post['post_id'] );
489
490 echo '</div>';
491 }
492 echo "</div>\n";
493 } else {
494 echo "<ul class='widgets-list-layout no-grav'>\n";
495 foreach ( $posts as $post ) {
496 echo '<li>';
497
498 /** This action is documented in modules/widgets/top-posts.php */
499 do_action( 'jetpack_widget_top_posts_before_post', $post['post_id'] );
500
501 /** This filter is documented in modules/widgets/top-posts.php */
502 $filtered_permalink = apply_filters( 'jetpack_top_posts_widget_permalink', $post['permalink'], $post );
503
504 if ( $post['image'] ) {
505 printf(
506 '<a href="%1$s" title="%2$s" class="bump-view" data-bump-view="tp"%3$s><img width="%4$d" height="%5$d" src="%6$s" alt="%2$s" data-pin-nopin="true" class="widgets-list-layout-blavatar" /></a>',
507 esc_url( $filtered_permalink ),
508 esc_attr( wp_kses( $post['title'], array() ) ),
509 ( get_queried_object_id() === $post['post_id'] ? ' aria-current="page"' : '' ),
510 absint( $width ),
511 absint( $height ),
512 esc_url( $post['image'] )
513 );
514 }
515
516 printf(
517 '<div class="widgets-list-layout-links">
518 <a href="%1$s" title="%2$s" class="bump-view" data-bump-view="tp"%3$s>%4$s</a>
519 </div>
520 ',
521 esc_url( $filtered_permalink ),
522 esc_attr( wp_kses( $post['title'], array() ) ),
523 ( get_queried_object_id() === $post['post_id'] ? ' aria-current="page"' : '' ),
524 esc_html( wp_kses( $post['title'], array() ) )
525 );
526
527 /** This action is documented in modules/widgets/top-posts.php */
528 do_action( 'jetpack_widget_top_posts_after_post', $post['post_id'] );
529
530 echo '</li>';
531 }
532 echo "</ul>\n";
533 }
534 break;
535 default:
536 echo '<ul>';
537
538 foreach ( $posts as $post ) {
539 echo '<li>';
540
541 /** This action is documented in modules/widgets/top-posts.php */
542 do_action( 'jetpack_widget_top_posts_before_post', $post['post_id'] );
543
544 /** This filter is documented in modules/widgets/top-posts.php */
545 $filtered_permalink = apply_filters( 'jetpack_top_posts_widget_permalink', $post['permalink'], $post );
546
547 printf(
548 '<a href="%1$s" class="bump-view" data-bump-view="tp"%2$s>%3$s</a>',
549 esc_url( $filtered_permalink ),
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
560 echo '</ul>';
561 break;
562 }
563
564 echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
565 }
566
567 /**
568 * Display a message with recommendations when there are no recorded top posts.
569 *
570 * @return string $fallback_message
571 */
572 private static function fallback_message() {
573 $link = esc_url( Redirect::get_url( 'jetpack-support-getting-more-views-and-traffic' ) );
574 if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
575 $link = 'https://en.support.wordpress.com/getting-more-site-traffic/';
576 }
577
578 $fallback_message = '<p>';
579 $fallback_message .= sprintf(
580 wp_kses(
581 /* Translators: Placeholder: link to the Jetpack support article. */
582 __( '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' ),
583 array(
584 'a' => array(
585 'href' => array(),
586 'target' => array(),
587 ),
588 )
589 ),
590 esc_url( $link )
591 );
592 $fallback_message .= '<p>';
593
594 return $fallback_message;
595 }
596
597 /**
598 * Widget default option values.
599 */
600 public static function defaults() {
601 return array(
602 'title' => esc_html__( 'Top Posts &amp; Pages', 'jetpack' ),
603 'count' => absint( 10 ),
604 'types' => array( 'post', 'page' ),
605 'ordering' => 'views',
606 'display' => 'text',
607 );
608 }
609
610 /**
611 * Get most liked posts
612 *
613 * ONLY TO BE USED IN WPCOM
614 *
615 * @since 8.4.0 Added $types param
616 *
617 * @param int $count The maximum number of posts to be returned.
618 * @param array $types The post types that should be returned. Optional. Defaults to 'post' and 'page'.
619 *
620 * @return array array of posts.
621 */
622 public function get_by_likes( $count, $types = array( 'post', 'page' ) ) {
623 $post_likes = wpl_get_blogs_most_liked_posts();
624 if ( ! $post_likes ) {
625 return array();
626 }
627
628 return $this->get_posts( array_keys( $post_likes ), $count, $types );
629 }
630
631 /**
632 * Get the top posts based on views
633 *
634 * @since 8.4.0 Added $types param
635 *
636 * @param int $count The maximum number of posts to be returned.
637 * @param array $args The widget arguments.
638 * @param array $types The post types that should be returned.
639 *
640 * @return array array of posts. Defaults to 'post' and 'page'.
641 */
642 public function get_by_views( $count, $args, $types = array( 'post', 'page' ) ) {
643 if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
644 $post_views = wp_cache_get( "get_top_posts_$count", 'stats' );
645 if ( false === $post_views ) {
646 $post_views = array_shift(
647 stats_get_daily_history(
648 false,
649 get_current_blog_id(),
650 'postviews',
651 'post_id',
652 false,
653 2,
654 '',
655 $count * 2 + 10,
656 true
657 )
658 );
659 unset( $post_views[0] );
660 wp_cache_add( "get_top_posts_$count", $post_views, 'stats', 1200 );
661 }
662
663 return $this->get_posts( array_keys( $post_views ), $count, $types );
664 }
665
666 /**
667 * Filter the number of days used to calculate Top Posts for the Top Posts widget.
668 * We do not recommend accessing more than 10 days of results at one.
669 * When more than 10 days of results are accessed at once, results should be cached via the WordPress transients API.
670 * Querying for -1 days will give results for an infinite number of days.
671 *
672 * @module widgets
673 *
674 * @since 3.9.3
675 *
676 * @param int 2 Number of days. Default is 2.
677 * @param array $args The widget arguments.
678 */
679 $days = (int) apply_filters( 'jetpack_top_posts_days', 2, $args );
680
681 /** Handling situations where the number of days makes no sense - allows for unlimited days where $days = -1 */
682 if ( 0 === $days || false === $days ) {
683 $days = 2;
684 }
685
686 $query_args = array(
687 'max' => 11,
688 'summarize' => 1,
689 'num' => (int) $days,
690 );
691 $post_view_posts = convert_stats_array_to_object( ( new WPCOM_Stats() )->get_top_posts( $query_args ) );
692
693 if ( ! isset( $post_view_posts->summary ) || empty( $post_view_posts->summary->postviews ) ) {
694 return array();
695 }
696
697 $post_view_ids = array_filter( wp_list_pluck( $post_view_posts->summary->postviews, 'id' ) );
698
699 if ( ! $post_view_ids ) {
700 return array();
701 }
702
703 return $this->get_posts( $post_view_ids, $count, $types );
704 }
705
706 /**
707 * Get some posts if no posts are found in the stats API
708 *
709 * @since 8.4.0 Added $count and $types
710 *
711 * @param int $count The maximum number of posts to be returned.
712 * @param array $types The post types that should be returned.
713 * @return array
714 */
715 public function get_fallback_posts( $count = 10, $types = array( 'post', 'page' ) ) {
716 $post_query = new WP_Query();
717
718 if ( ! is_array( $types ) || empty( $types ) ) {
719 $types = array( 'post', 'page' );
720 }
721
722 $posts = $post_query->query(
723 array(
724 'posts_per_page' => $count,
725 'post_status' => 'publish',
726 'post_type' => $types,
727 'no_found_rows' => true,
728 'fields' => 'ids',
729 )
730 );
731
732 if ( ! $posts ) {
733 return array();
734 }
735
736 return $this->get_posts( $posts, $count, $types );
737 }
738
739 /**
740 * Get posts from an array of IDs
741 *
742 * @since 8.4.0 Added $types parameters
743 *
744 * @param array $post_ids The post IDs.
745 * @param int $count The maximum number of posts to return.
746 * @param array $types The post types that should be returned. Optional. Defaults to 'post', 'page'.
747 * @return array
748 */
749 public function get_posts( $post_ids, $count, $types = array( 'post', 'page' ) ) {
750 $counter = 0;
751
752 if ( ! is_array( $types ) || empty( $types ) ) {
753 $types = array( 'post', 'page' );
754 }
755
756 $posts = array();
757 foreach ( (array) $post_ids as $post_id ) {
758 $post = get_post( $post_id );
759
760 if ( ! $post ) {
761 continue;
762 }
763
764 /**
765 * Attachment pages use the 'inherit' post status by default.
766 * To be able to remove attachment pages from private and password protect posts,
767 * we need to replace their post status by the parent post' status.
768 */
769 if ( 'inherit' === $post->post_status && 'attachment' === $post->post_type ) {
770 $post->post_status = get_post_status( $post_id );
771 }
772
773 // hide private and password protected posts.
774 if ( 'publish' !== $post->post_status || ! empty( $post->post_password ) ) {
775 continue;
776 }
777
778 // Filter by chosen Post Types.
779 if ( ! in_array( $post->post_type, $types, true ) ) {
780 continue;
781 }
782
783 // Both get HTML stripped etc on display.
784 if ( empty( $post->post_title ) ) {
785 $title_source = $post->post_content;
786 $title = wp_html_excerpt( $title_source, 50 );
787 $title .= '&hellip;';
788 } else {
789 $title = $post->post_title;
790 }
791
792 $permalink = get_permalink( $post->ID );
793
794 $post_type = $post->post_type;
795
796 $posts[] = compact( 'title', 'permalink', 'post_id', 'post_type' );
797 ++$counter;
798
799 if ( $counter == $count ) { // phpcs:ignore Universal.Operators.StrictComparisons.LooseEqual
800 break; // only need to load and show x number of likes.
801 }
802 }
803
804 /**
805 * Filter the Top Posts and Pages.
806 *
807 * @module widgets
808 *
809 * @since 3.0.0
810 *
811 * @param array $posts Array of the most popular posts.
812 * @param array $post_ids Array of Post IDs.
813 * @param string $count Number of Top Posts we want to display.
814 */
815 return apply_filters( 'jetpack_widget_get_top_posts', $posts, $post_ids, $count );
816 }
817 }
818
819 /**
820 * Create a shortcode to display the widget anywhere.
821 *
822 * @since 3.9.2
823 *
824 * @param array $instance Saved values from database.
825 */
826 function jetpack_do_top_posts_widget( $instance ) {
827 // Post Types can't be entered as an array in the shortcode parameters.
828 if ( isset( $instance['types'] ) && is_string( $instance['types'] ) ) {
829 $instance['types'] = explode( ',', $instance['types'] );
830 }
831
832 $instance = shortcode_atts(
833 Jetpack_Top_Posts_Widget::defaults(),
834 $instance,
835 'jetpack_top_posts_widget'
836 );
837
838 // Add a class to allow styling.
839 $args = array(
840 'before_widget' => sprintf( '<div class="%s">', 'jetpack_top_posts_widget' ),
841 );
842
843 ob_start();
844 the_widget( 'Jetpack_Top_Posts_Widget', $instance, $args );
845 $output = ob_get_clean();
846
847 return $output;
848 }
849 add_shortcode( 'jetpack_top_posts_widget', 'jetpack_do_top_posts_widget' );
850