PluginProbe
WebberZone Top 10 — Popular Posts / 4.3.1
WebberZone Top 10 — Popular Posts v4.3.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
top-10 / includes / admin / class-tools-page.php

class-tools-page.php in WebberZone Top 10 — Popular Posts 4.3.1, at includes/admin/class-tools-page.php

739 lines 26.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Tools Page class.
4 *
5 * @package WebberZone\Top_Ten\Admin
6 */
7
8 namespace WebberZone\Top_Ten\Admin;
9
10 use WebberZone\Top_Ten\Database;
11 use WebberZone\Top_Ten\Counter;
12 use WebberZone\Top_Ten\Admin\Activator;
13 use WebberZone\Top_Ten\Util\Hook_Registry;
14
15 if ( ! defined( 'WPINC' ) ) {
16 die;
17 }
18
19 /**
20 * Generates the Tools page.
21 *
22 * @since 3.3.0
23 */
24 class Tools_Page {
25
26 /**
27 * Parent Menu ID.
28 *
29 * @since 3.3.0
30 *
31 * @var string Parent Menu ID.
32 */
33 public $parent_id;
34
35 /**
36 * Constructor class.
37 *
38 * @since 3.3.0
39 */
40 public function __construct() {
41 Hook_Registry::add_action( 'admin_menu', array( $this, 'admin_menu' ) );
42 Hook_Registry::add_action( 'network_admin_menu', array( $this, 'network_admin_menu' ), 11 );
43 Hook_Registry::add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
44 Hook_Registry::add_action( 'admin_init', array( $this, 'handle_recreate_tables_action' ) );
45 Hook_Registry::add_action( 'admin_init', array( $this, 'handle_create_missing_tables_action' ) );
46
47 // Clear table statistics cache when counts are updated.
48 Hook_Registry::add_action( 'tptn_count_updated', array( 'WebberZone\Top_Ten\Database', 'clear_table_statistics_cache' ) );
49 Hook_Registry::add_action( 'tptn_delete_counts', array( 'WebberZone\Top_Ten\Database', 'clear_table_statistics_cache' ) );
50 Hook_Registry::add_action( 'tptn_set_count', array( 'WebberZone\Top_Ten\Database', 'clear_table_statistics_cache' ) );
51 }
52
53 /**
54 * Admin Menu.
55 *
56 * @since 3.3.0
57 */
58 public function admin_menu() {
59
60 $this->parent_id = add_submenu_page(
61 'tptn_dashboard',
62 esc_html__( 'Top 10 Tools', 'top-10' ),
63 esc_html__( 'Tools', 'top-10' ),
64 'manage_options',
65 'tptn_tools_page',
66 array( $this, 'render_page' )
67 );
68
69 add_action( 'load-' . $this->parent_id, array( $this, 'help_tabs' ) );
70 }
71
72 /**
73 * Admin Menu.
74 *
75 * @since 3.3.0
76 */
77 public function network_admin_menu() {
78
79 $this->parent_id = add_submenu_page(
80 'tptn_dashboard',
81 esc_html__( 'Top 10 Tools', 'top-10' ),
82 esc_html__( 'Tools', 'top-10' ),
83 'manage_network_options',
84 'tptn_network_tools_page',
85 array( $this, 'render_page' )
86 );
87
88 add_action( 'load-' . $this->parent_id, array( $this, 'help_tabs' ) );
89 }
90
91 /**
92 * Enqueue scripts in admin area.
93 *
94 * @since 3.3.0
95 *
96 * @param string $hook The current admin page.
97 */
98 public function admin_enqueue_scripts( $hook ) {
99 if ( $hook === $this->parent_id ) {
100 wp_enqueue_script( 'top-ten-admin-js' );
101 wp_enqueue_style( 'top-ten-admin-css' );
102 wp_enqueue_style( 'wp-spinner' );
103 wp_localize_script(
104 'top-ten-admin-js',
105 'top_ten_admin_data',
106 array(
107 'ajax_url' => admin_url( 'admin-ajax.php' ),
108 'security' => wp_create_nonce( 'tptn-admin' ),
109 'strings' => array(
110 'confirm_message' => esc_html__( 'Are you sure you want to clear the cache?', 'top-10' ),
111 'clearing_text' => esc_html__( 'Clearing...', 'top-10' ),
112 'fail_message' => esc_html__( 'Failed to clear cache. Please try again.', 'top-10' ),
113 'request_fail_message' => esc_html__( 'Request failed: ', 'top-10' ),
114 ),
115 )
116 );
117 }
118 }
119
120 /**
121 * Render the tools settings page.
122 *
123 * @since 2.5.0
124 *
125 * @return void
126 */
127 public function render_page() {
128 $screen = get_current_screen();
129 $network_wide = false;
130
131 if ( $screen->id === $this->parent_id . '-network' ) {
132 $network_wide = true;
133 }
134
135 /* Truncate overall posts table */
136 if ( isset( $_POST['tptn_recreate_primary_key'] ) && check_admin_referer( 'tptn-tools-settings' ) ) {
137 self::recreate_primary_key();
138 add_settings_error( 'tptn-notices', '', esc_html__( 'Primary Key has been recreated', 'top-10' ), 'updated' );
139 }
140
141 /* Truncate overall posts table */
142 if ( isset( $_POST['tptn_reset_overall'] ) && check_admin_referer( 'tptn-tools-settings' ) ) {
143 if ( ! is_multisite() || $network_wide ) {
144 Database::truncate_table( Database::get_table( false ) );
145 } else {
146 Counter::delete_counts( array( 'daily' => false ) );
147 }
148 add_settings_error( 'tptn-notices', '', esc_html__( 'Top 10 popular posts reset', 'top-10' ), 'updated' );
149 }
150
151 /* Truncate daily posts table */
152 if ( isset( $_POST['tptn_reset_daily'] ) && check_admin_referer( 'tptn-tools-settings' ) ) {
153 if ( ! is_multisite() || $network_wide ) {
154 Database::truncate_table( Database::get_table( true ) );
155 } else {
156 Counter::delete_counts( array( 'daily' => true ) );
157 }
158 add_settings_error( 'tptn-notices', '', esc_html__( 'Top 10 daily popular posts reset', 'top-10' ), 'updated' );
159 }
160
161 /* Recreate tables */
162 if ( isset( $_POST['tptn_recreate_tables'] ) && check_admin_referer( 'tptn-tools-settings' ) ) {
163 $result = self::recreate_tables();
164 if ( is_wp_error( $result ) ) {
165 add_settings_error( 'tptn-notices', '', $result->get_error_message(), 'error' );
166 } else {
167 add_settings_error( 'tptn-notices', '', esc_html__( 'Top 10 tables have been recreated', 'top-10' ), 'updated' );
168 }
169 }
170
171 /* Sync funnel (run aggregation now) */
172 if ( isset( $_POST['tptn_sync_funnel'] ) && check_admin_referer( 'tptn-tools-settings' ) ) {
173 $result = Database::aggregate_visit_log();
174 if ( true === $result ) {
175 add_settings_error( 'tptn-notices', '', esc_html__( 'Funnel has been synced. Buffered visits have been aggregated.', 'top-10' ), 'updated' );
176 } elseif ( 0 === $result ) {
177 add_settings_error( 'tptn-notices', '', esc_html__( 'Nothing to sync. The funnel is empty.', 'top-10' ), 'updated' );
178 } elseif ( false === $result ) {
179 add_settings_error( 'tptn-notices', '', esc_html__( 'Sync skipped: another aggregation is already in progress. Please try again in a moment.', 'top-10' ), 'updated' );
180 } elseif ( is_wp_error( $result ) ) {
181 add_settings_error(
182 'tptn-notices',
183 '',
184 sprintf(
185 /* translators: %s: error message from the database */
186 esc_html__( 'Sync failed: %s', 'top-10' ),
187 esc_html( $result->get_error_message() )
188 ),
189 'error'
190 );
191 }
192 }
193
194 ob_start();
195 ?>
196 <div class="wrap">
197 <h1><?php esc_html_e( 'Top 10 Tools', 'top-10' ); ?></h1>
198 <?php do_action( 'tptn_settings_page_header' ); ?>
199
200 <?php settings_errors(); ?>
201
202 <div id="poststuff">
203 <div id="post-body" class="metabox-holder columns-2">
204 <div id="post-body-content">
205
206 <form method="post" >
207
208 <div class="postbox">
209 <h2><span><?php esc_html_e( 'Status', 'top-10' ); ?></span></h2>
210 <div class="inside">
211 <div class="tptn-db-status">
212 <?php echo self::get_db_status_report(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
213 </div>
214 </div>
215 </div>
216
217 <div class="postbox">
218 <h2><span><?php esc_html_e( 'Clear cache', 'top-10' ); ?></span></h2>
219 <div class="inside">
220 <p>
221 <?php
222 printf(
223 '<button type="button" name="tptn_cache_clear" class="button button-secondary tptn_cache_clear" aria-label="%1$s">%1$s</button>',
224 esc_html__( 'Clear cache', 'top-10' )
225 );
226 ?>
227 </p>
228 <p class="description">
229 <?php esc_html_e( 'Clear the Top 10 cache. This will also be cleared automatically when you save the settings page.', 'top-10' ); ?>
230 </p>
231 </div>
232 </div>
233
234 <div class="postbox">
235 <h2><span><?php esc_html_e( 'Sync Funnel', 'top-10' ); ?></span></h2>
236 <div class="inside">
237 <p>
238 <button name="tptn_sync_funnel" type="submit" id="tptn_sync_funnel" class="button button-secondary"><?php esc_attr_e( 'Sync Funnel Now', 'top-10' ); ?></button>
239 </p>
240 <p class="description">
241 <?php esc_html_e( 'Drain the visits funnel into the count tables immediately. This is equivalent to running the 5-minute aggregation cron job.', 'top-10' ); ?>
242 </p>
243 </div>
244 </div>
245
246 <div class="postbox">
247 <h2><span><?php esc_html_e( 'Recreate Primary Key', 'top-10' ); ?></span></h2>
248 <div class="inside">
249 <p>
250 <button name="tptn_recreate_primary_key" type="submit" id="tptn_recreate_primary_key" class="button button-secondary"><?php esc_attr_e( 'Recreate Primary Key', 'top-10' ); ?></button>
251 </p>
252 <p class="description">
253 <?php esc_html_e( 'Deletes and reinitializes the primary key in the database tables. If the above function gives an error, then you can run the below code in phpMyAdmin or Adminer. Remember to backup your database first!', 'top-10' ); ?>
254 </p>
255 <p>
256 <code style="display:block;"><?php echo self::recreate_primary_key_html(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></code>
257 </p>
258 </div>
259 </div>
260
261 <div class="postbox">
262 <h2><span><?php esc_html_e( 'Reset database', 'top-10' ); ?></span></h2>
263 <div class="inside">
264 <p class="description">
265 <?php esc_html_e( 'This will reset the Top 10 tables. If this is a multisite install, this will reset the popular posts for the current site. If this is the Network Admin screen, then it will reset the popular posts across all sites. This cannot be reversed. Make sure that your database has been backed up before proceeding', 'top-10' ); ?>
266 </p>
267 <p>
268 <?php
269 printf(
270 '<button name="tptn_reset_overall" type="submit" id="tptn_reset_overall" class="button button-secondary" style="color:#fff;background-color: #a00;border-color: #900;" onclick="if (!confirm(\'%s\')) return false;">%s</button>',
271 esc_attr__( 'Are you sure you want to reset the popular posts?', 'top-10' ),
272 esc_attr__( 'Reset Popular Posts', 'top-10' )
273 );
274 ?>
275 </p>
276 <p>
277 <?php
278 printf(
279 '<button name="tptn_reset_daily" type="submit" id="tptn_reset_daily" class="button button-secondary" style="color:#fff;background-color: #a00;border-color: #900;" onclick="if (!confirm(\'%s\')) return false;">%s</button>',
280 esc_attr__( 'Are you sure you want to reset the daily popular posts?', 'top-10' ),
281 esc_attr__( 'Reset Daily Popular Posts', 'top-10' )
282 );
283 ?>
284 </p>
285 </div>
286 </div>
287
288 <?php if ( ! is_multisite() || is_network_admin() ) : ?>
289 <div class="postbox">
290 <h2><span><?php esc_html_e( 'Recreate Database Tables', 'top-10' ); ?></span></h2>
291 <div class="inside">
292 <p class="description">
293 <?php esc_html_e( 'Only click the button below after performing a full backup of the database. You can use any of the popular backup plugins or phpMyAdmin to achieve this. The authors of this plugin do not guarantee that everything will go smoothly as it depends on your site environment and volume of data. If you are not comfortable, please do not proceed.', 'top-10' ); ?>
294 </p>
295 <p>
296 <button name="tptn_recreate_tables" type="submit" id="tptn_recreate_tables" style="color:#fff;background-color: #a00;border-color: #900;" onclick="if (!confirm('<?php esc_attr_e( 'Hit Cancel if you have not backed up your database', 'top-10' ); ?>')) return false;" class="button button-secondary"><?php esc_attr_e( 'Recreate Database Tables', 'top-10' ); ?></button>
297 </p>
298 </div>
299 </div>
300 <?php endif; ?>
301
302 <?php wp_nonce_field( 'tptn-tools-settings' ); ?>
303 </form>
304
305 </div><!-- /#post-body-content -->
306
307 <div id="postbox-container-1" class="postbox-container">
308
309 <div id="side-sortables" class="meta-box-sortables ui-sortable">
310 <?php include_once 'sidebar.php'; ?>
311 </div><!-- /#side-sortables -->
312
313 </div><!-- /#postbox-container-1 -->
314 </div><!-- /#post-body -->
315 <br class="clear" />
316 </div><!-- /#poststuff -->
317
318 </div><!-- /.wrap -->
319
320 <?php
321 echo ob_get_clean(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
322 }
323
324 /**
325 * Function to delete and create the primary keys in the database table.
326 *
327 * @since 2.5.6
328 */
329 public static function recreate_primary_key() {
330 global $wpdb;
331
332 $table_name = Database::get_table( false );
333 $table_name_daily = Database::get_table( true );
334
335 $wpdb->hide_errors();
336
337 if ( $wpdb->query( $wpdb->prepare( "SHOW INDEXES FROM {$table_name} WHERE Key_name = %s", 'PRIMARY' ) ) ) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
338 $wpdb->query( 'ALTER TABLE ' . $table_name . ' DROP PRIMARY KEY ' ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.SchemaChange
339 }
340 if ( $wpdb->query( $wpdb->prepare( "SHOW INDEXES FROM {$table_name_daily} WHERE Key_name = %s", 'PRIMARY' ) ) ) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
341 $wpdb->query( 'ALTER TABLE ' . $table_name_daily . ' DROP PRIMARY KEY ' ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.SchemaChange
342 }
343 if ( $wpdb->query( $wpdb->prepare( "SHOW INDEXES FROM {$table_name_daily} WHERE Key_name = %s", 'blog_date' ) ) ) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
344 $wpdb->query( 'ALTER TABLE ' . $table_name_daily . ' DROP INDEX blog_date' ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.SchemaChange
345 }
346
347 $wpdb->query( 'ALTER TABLE ' . $table_name . ' ADD PRIMARY KEY(postnumber, blog_id) ' ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.SchemaChange
348 $wpdb->query( 'ALTER TABLE ' . $table_name_daily . ' ADD PRIMARY KEY(postnumber, dp_date, blog_id) ' ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.SchemaChange
349 $wpdb->query( 'ALTER TABLE ' . $table_name_daily . ' ADD INDEX blog_date(blog_id, dp_date, postnumber)' ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.SchemaChange
350
351 $wpdb->show_errors();
352 }
353
354 /**
355 * Retrieves the SQL code to recreate the PRIMARY KEY.
356 *
357 * @since 2.5.7
358 */
359 public static function recreate_primary_key_html() {
360
361 $table_name = Database::get_table( false );
362 $table_name_daily = Database::get_table( true );
363
364 $sql = 'ALTER TABLE ' . $table_name . ' DROP PRIMARY KEY; ';
365 $sql .= '<br />';
366 $sql .= 'ALTER TABLE ' . $table_name_daily . ' DROP PRIMARY KEY; ';
367 $sql .= '<br />';
368 $sql .= 'ALTER TABLE ' . $table_name_daily . ' DROP INDEX IF EXISTS blog_date; ';
369 $sql .= '<br />';
370 $sql .= 'ALTER TABLE ' . $table_name . ' ADD PRIMARY KEY(postnumber, blog_id); ';
371 $sql .= '<br />';
372 $sql .= 'ALTER TABLE ' . $table_name_daily . ' ADD PRIMARY KEY(postnumber, dp_date, blog_id); ';
373 $sql .= '<br />';
374 $sql .= 'ALTER TABLE ' . $table_name_daily . ' ADD INDEX blog_date(blog_id, dp_date, postnumber); ';
375
376 /**
377 * Filters the SQL code to recreate the PRIMARY KEY.
378 *
379 * @since 2.5.7
380 * @param string $sql SQL code to recreate PRIMARY KEY.
381 */
382 return apply_filters( 'tptn_recreate_primary_key_html', $sql );
383 }
384
385 /**
386 * Retrieves the SQL code to recreate the PRIMARY KEY.
387 *
388 * @since 2.7.0
389 */
390 public static function recreate_tables() {
391 $errors = new \WP_Error();
392
393 $result = Database::recreate_overall_table( false );
394 if ( is_wp_error( $result ) ) {
395 $errors->merge_from( $result );
396 }
397
398 $result = Database::recreate_daily_table( false );
399 if ( is_wp_error( $result ) ) {
400 $errors->merge_from( $result );
401 }
402
403 $result = Database::recreate_funnel_table( false );
404 if ( is_wp_error( $result ) ) {
405 $errors->merge_from( $result );
406 }
407
408 $result = Database::recreate_log_table( false );
409 if ( is_wp_error( $result ) ) {
410 $errors->merge_from( $result );
411 }
412
413 return $errors->has_errors() ? $errors : true;
414 }
415
416 /**
417 * Generates the Tools help page.
418 *
419 * @since 3.3.0
420 */
421 public static function help_tabs() {
422 $screen = get_current_screen();
423
424 $screen->set_help_sidebar(
425 /* translators: 1: Support link. */
426 '<p>' . sprintf( __( 'For more information or how to get support visit the <a href="%1$s">WebberZone support site</a>.', 'top-10' ), esc_url( 'https://webberzone.com/support/' ) ) . '</p>' .
427 /* translators: 1: Forum link. */
428 '<p>' . sprintf( __( 'Support queries should be posted in the <a href="%1$s">WordPress.org support forums</a>.', 'top-10' ), esc_url( 'https://wordpress.org/support/plugin/top-10' ) ) . '</p>' .
429 '<p>' . sprintf(
430 /* translators: 1: Github Issues link, 2: Github page. */
431 __( '<a href="%1$s">Post an issue</a> on <a href="%2$s">GitHub</a> (bug reports only).', 'top-10' ),
432 esc_url( 'https://github.com/WebberZone/top-10/issues' ),
433 esc_url( 'https://github.com/WebberZone/top-10' )
434 ) . '</p>'
435 );
436
437 $screen->add_help_tab(
438 array(
439 'id' => 'tptn-settings-general',
440 'title' => __( 'General', 'top-10' ),
441 'content' =>
442 '<p>' . __( 'This screen provides some tools that help maintain certain features of Top 10.', 'top-10' ) . '</p>' .
443 '<p>' . __( 'Clear the cache, reset the popular posts tables plus some miscellaneous fixes for older versions of Top 10.', 'top-10' ) . '</p>',
444 )
445 );
446
447 do_action( 'tptn_settings_tools_help', $screen );
448 }
449
450 /**
451 * Check if tables exist and create them if they don't.
452 *
453 * @since 4.2.0
454 *
455 * @return array Array of table statuses indicating whether they are installed.
456 */
457 public static function check_table_status() {
458 $tables = array(
459 'top_ten' => Database::get_table( false ),
460 'top_ten_daily' => Database::get_table( true ),
461 'top_ten_visits_funnel' => Database::get_funnel_table(),
462 'top_ten_visits_log' => Database::get_log_table(),
463 );
464
465 $statuses = array();
466
467 $installed_label = '<span style="color: #006400;">' . __( 'Installed', 'top-10' ) . '</span>';
468 $not_installed_label = '<span style="color: #8B0000;">' . __( 'Not Installed', 'top-10' ) . '</span>';
469
470 foreach ( $tables as $key => $table_name ) {
471 $statuses[ $key ] = Database::is_table_installed( $table_name ) ? $installed_label : $not_installed_label;
472 }
473
474 // Create tables if they don't exist.
475 if ( ! Database::are_tables_installed() ) {
476 // Use Activator to create tables.
477 Activator::create_tables();
478 Database::clear_table_statistics_cache();
479
480 // Refresh statuses after creating tables.
481 foreach ( $tables as $key => $table_name ) {
482 $statuses[ $key ] = Database::is_table_installed( $table_name ) ? $installed_label : $not_installed_label;
483 }
484 }
485
486 /**
487 * Filter the table statuses report.
488 *
489 * @since 4.1.0
490 *
491 * @param array $statuses Array of table statuses.
492 */
493 return apply_filters( 'tptn_table_statuses', $statuses );
494 }
495
496 /**
497 * Get database status report.
498 *
499 * @since 4.2.0
500 *
501 * @return string HTML output for the database status report.
502 */
503 public static function get_db_status_report() {
504 global $tptn_db_version;
505
506 // Get table statuses.
507 $statuses = self::check_table_status();
508
509 // Get table statistics from Database class.
510 $table_stats = Database::get_table_statistics();
511
512 ob_start();
513 ?>
514 <table class="form-table">
515 <tr>
516 <th scope="row"><?php esc_html_e( 'Database version', 'top-10' ); ?></th>
517 <td>
518 <?php esc_html_e( 'Installed version', 'top-10' ); ?> <?php echo esc_html( get_site_option( 'tptn_db_version', '0' ) ); ?> /
519 <?php esc_html_e( 'Current version', 'top-10' ); ?> <?php echo esc_html( $tptn_db_version ); ?>
520 </td>
521 </tr>
522
523 <?php
524 $table_keys = array( 'top_ten', 'top_ten_daily', 'top_ten_visits_funnel', 'top_ten_visits_log' );
525 foreach ( $table_keys as $table_key ) :
526 if ( ! isset( $statuses[ $table_key ] ) ) {
527 continue;
528 }
529 ?>
530 <tr>
531 <th scope="row"><?php printf( /* translators: %s: Table name */ esc_html__( '%s table', 'top-10' ), esc_html( $table_key ) ); ?></th>
532 <td>
533 <?php
534 echo $statuses[ $table_key ]; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
535
536 if ( isset( $table_stats[ $table_key ] ) ) {
537 $format = ( is_multisite() && ! is_network_admin() )
538 /* translators: 1: Number of entries, 2: Estimated table size */
539 ? __( 'Entries: %1$s | Est. Size: %2$s', 'top-10' )
540 /* translators: 1: Number of entries, 2: Table size */
541 : __( 'Entries: %1$s | Size: %2$s', 'top-10' );
542
543 echo '<br><span class="description">';
544 printf(
545 esc_html( $format ),
546 '<strong>' . esc_html( number_format_i18n( $table_stats[ $table_key ]['entries'] ) ) . '</strong>',
547 '<strong>' . esc_html( size_format( $table_stats[ $table_key ]['size'] ) ) . '</strong>'
548 );
549 echo '</span>';
550 }
551 ?>
552 </td>
553 </tr>
554 <?php endforeach; ?>
555
556 <?php
557 $cron_jobs = array(
558 'tptn_cron_hook' => __( 'Maintenance cron', 'top-10' ),
559 'tptn_aggregation_cron_hook' => __( 'Aggregation cron', 'top-10' ),
560 );
561 foreach ( $cron_jobs as $hook => $label ) :
562 $next_run = wp_next_scheduled( $hook );
563 $recurrence = $next_run ? wp_get_schedule( $hook ) : false;
564 $schedules = wp_get_schedules();
565 $interval_display = '';
566 if ( $recurrence && isset( $schedules[ $recurrence ]['display'] ) ) {
567 $interval_display = $schedules[ $recurrence ]['display'];
568 }
569 ?>
570 <tr>
571 <th scope="row"><?php echo esc_html( $label ); ?></th>
572 <td>
573 <?php if ( $next_run ) : ?>
574 <span style="color: #006400;"><?php esc_html_e( 'Scheduled', 'top-10' ); ?></span>
575 <br><span class="description">
576 <?php
577 if ( $interval_display ) {
578 printf(
579 /* translators: %s: schedule display name e.g. "Every two minutes" */
580 esc_html__( 'Runs: %s', 'top-10' ),
581 esc_html( $interval_display )
582 );
583 echo '<br>';
584 }
585 printf(
586 /* translators: %s: human-readable time difference */
587 esc_html__( 'Next run: %s', 'top-10' ),
588 /* translators: %s: human-readable time difference */
589 esc_html( sprintf( __( 'in %s', 'top-10' ), human_time_diff( time(), $next_run ) ) )
590 );
591 ?>
592 </span>
593 <?php else : ?>
594 <span style="color: #8B0000;"><?php esc_html_e( 'Not scheduled', 'top-10' ); ?></span>
595 <?php endif; ?>
596 </td>
597 </tr>
598 <?php endforeach; ?>
599
600 <?php if ( ! Database::are_tables_installed() ) : ?>
601 <tr>
602 <th scope="row"><?php esc_html_e( 'Repair database', 'top-10' ); ?></th>
603 <td>
604 <a href="<?php echo esc_url( wp_nonce_url( admin_url( 'admin.php?page=tptn_dashboard&action=recreate_tables' ), 'tptn-recreate-tables' ) ); ?>" class="button">
605 <?php esc_html_e( 'Recreate tables', 'top-10' ); ?>
606 </a>
607 </td>
608 </tr>
609 <?php endif; ?>
610 </table>
611 <?php
612
613 return ob_get_clean();
614 }
615
616 /**
617 * Handle recreate tables action from admin area.
618 *
619 * @since 4.2.0
620 */
621 /**
622 * Handle the create-missing-tables GET action triggered from the admin notice.
623 *
624 * @since 4.3.0
625 */
626 public static function handle_create_missing_tables_action() {
627 if ( ! isset( $_GET['action'] ) || 'tptn_create_missing_tables' !== $_GET['action'] || ! isset( $_GET['_wpnonce'] ) ) {
628 return;
629 }
630
631 if ( ! wp_verify_nonce( sanitize_key( $_GET['_wpnonce'] ), 'tptn-create-missing-tables' ) ) {
632 wp_die( esc_html__( 'Security check failed', 'top-10' ) );
633 }
634
635 if ( ! current_user_can( 'manage_options' ) ) {
636 wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'top-10' ) );
637 }
638
639 Activator::create_tables();
640 Database::clear_table_statistics_cache();
641
642 $still_missing = false;
643 foreach ( array( Database::get_table( false ), Database::get_table( true ), Database::get_log_table(), Database::get_funnel_table() ) as $table ) {
644 if ( ! Database::is_table_installed( $table ) ) {
645 $still_missing = true;
646 break;
647 }
648 }
649
650 $status = $still_missing ? 'failed' : 'created';
651 $referer = wp_get_referer();
652 $fallback = admin_url( 'admin.php?page=' . ( is_network_admin() ? 'tptn_network_tools_page' : 'tptn_tools_page' ) );
653 $redirect = add_query_arg( 'tptn_tables_created', $status, $referer ? $referer : $fallback );
654
655 wp_safe_redirect( $redirect );
656 exit;
657 }
658
659 /**
660 * Handle the recreate-tables GET action from the tools page.
661 *
662 * @since 4.1.0
663 */
664 public static function handle_recreate_tables_action() {
665 if ( ! isset( $_GET['action'] ) || 'recreate_tables' !== $_GET['action'] || ! isset( $_GET['_wpnonce'] ) ) {
666 return;
667 }
668
669 if ( ! wp_verify_nonce( sanitize_key( $_GET['_wpnonce'] ), 'tptn-recreate-tables' ) ) {
670 wp_die( esc_html__( 'Security check failed', 'top-10' ) );
671 }
672
673 if ( ! current_user_can( 'manage_options' ) ) {
674 wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'top-10' ) );
675 }
676
677 // Recreate tables.
678 $result_overall = Database::recreate_overall_table( false );
679 $result_daily = Database::recreate_daily_table( false );
680 $result_funnel = Database::recreate_funnel_table( false );
681 $result_log = Database::recreate_log_table( false );
682
683 // Check for errors.
684 if ( is_wp_error( $result_overall ) ) {
685 add_settings_error(
686 'tptn-notices',
687 'tptn-recreate-overall-error',
688 $result_overall->get_error_message(),
689 'error'
690 );
691 }
692
693 if ( is_wp_error( $result_daily ) ) {
694 add_settings_error(
695 'tptn-notices',
696 'tptn-recreate-daily-error',
697 $result_daily->get_error_message(),
698 'error'
699 );
700 }
701
702 if ( is_wp_error( $result_funnel ) ) {
703 add_settings_error(
704 'tptn-notices',
705 'tptn-recreate-funnel-error',
706 $result_funnel->get_error_message(),
707 'error'
708 );
709 }
710
711 if ( is_wp_error( $result_log ) ) {
712 add_settings_error(
713 'tptn-notices',
714 'tptn-recreate-log-error',
715 $result_log->get_error_message(),
716 'error'
717 );
718 }
719
720 // If no errors, add success message.
721 if ( ! is_wp_error( $result_overall ) && ! is_wp_error( $result_daily ) && ! is_wp_error( $result_funnel ) && ! is_wp_error( $result_log ) ) {
722 add_settings_error(
723 'tptn-notices',
724 'tptn-recreate-success',
725 __( 'Tables have been recreated successfully.', 'top-10' ),
726 'success'
727 );
728
729 // Clear table statistics cache since tables were recreated.
730 Database::clear_table_statistics_cache();
731 }
732
733 // Redirect back to the tools page.
734 $page = is_network_admin() ? 'tptn_network_tools_page' : 'tptn_tools_page';
735 wp_safe_redirect( admin_url( 'admin.php?page=' . $page . '&settings-updated=true' ) );
736 exit;
737 }
738 }
739