PluginProbe
WebberZone Top 10 — Popular Posts / trunk
WebberZone Top 10 — Popular Posts vtrunk
4.5.0 4.4.3 4.4.2 4.4.1 4.4.0 4.3.4 4.3.3 4.3.2 4.3.1 4.3.0 trunk 1.0 1.0.1 1.1 1.2 1.3 1.4 1.4.1 1.5 1.5.1 1.5.2 1.5.3 1.6 1.6.1 1.6.2 All 116 releases
top-10 / includes / admin / class-dashboard-widgets.php

class-dashboard-widgets.php in WebberZone Top 10 — Popular Posts trunk, at includes/admin/class-dashboard-widgets.php

494 lines 15.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Dashboard Widgets class.
4 *
5 * @package WebberZone\Top_Ten\Admin
6 */
7
8 namespace WebberZone\Top_Ten\Admin;
9
10 use WebberZone\Top_Ten\Util\Helpers;
11
12 if ( ! defined( 'WPINC' ) ) {
13 die;
14 }
15
16 /**
17 * Registers the Top 10 dashboard widgets and At a Glance items.
18 *
19 * @since 3.3.0
20 */
21 class Dashboard_Widgets {
22
23 /**
24 * Constructor class.
25 *
26 * @since 3.3.0
27 */
28 public function __construct() {
29 add_filter( 'wp_dashboard_setup', array( $this, 'wp_dashboard_setup' ) );
30 add_filter( 'wp_network_dashboard_setup', array( $this, 'wp_network_dashboard_setup' ) );
31 add_filter( 'dashboard_glance_items', array( $this, 'dashboard_glance_items' ) );
32 add_action( 'admin_head-index.php', array( $this, 'dashboard_glance_styles' ) );
33 }
34
35 /**
36 * Clear cached network dashboard widget output.
37 *
38 * @since 4.5.0
39 */
40 public static function clear_network_dashboard_cache() {
41 update_site_option( 'tptn_network_dashboard_cache_version', wp_generate_uuid4() );
42 delete_site_transient( 'tptn_network_popular_posts_count' );
43 }
44
45 /**
46 * Add Top 10 views from the current hour to the At a Glance widget.
47 *
48 * @since 4.3.0
49 *
50 * @param string[] $items Array of extra At a Glance items.
51 * @return string[] Modified array of extra At a Glance items.
52 */
53 public function dashboard_glance_items( $items ) {
54 if ( ! current_user_can( 'manage_options' ) && ! \tptn_get_option( 'show_count_non_admins' ) ) {
55 return $items;
56 }
57
58 $current_hour = current_time( 'Y-m-d H' );
59 $hour_views = self::get_current_hour_views();
60 $day_views = self::get_views_for_period( current_time( 'Y-m-d 00' ), $current_hour );
61
62 $items[] = self::get_glance_item(
63 $hour_views,
64 /* translators: %s: Number of views in the last hour. */
65 _n( '%s view in the last hour', '%s views in the last hour', $hour_views, 'top-10' ),
66 admin_url( 'admin.php?page=tptn_dashboard' )
67 );
68
69 $items[] = self::get_glance_item(
70 $day_views,
71 /* translators: %s: Number of views in the past day. */
72 _n( '%s view in the past day', '%s views in the past day', $day_views, 'top-10' ),
73 wp_nonce_url(
74 add_query_arg(
75 array(
76 'page' => 'tptn_dashboard',
77 'post-date-filter-from' => current_time( 'd M Y' ),
78 'post-date-filter-to' => current_time( 'd M Y' ),
79 ),
80 admin_url( 'admin.php' )
81 ),
82 'tptn-dashboard'
83 )
84 );
85
86 return $items;
87 }
88
89 /**
90 * Add a better icon for Top 10 At a Glance items.
91 *
92 * @since 4.3.0
93 */
94 public function dashboard_glance_styles() {
95 ?>
96 <style>
97 #dashboard_right_now li a.tptn-glance-views:before {
98 content: "\f239";
99 content: "\f239" / "";
100 }
101 </style>
102 <?php
103 }
104
105 /**
106 * Get the number of views recorded in the current hour.
107 *
108 * Top 10 stores daily counts in hourly buckets, so this returns the current
109 * hour bucket rather than a rolling 60-minute window.
110 *
111 * @since 4.3.0
112 *
113 * @return int Number of views in the current hour.
114 */
115 public static function get_current_hour_views() {
116 return self::get_views_for_period( current_time( 'Y-m-d H' ), current_time( 'Y-m-d H' ) );
117 }
118
119 /**
120 * Get the number of views recorded in a period.
121 *
122 * @since 4.3.0
123 *
124 * @param string $from_hour Start hour in Y-m-d H format.
125 * @param string $to_hour End hour in Y-m-d H format.
126 * @return int Number of views in the period.
127 */
128 public static function get_views_for_period( $from_hour, $to_hour ) {
129 global $wpdb;
130
131 $table = $wpdb->base_prefix . 'top_ten_daily';
132 $sql = $wpdb->prepare(
133 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
134 "SELECT SUM(cntaccess) FROM {$table} WHERE dp_date >= %s AND dp_date <= %s AND blog_id = %d",
135 $from_hour,
136 $to_hour,
137 get_current_blog_id()
138 );
139 $views = $wpdb->get_var( $sql ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
140
141 return (int) $views;
142 }
143
144 /**
145 * Build a Top 10 At a Glance item.
146 *
147 * @since 4.3.0
148 *
149 * @param int $views Views.
150 * @param string $label Label with a %s placeholder for the formatted count.
151 * @param string $url Link URL.
152 * @return string At a Glance item markup.
153 */
154 public static function get_glance_item( $views, $label, $url ) {
155 return sprintf(
156 '<a class="tptn-glance-views" href="%1$s">%2$s</a>',
157 esc_url( $url ),
158 esc_html( sprintf( $label, Helpers::number_format_i18n( $views ) ) )
159 );
160 }
161
162 /**
163 * Function to add the widgets to the Dashboard.
164 *
165 * @since 3.3.0
166 */
167 public static function wp_dashboard_setup() {
168
169 /**
170 * Filter whether to register the dashboard widgets.
171 *
172 * @since 3.3.0
173 *
174 * @param bool $dashboard_setup Whether to register the dashboard widgets.
175 */
176 $dashboard_setup = apply_filters( 'tptn_dashboard_setup', true );
177
178 if ( $dashboard_setup && ( current_user_can( 'manage_options' ) || \tptn_get_option( 'show_count_non_admins' ) ) ) {
179
180 // Add the overall popular posts widget.
181 wp_add_dashboard_widget(
182 'tptn_overall_dashboard',
183 __( 'Top 10 - Overall Popular Posts', 'top-10' ),
184 array( __CLASS__, 'popular_posts_widget' ),
185 array( __CLASS__, 'pop_display' )
186 );
187
188 // Add the daily popular posts widget.
189 wp_add_dashboard_widget(
190 'tptn_daily_dashboard',
191 /* translators: %s: Custom period label (e.g. Daily, Custom (7 days)). */
192 sprintf( __( 'Top 10 - %s Popular Posts', 'top-10' ), Helpers::get_daily_range_label( 1 ) ),
193 array( __CLASS__, 'popular_posts_widget_daily' ),
194 array( __CLASS__, 'pop_display' )
195 );
196
197 // Add the mini views overview widget.
198 wp_add_dashboard_widget(
199 'tptn_views_over_time_dashboard',
200 __( 'Top 10 Views Overview', 'top-10' ),
201 array( __CLASS__, 'views_over_time_widget' ),
202 );
203 }
204 }
205
206 /**
207 * Function to add the widgets to the Network Dashboard.
208 *
209 * @since 4.2.0
210 */
211 public static function wp_network_dashboard_setup() {
212
213 /**
214 * Filter whether to register the network dashboard widgets.
215 *
216 * @since 4.2.0
217 *
218 * @param bool $dashboard_setup Whether to register the network dashboard widgets.
219 */
220 $dashboard_setup = apply_filters( 'tptn_network_dashboard_setup', true );
221
222 if ( $dashboard_setup && current_user_can( 'manage_network' ) ) {
223
224 // Add the overall popular posts widget.
225 wp_add_dashboard_widget(
226 'tptn_network_overall_dashboard',
227 __( 'Top 10 - Network Popular Posts', 'top-10' ),
228 array( __CLASS__, 'network_popular_posts_widget' ),
229 array( __CLASS__, 'pop_display' )
230 );
231
232 // Add the daily popular posts widget.
233 wp_add_dashboard_widget(
234 'tptn_network_daily_dashboard',
235 /* translators: %s: Custom period label (e.g. Daily, Custom (7 days)). */
236 sprintf( __( 'Top 10 - Network %s Popular Posts', 'top-10' ), Helpers::get_daily_range_label( 1 ) ),
237 array( __CLASS__, 'network_popular_posts_widget_daily' ),
238 array( __CLASS__, 'pop_display' )
239 );
240
241 // Add the mini views overview widget to network dashboard.
242 wp_add_dashboard_widget(
243 'tptn_network_views_over_time_dashboard',
244 __( 'Top 10 Network Views Overview', 'top-10' ),
245 array( __CLASS__, 'views_over_time_widget' ),
246 );
247 }
248 }
249
250 /**
251 * Create the Dashboard Widget and content of the Popular pages
252 *
253 * @since 3.3.0
254 *
255 * @param bool $daily Switch for Custom Period or Overall popular posts.
256 * @param int $page Which page of the lists are on.
257 * @param int $limit Maximum number of posts per page.
258 * @param bool $widget Is this a WordPress widget.
259 * @param bool $network Whether to show network-wide posts.
260 * @return string Formatted list of popular posts
261 */
262 public static function pop_display( $daily = false, $page = 0, $limit = 0, $widget = true, $network = false ) {
263
264 if ( ! $limit ) {
265 $limit = \tptn_get_option( 'limit' );
266 }
267
268 $cache_key = '';
269 $cache_ttl = 0;
270 if ( $network && $widget ) {
271 /**
272 * Filters how long the network dashboard widget output is cached.
273 *
274 * @since 4.5.0
275 *
276 * @param int $cache_ttl Cache lifetime in seconds. Default 15 minutes.
277 */
278 $cache_ttl = max( 0, (int) apply_filters( 'tptn_network_dashboard_cache_ttl', 15 * MINUTE_IN_SECONDS ) );
279 $cache_key = 'tptn_network_dashboard_' . md5(
280 (string) wp_json_encode(
281 array(
282 'version' => get_site_option( 'tptn_network_dashboard_cache_version', '1' ),
283 'daily' => (bool) $daily,
284 'orderby' => $daily ? 'daily_count' : 'total_count',
285 'order' => 'DESC',
286 'from_date' => current_time( 'Y-m-d' ),
287 'to_date' => current_time( 'Y-m-d' ),
288 'page' => (int) $page,
289 'limit' => (int) $limit,
290 )
291 )
292 );
293
294 if ( $cache_ttl > 0 ) {
295 $cached = get_site_transient( $cache_key );
296 if ( false !== $cached ) {
297 return $cached;
298 }
299 }
300 }
301
302 $args = array();
303 $visits = 'total_count';
304 if ( $daily ) {
305 $args['orderby'] = 'daily_count';
306 $visits = 'daily_count';
307 }
308 $statistics_table = new Statistics_Table( $network );
309 $results = $statistics_table->get_popular_posts( $limit, $page + 1, $args );
310
311 $output = '<div id="tptn_popular_posts' . ( $daily ? '_daily' : '' ) . '">';
312
313 if ( $results ) {
314 $output .= '<ul>';
315 foreach ( $results as $result ) {
316 if ( ! absint( $result[ $visits ] ) ) {
317 continue;
318 }
319
320 $context_key = class_exists( 'WebberZone\\Top_Ten\\Pro\\Sitewide_Database' ) && \WebberZone\Top_Ten\Pro\Sitewide_Database::is_available()
321 ? \WebberZone\Top_Ten\Pro\Sitewide_Database::get_context_key( $result['ID'], $result['blog_id'] ?? null )
322 : '';
323
324 // Handle network context differently.
325 if ( $network ) {
326 // Check if blog exists.
327 $blog_details = get_blog_details( $result['blog_id'] );
328 if ( ! $blog_details ) {
329 continue; // Skip if blog doesn't exist.
330 }
331
332 if ( '' !== $context_key ) {
333 $label = \WebberZone\Top_Ten\Pro\Sitewide_Database::get_context_label( $context_key );
334 $output .= '<li>' . esc_html( $label ) . ' <span class="tptn-sitewide-label">(' . esc_html__( 'site-wide', 'top-10' ) . ')</span>';
335 } else {
336 // Get post from specific blog.
337 $post = get_blog_post( $result['blog_id'], $result['ID'] );
338 if ( ! $post || 'publish' !== $post->post_status ) {
339 continue; // Skip if post doesn't exist or isn't published.
340 }
341
342 $output .= '<li><a href="' . get_blog_permalink( $result['blog_id'], $result['ID'] ) . '">' . esc_html( $post->post_title ) . '</a>';
343 }
344 $output .= ' (' . Helpers::number_format_i18n( $result[ $visits ] ) . ')';
345 $output .= ' <span class="tptn-blog-name">- ' . esc_html( $blog_details->blogname ) . '</span>';
346 $output .= '</li>';
347 } else {
348 // Single site context.
349 if ( '' !== $context_key ) {
350 $label = \WebberZone\Top_Ten\Pro\Sitewide_Database::get_context_label( $context_key );
351 $output .= '<li>' . esc_html( $label ) . ' <span class="tptn-sitewide-label">(' . esc_html__( 'site-wide', 'top-10' ) . ')</span>';
352 } else {
353 if ( ! get_post_status( $result['ID'] ) ) {
354 continue;
355 }
356 $output .= '<li><a href="' . get_permalink( $result['ID'] ) . '">' . get_the_title( $result['ID'] ) . '</a>';
357 }
358 $output .= ' (' . Helpers::number_format_i18n( $result[ $visits ] ) . ')';
359 $output .= '</li>';
360 }
361 }
362 $output .= '</ul>';
363 }
364
365 $output .= '<p style="text-align:center">';
366
367 if ( $daily ) {
368 if ( $network ) {
369 $output .= '<a href="' . network_admin_url( 'admin.php?page=tptn_network_pop_posts_page&orderby=daily_count&order=desc' ) . '">' . __( 'View all network daily popular posts', 'top-10' ) . '</a>';
370 } else {
371 $output .= '<a href="' . esc_url(
372 add_query_arg(
373 array(
374 'page' => 'tptn_popular_posts',
375 'orderby' => 'daily_count',
376 'order' => 'desc',
377 'post-date-filter-from' => current_time( 'd M Y' ),
378 'post-date-filter-to' => current_time( 'd M Y' ),
379 ),
380 admin_url( 'admin.php' )
381 )
382 ) . '">' . __( 'View all daily popular posts', 'top-10' ) . '</a>';
383 }
384 } elseif ( $network ) {
385 $output .= '<a href="' . network_admin_url( 'admin.php?page=tptn_network_pop_posts_page&orderby=total_count&order=desc' ) . '">' . __( 'View all network popular posts', 'top-10' ) . '</a>';
386 } else {
387 $output .= '<a href="' . admin_url( 'admin.php?page=tptn_popular_posts&orderby=total_count&order=desc' ) . '">' . __( 'View all popular posts', 'top-10' ) . '</a>';
388 }
389
390 $output .= '</p>';
391
392 $output .= '<p style="text-align:center;border-top: #000 1px solid">';
393
394 /* translators: 1: Top 10 page link. */
395 $output .= sprintf( __( 'Popular posts by <a href="%s" target="_blank">Top 10 plugin</a>', 'top-10' ), esc_url( 'https://webberzone.com/plugins/top-10/' ) );
396 $output .= '</p>';
397 $output .= '</div>';
398
399 /**
400 * Filters the dashboard widget output
401 *
402 * @since 3.3.0
403 *
404 * @param string $output Widget output
405 * @param bool $daily Is this a daily widget
406 * @param int $page Page number
407 * @param int $limit Limit of posts
408 * @param bool $widget Is this a widget
409 * @param bool $network Is this network-wide
410 */
411 $output = apply_filters( 'tptn_popular_posts_widget_output', $output, $daily, $page, $limit, $widget, $network );
412
413 if ( $network && $widget && $cache_ttl > 0 ) {
414 set_site_transient( $cache_key, $output, $cache_ttl );
415 }
416
417 return $output;
418 }
419
420
421 /**
422 * Widget for Popular Posts.
423 *
424 * @since 3.3.0
425 */
426 public static function popular_posts_widget() {
427 echo self::pop_display( false ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
428 }
429
430
431 /**
432 * Widget for Custom Period Popular Posts.
433 *
434 * @since 3.3.0
435 */
436 public static function popular_posts_widget_daily() {
437 echo self::pop_display( true ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
438 }
439
440 /**
441 * Widget for Network Popular Posts.
442 *
443 * @since 4.2.0
444 */
445 public static function network_popular_posts_widget() {
446 echo self::pop_display( false, 0, 0, true, true ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
447 }
448
449 /**
450 * Widget for Network Custom Period Popular Posts.
451 *
452 * @since 4.2.0
453 */
454 public static function network_popular_posts_widget_daily() {
455 echo self::pop_display( true, 0, 0, true, true ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
456 }
457
458 /**
459 * Widget for Views Overview (mini chart).
460 *
461 * @since 4.2.0
462 */
463 public static function views_over_time_widget() {
464 global $tptn_freemius;
465
466 $output = '<div class="tptn-views-over-time-widget">';
467
468 if ( ! $tptn_freemius->is_paying() ) {
469 $output .= sprintf(
470 '<div class="tptn-views-over-time-chart-placeholder" style="height:150px;margin-bottom:15px;border:1px dashed #ccd0d4;background:#f8f9fa;display:flex;align-items:center;justify-content:center;color:#6c757d;font-style:italic;">%s</div>',
471 esc_html__( 'Upgrade to Top 10 Pro to see your views over time chart here.', 'top-10' )
472 );
473 $output .= sprintf(
474 '<p style="text-align:center;"><a class="button button-primary" href="%s">%s</a></p>',
475 esc_url( $tptn_freemius->get_upgrade_url() ),
476 esc_html__( 'Upgrade to Pro', 'top-10' )
477 );
478 }
479
480 $output .= '</div>';
481
482 /**
483 * Filters the views over time widget content.
484 *
485 * @since 4.2.0
486 *
487 * @param string $output The widget content.
488 */
489 $output = apply_filters( 'tptn_views_over_time_widget_content', $output );
490
491 echo $output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
492 }
493 }
494