PluginProbe
WebberZone Top 10 — Popular Posts / 4.5.1
WebberZone Top 10 — Popular Posts v4.5.1
4.5.1 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 All 117 releases
← All changes | includes/admin/class-dashboard.php +136 -60 4.4.04.5.1 View file →
@@ -39,8 +39,9 @@
39 39 add_action( 'admin_menu', array( $this, 'admin_menu' ) );
40 40 add_action( 'network_admin_menu', array( $this, 'network_admin_menu' ), 9 );
41 41 add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
42 42 add_action( 'wp_ajax_tptn_chart_data', array( $this, 'get_chart_data' ) );
43 + add_action( 'wp_ajax_tptn_dashboard_tab', array( $this, 'get_dashboard_tab' ) );
43 44 }
44 45
45 46 /**
46 47 * Render the settings page.
@@ -56,13 +57,28 @@
56 57
57 58 $post_date_from = ( isset( $_REQUEST['post-date-filter-from'] ) && check_admin_referer( 'tptn-dashboard' ) ) ? sanitize_text_field( wp_unslash( $_REQUEST['post-date-filter-from'] ) ) : $chart_from_date;
58 59
59 60 $post_date_to = ( isset( $_REQUEST['post-date-filter-to'] ) && check_admin_referer( 'tptn-dashboard' ) ) ? sanitize_text_field( wp_unslash( $_REQUEST['post-date-filter-to'] ) ) : $chart_to_date;
61 + $tabs = $this->get_tabs();
62 + $initial_tab = '';
63 + foreach ( $tabs as $tab_id => $tab ) {
64 + if ( empty( $tab['hide'] ) ) {
65 + $initial_tab = $tab_id;
66 + break;
67 + }
68 + }
60 69
61 70 ?>
62 71 <div class="wrap">
63 72 <h1><?php esc_html_e( 'Top 10 Dashboard', 'top-10' ); ?></h1>
64 - <?php do_action( 'tptn_settings_page_header' ); ?>
73 + <?php
74 + /**
75 + * Fires at the top of a Top 10 admin page, after the heading.
76 + *
77 + * @since 4.0.0
78 + */
79 + do_action( 'tptn_settings_page_header' );
80 + ?>
65 81
66 82 <?php settings_errors(); ?>
67 83
68 84 <div id="poststuff">
@@ -93,12 +109,12 @@
93 109
94 110 </form>
95 111
96 112 <h2><?php esc_html_e( 'Historical visits', 'top-10' ); ?></h2>
97 - <div id="dashboard-historical-visits">
113 + <div id="dashboard-historical-visits" data-tptn-network="<?php echo esc_attr( is_network_admin() ? '1' : '0' ); ?>">
98 114 <ul class="nav-tab-wrapper" style="padding:0; border-bottom: 1px solid #ccc;">
99 115 <?php
100 - foreach ( $this->get_tabs() as $tab_id => $tab_name ) {
116 + foreach ( $tabs as $tab_id => $tab_name ) {
101 117 $tab_class = 'nav-tab';
102 118 $tab_style = '';
103 119
104 120 // Check if this tab should be hidden initially.
@@ -120,15 +136,23 @@
120 136 </ul>
121 137
122 138 <form method="post" action="options.php">
123 139
124 - <?php foreach ( $this->get_tabs() as $tab_id => $tab_name ) : ?>
140 + <?php foreach ( $tabs as $tab_id => $tab_name ) : ?>
125 141
126 - <div id="<?php echo esc_attr( $tab_id ); ?>">
142 + <div id="<?php echo esc_attr( $tab_id ); ?>" data-tptn-tab="<?php echo esc_attr( $tab_id ); ?>" data-tptn-loaded="<?php echo esc_attr( $tab_id === $initial_tab ? '1' : '0' ); ?>">
143 + <div class="tptn-dashboard-tab-content">
127 144 <?php
128 - $output = empty( $tab_name['hide'] ) ? $this->display_popular_posts( $tab_name ) : '';
145 + $output = '';
146 + if ( empty( $tab_name['hide'] ) && $tab_id === $initial_tab ) {
147 + $tab_name['network'] = is_network_admin();
148 + $output = $this->display_popular_posts( $tab_name );
149 + } elseif ( empty( $tab_name['hide'] ) ) {
150 + $output = '<p class="tptn-dashboard-tab-loading" aria-live="polite">' . esc_html__( 'Loading…', 'top-10' ) . '</p>';
151 + }
129 152 echo $output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
130 153 ?>
154 + </div>
131 155
132 156 <div style="font-weight:bold;padding:5px;">
133 157
134 158 <?php
@@ -273,8 +297,10 @@
273 297 'network' => is_network_admin() ? 1 : 0,
274 298 'enableBarClick' => $enable_bar_click ? 1 : 0,
275 299 'statsUrl' => $stats_base_url,
276 300 'clickBarHint' => __( 'Click to view top posts for this day', 'top-10' ),
301 + 'tabs_url' => admin_url( 'admin-ajax.php' ),
302 + 'tab_error' => __( 'Unable to load this tab. Please refresh the page and try again.', 'top-10' ),
277 303 )
278 304 );
279 305 wp_enqueue_style( 'top-ten-admin-css' );
280 306 }
@@ -292,19 +318,21 @@
292 318 */
293 319 public static function fetch_visits_by_date( $from_date, $to_date, $network = false ) {
294 320 global $wpdb;
295 321
322 + $date_range = self::get_datetime_range( $from_date, $to_date );
323 +
296 324 if ( $network ) {
297 325 $sql = $wpdb->prepare(
298 326 " SELECT SUM(cntaccess) AS visits, DATE(dp_date) as date
299 327 FROM {$wpdb->base_prefix}top_ten_daily
300 - WHERE DATE(dp_date) >= DATE(%s)
301 - AND DATE(dp_date) <= DATE(%s)
328 + WHERE dp_date >= %s
329 + AND dp_date < %s
302 330 GROUP BY date
303 331 ORDER BY date ASC
304 332 ",
305 - $from_date,
306 - $to_date
333 + $date_range['from'],
334 + $date_range['until']
307 335 );
308 336 } else {
309 337 $blog_id = get_current_blog_id();
310 338
@@ -310,16 +338,16 @@
310 338
311 339 $sql = $wpdb->prepare(
312 340 " SELECT SUM(cntaccess) AS visits, DATE(dp_date) as date
313 341 FROM {$wpdb->base_prefix}top_ten_daily
314 - WHERE DATE(dp_date) >= DATE(%s)
315 - AND DATE(dp_date) <= DATE(%s)
342 + WHERE dp_date >= %s
343 + AND dp_date < %s
316 344 AND blog_id = %d
317 345 GROUP BY date
318 346 ORDER BY date ASC
319 347 ",
320 - $from_date,
321 - $to_date,
348 + $date_range['from'],
349 + $date_range['until'],
322 350 $blog_id
323 351 );
324 352 }
325 353
@@ -333,8 +361,24 @@
333 361 return $data;
334 362 }
335 363
336 364 /**
365 + * Convert inclusive calendar dates into a half-open datetime range.
366 + *
367 + * @since 4.5.0
368 + *
369 + * @param string $from_date Inclusive start date.
370 + * @param string $to_date Inclusive end date.
371 + * @return array{from:string,until:string} Datetime boundaries.
372 + */
373 + private static function get_datetime_range( $from_date, $to_date ) {
374 + return array(
375 + 'from' => gmdate( 'Y-m-d 00:00:00', strtotime( $from_date ) ),
376 + 'until' => gmdate( 'Y-m-d 00:00:00', strtotime( $to_date . ' +1 day' ) ),
377 + );
378 + }
379 +
380 + /**
337 381 * Function to add an action to search for tags using Ajax.
338 382 *
339 383 * @since 3.0.0
340 384 */
@@ -360,9 +404,41 @@
360 404 echo wp_json_encode( $data );
361 405 wp_die();
362 406 }
363 407
408 + /**
409 + * Load one historical dashboard tab.
410 + *
411 + * @since 4.5.0
412 + */
413 + public function get_dashboard_tab() {
414 + $is_network = is_multisite() && isset( $_REQUEST['network'] ) && 1 === (int) $_REQUEST['network'];
415 + $roles = wp_parse_list( \tptn_get_option( 'show_dashboard_to_roles' ) );
416 + $capability = $is_network ? 'manage_network_options' : Settings_API::get_capability_for_menu( $roles );
364 417
418 + if ( ! current_user_can( $capability ) ) {
419 + wp_send_json_error( array( 'message' => __( 'You are not allowed to view this data.', 'top-10' ) ), 403 );
420 + }
421 +
422 + check_ajax_referer( 'tptn-dashboard', 'security' );
423 +
424 + $tab_id = isset( $_REQUEST['tab'] ) ? sanitize_key( $_REQUEST['tab'] ) : '';
425 + $tabs = $this->get_tabs();
426 + if ( '' === $tab_id || ! isset( $tabs[ $tab_id ] ) || ! empty( $tabs[ $tab_id ]['hide'] ) ) {
427 + wp_send_json_error( array( 'message' => __( 'The requested dashboard tab is not available.', 'top-10' ) ), 400 );
428 + }
429 +
430 + $args = $tabs[ $tab_id ];
431 + $args['network'] = $is_network;
432 +
433 + wp_send_json_success(
434 + array(
435 + 'html' => $this->display_popular_posts( $args ),
436 + )
437 + );
438 + }
439 +
440 +
365 441 /**
366 442 * Array containing the settings' sections.
367 443 *
368 444 * @since 3.0.0
@@ -382,19 +458,19 @@
382 458 'to_date' => gmdate( 'd M Y', strtotime( '-1 day' ) ),
383 459 ),
384 460 'lastweek' => array(
385 461 'title' => __( 'Last 7 days', 'top-10' ),
386 - 'from_date' => gmdate( 'd M Y', strtotime( '-1 week' ) ),
462 + 'from_date' => gmdate( 'd M Y', strtotime( '-6 days' ) ),
387 463 'to_date' => current_time( 'd M Y' ),
388 464 ),
389 465 'lastfortnight' => array(
390 466 'title' => __( 'Last 14 days', 'top-10' ),
391 - 'from_date' => gmdate( 'd M Y', strtotime( '-2 weeks' ) ),
467 + 'from_date' => gmdate( 'd M Y', strtotime( '-13 days' ) ),
392 468 'to_date' => current_time( 'd M Y' ),
393 469 ),
394 470 'lastmonth' => array(
395 471 'title' => __( 'Last 30 days', 'top-10' ),
396 - 'from_date' => gmdate( 'd M Y', strtotime( '-30 days' ) ),
472 + 'from_date' => gmdate( 'd M Y', strtotime( '-29 days' ) ),
397 473 'to_date' => current_time( 'd M Y' ),
398 474 ),
399 475 'overall' => array(
400 476 'title' => __( 'All time', 'top-10' ),
@@ -460,23 +536,33 @@
460 536 </thead>
461 537 <tbody>
462 538 <?php
463 539 foreach ( $results as $result ) :
464 - $visits_int = (int) $result['visits'];
465 - $visits = \WebberZone\Top_Ten\Util\Helpers::number_format_i18n( $visits_int );
540 + $visits_int = (int) $result['visits'];
541 + $visits = \WebberZone\Top_Ten\Util\Helpers::number_format_i18n( $visits_int );
542 + $context_key = class_exists( 'WebberZone\\Top_Ten\\Pro\\Sitewide_Database' ) && \WebberZone\Top_Ten\Pro\Sitewide_Database::is_available()
543 + ? \WebberZone\Top_Ten\Pro\Sitewide_Database::get_context_key( $result['ID'], $result['blog_id'] ?? null )
544 + : '';
466 545 if ( $is_network && isset( $result['blog_id'] ) ) {
467 - $post = get_blog_post( (int) $result['blog_id'], (int) $result['ID'] );
468 - $permalink = is_multisite() ? get_blog_permalink( (int) $result['blog_id'], (int) $result['ID'] ) : get_permalink( (int) $result['ID'] );
546 + $post = '' === $context_key ? get_blog_post( (int) $result['blog_id'], (int) $result['ID'] ) : null;
547 + $permalink = '' === $context_key ? ( is_multisite() ? get_blog_permalink( (int) $result['blog_id'], (int) $result['ID'] ) : get_permalink( (int) $result['ID'] ) ) : '';
469 548 } else {
470 - $post = get_post( (int) $result['ID'] );
471 - $permalink = get_permalink( (int) $result['ID'] );
549 + $post = '' === $context_key ? get_post( (int) $result['ID'] ) : null;
550 + $permalink = '' === $context_key ? get_permalink( (int) $result['ID'] ) : '';
472 551 }
473 - if ( ! $post ) {
552 + if ( ! $post && '' === $context_key ) {
474 553 continue;
475 554 }
555 + $title = '' !== $context_key ? \WebberZone\Top_Ten\Pro\Sitewide_Database::get_context_label( $context_key ) : get_the_title( $post );
476 556 ?>
477 557 <tr>
478 - <td><a href="<?php echo esc_url( $permalink ); ?>" target="_blank"><?php echo esc_html( get_the_title( $post ) ); ?></a></td>
558 + <td>
559 + <?php if ( '' !== $context_key ) : ?>
560 + <?php echo esc_html( $title ); ?> <span class="tptn-sitewide-label">(<?php esc_html_e( 'site-wide', 'top-10' ); ?>)</span>
561 + <?php else : ?>
562 + <a href="<?php echo esc_url( $permalink ); ?>" target="_blank"><?php echo esc_html( $title ); ?></a>
563 + <?php endif; ?>
564 + </td>
479 565 <?php if ( $is_network && isset( $result['blog_id'] ) ) : ?>
480 566 <td>
481 567 <?php
482 568 $blog_details = get_blog_details( (int) $result['blog_id'] );
@@ -549,28 +635,25 @@
549 635 $explicit_network = isset( $args['network'] ) ? (bool) $args['network'] : null;
550 636 $is_network = ( null !== $explicit_network ) ? $explicit_network : ( is_multisite() && is_network_admin() );
551 637 $table_name = Database::get_table( $args['daily'] );
552 638
553 - $has_dates = ! empty( $args['from_date'] ) && ! empty( $args['to_date'] );
639 + $has_dates = ! empty( $args['from_date'] ) && ! empty( $args['to_date'] );
640 + $date_range = $has_dates ? self::get_datetime_range( $args['from_date'], $args['to_date'] ) : array();
554 641
555 642 if ( $args['daily'] && $has_dates && $is_network ) {
556 - $from_date = gmdate( 'Y-m-d', strtotime( $args['from_date'] ) );
557 - $to_date = gmdate( 'Y-m-d', strtotime( $args['to_date'] ) );
558 - $sql = $wpdb->prepare(
643 + $sql = $wpdb->prepare(
559 644 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
560 - "SELECT SUM(cntaccess) FROM {$table_name} WHERE DATE(dp_date) >= DATE(%s) AND DATE(dp_date) <= DATE(%s)",
561 - $from_date,
562 - $to_date
645 + "SELECT SUM(cntaccess) FROM {$table_name} WHERE dp_date >= %s AND dp_date < %s",
646 + $date_range['from'],
647 + $date_range['until']
563 648 );
564 649 } elseif ( $args['daily'] && $has_dates ) {
565 - $blog_id = (int) $args['blog_id'];
566 - $from_date = gmdate( 'Y-m-d', strtotime( $args['from_date'] ) );
567 - $to_date = gmdate( 'Y-m-d', strtotime( $args['to_date'] ) );
568 - $sql = $wpdb->prepare(
650 + $blog_id = (int) $args['blog_id'];
651 + $sql = $wpdb->prepare(
569 652 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
570 - "SELECT SUM(cntaccess) FROM {$table_name} WHERE DATE(dp_date) >= DATE(%s) AND DATE(dp_date) <= DATE(%s) AND blog_id = %d",
571 - $from_date,
572 - $to_date,
653 + "SELECT SUM(cntaccess) FROM {$table_name} WHERE dp_date >= %s AND dp_date < %s AND blog_id = %d",
654 + $date_range['from'],
655 + $date_range['until'],
573 656 $blog_id
574 657 );
575 658 } elseif ( $is_network ) {
576 659 $sql = "SELECT SUM(cntaccess) FROM {$table_name}"; // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
@@ -625,8 +708,13 @@
625 708
626 709 $explicit_network = isset( $args['network'] ) ? (bool) $args['network'] : null;
627 710 $is_network = ( null !== $explicit_network ) ? $explicit_network : ( is_multisite() && is_network_admin() );
628 711 $table_name = Database::get_table( $args['daily'] );
712 + $date_range = ( $args['daily'] && ! empty( $args['from_date'] ) && ! empty( $args['to_date'] ) )
713 + ? self::get_datetime_range( $args['from_date'], $args['to_date'] )
714 + : array();
715 + $sitewide_ids = class_exists( 'WebberZone\\Top_Ten\\Pro\\Sitewide_Database' ) && \WebberZone\Top_Ten\Pro\Sitewide_Database::is_available() ? \WebberZone\Top_Ten\Pro\Sitewide_Database::get_context_ids() : array();
716 + $sitewide_sql = empty( $sitewide_ids ) ? '0=1' : $table_name . '.postnumber IN (' . implode( ',', array_map( 'intval', $sitewide_ids ) ) . ')';
629 717
630 718 if ( $is_network ) {
631 719 // Network-wide: aggregate across all blogs.
632 720 $fields[] = ( $args['daily'] ) ? "SUM({$table_name}.cntaccess) as visits" : "{$table_name}.cntaccess as visits";
@@ -636,18 +724,12 @@
636 724 $fields = implode( ', ', $fields );
637 725
638 726 $where = ' 1=1 ';
639 727
640 - if ( isset( $args['from_date'] ) && ! empty( $args['from_date'] ) && $args['daily'] ) {
641 - $from_date = gmdate( 'Y-m-d', strtotime( $args['from_date'] ) );
642 - $where .= $wpdb->prepare( " AND DATE({$table_name}.dp_date) >= DATE(%s) ", $from_date ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
728 + if ( ! empty( $date_range ) ) {
729 + $where .= $wpdb->prepare( " AND {$table_name}.dp_date >= %s AND {$table_name}.dp_date < %s ", $date_range['from'], $date_range['until'] ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
643 730 }
644 731
645 - if ( isset( $args['to_date'] ) && ! empty( $args['to_date'] ) && $args['daily'] ) {
646 - $to_date = gmdate( 'Y-m-d', strtotime( $args['to_date'] ) );
647 - $where .= $wpdb->prepare( " AND DATE({$table_name}.dp_date) <= DATE(%s) ", $to_date ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
648 - }
649 -
650 732 $groupby = ' ';
651 733 $groupby = " GROUP BY {$table_name}.postnumber, {$table_name}.blog_id ";
652 734
653 735 $orderby = ' visits DESC ';
@@ -658,33 +740,27 @@
658 740 $sql = "SELECT $fields FROM {$table_name} WHERE $where $groupby $orderby $limits";
659 741 } else {
660 742 // Single site: existing behaviour filtered by current blog.
661 743 $fields[] = ( $args['daily'] ) ? "SUM({$table_name}.cntaccess) as visits" : "{$table_name}.cntaccess as visits";
662 - $fields[] = "{$wpdb->posts}.ID";
744 + $fields[] = "{$table_name}.postnumber as ID";
663 745
664 746 $fields = implode( ', ', $fields );
665 747
666 748 // Create the JOIN clause.
667 - $join = " INNER JOIN {$wpdb->posts} ON {$table_name}.postnumber={$wpdb->posts}.ID ";
749 + $join = " LEFT JOIN {$wpdb->posts} ON {$table_name}.postnumber={$wpdb->posts}.ID ";
668 750
669 751 // Create the base WHERE clause.
670 752 $where = $wpdb->prepare( " AND {$table_name}.blog_id = %d ", $args['blog_id'] ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
671 - $where .= " AND ($wpdb->posts.post_status = 'publish' OR $wpdb->posts.post_status = 'inherit') "; // Show published posts and attachments.
672 - $where .= " AND ($wpdb->posts.post_type <> 'revision' ) "; // No revisions.
753 + $where .= " AND (($wpdb->posts.post_status = 'publish' OR $wpdb->posts.post_status = 'inherit') OR {$sitewide_sql}) "; // Show published posts, attachments and site-wide contexts.
754 + $where .= " AND (($wpdb->posts.post_type <> 'revision' ) OR {$sitewide_sql}) "; // No revisions.
673 755
674 - if ( isset( $args['from_date'] ) && ! empty( $args['from_date'] ) ) {
675 - $from_date = gmdate( 'Y-m-d', strtotime( $args['from_date'] ) );
676 - $where .= $wpdb->prepare( " AND DATE({$table_name}.dp_date) >= DATE(%s) ", $from_date ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
756 + if ( ! empty( $date_range ) ) {
757 + $where .= $wpdb->prepare( " AND {$table_name}.dp_date >= %s AND {$table_name}.dp_date < %s ", $date_range['from'], $date_range['until'] ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
677 758 }
678 759
679 - if ( isset( $args['to_date'] ) && ! empty( $args['to_date'] ) ) {
680 - $to_date = gmdate( 'Y-m-d', strtotime( $args['to_date'] ) );
681 - $where .= $wpdb->prepare( " AND DATE({$table_name}.dp_date) <= DATE(%s) ", $to_date ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
682 - }
683 -
684 760 // Create the base GROUP BY clause.
685 761 if ( $args['daily'] ) {
686 - $groupby = " {$wpdb->posts}.ID";
762 + $groupby = " {$table_name}.postnumber";
687 763 }
688 764
689 765 // Create the base ORDER BY clause.
690 766 $orderby = ' visits DESC ';