PluginProbe
WebberZone Top 10 — Popular Posts / 4.3.0
WebberZone Top 10 — Popular Posts v4.3.0
4.5.2 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 All 118 releases
top-10 / includes / admin / class-tools-page.php

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

696 lines 27.3 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 self::recreate_tables();
164 add_settings_error( 'tptn-notices', '', esc_html__( 'Top 10 tables have been recreated', 'top-10' ), 'updated' );
165 }
166
167 /* Sync funnel (run aggregation now) */
168 if ( isset( $_POST['tptn_sync_funnel'] ) && check_admin_referer( 'tptn-tools-settings' ) ) {
169 $result = Database::aggregate_visit_log();
170 if ( $result ) {
171 add_settings_error( 'tptn-notices', '', esc_html__( 'Funnel has been synced. Buffered visits have been aggregated.', 'top-10' ), 'updated' );
172 } else {
173 add_settings_error( 'tptn-notices', '', esc_html__( 'Nothing to sync. The funnel is empty or another sync is in progress.', 'top-10' ), 'updated' );
174 }
175 }
176
177 ob_start();
178 ?>
179 <div class="wrap">
180 <h1><?php esc_html_e( 'Top 10 Tools', 'top-10' ); ?></h1>
181 <?php do_action( 'tptn_settings_page_header' ); ?>
182
183 <?php settings_errors(); ?>
184
185 <div id="poststuff">
186 <div id="post-body" class="metabox-holder columns-2">
187 <div id="post-body-content">
188
189 <form method="post" >
190
191 <div class="postbox">
192 <h2><span><?php esc_html_e( 'Status', 'top-10' ); ?></span></h2>
193 <div class="inside">
194 <div class="tptn-db-status">
195 <?php echo self::get_db_status_report(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
196 </div>
197 </div>
198 </div>
199
200 <div class="postbox">
201 <h2><span><?php esc_html_e( 'Clear cache', 'top-10' ); ?></span></h2>
202 <div class="inside">
203 <p>
204 <?php
205 printf(
206 '<button type="button" name="tptn_cache_clear" class="button button-secondary tptn_cache_clear" aria-label="%1$s">%1$s</button>',
207 esc_html__( 'Clear cache', 'top-10' )
208 );
209 ?>
210 </p>
211 <p class="description">
212 <?php esc_html_e( 'Clear the Top 10 cache. This will also be cleared automatically when you save the settings page.', 'top-10' ); ?>
213 </p>
214 </div>
215 </div>
216
217 <div class="postbox">
218 <h2><span><?php esc_html_e( 'Sync Funnel', 'top-10' ); ?></span></h2>
219 <div class="inside">
220 <p>
221 <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>
222 </p>
223 <p class="description">
224 <?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' ); ?>
225 </p>
226 </div>
227 </div>
228
229 <div class="postbox">
230 <h2><span><?php esc_html_e( 'Recreate Primary Key', 'top-10' ); ?></span></h2>
231 <div class="inside">
232 <p>
233 <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>
234 </p>
235 <p class="description">
236 <?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' ); ?>
237 </p>
238 <p>
239 <code style="display:block;"><?php echo self::recreate_primary_key_html(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></code>
240 </p>
241 </div>
242 </div>
243
244 <div class="postbox">
245 <h2><span><?php esc_html_e( 'Reset database', 'top-10' ); ?></span></h2>
246 <div class="inside">
247 <p class="description">
248 <?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' ); ?>
249 </p>
250 <p>
251 <?php
252 printf(
253 '<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>',
254 esc_attr__( 'Are you sure you want to reset the popular posts?', 'top-10' ),
255 esc_attr__( 'Reset Popular Posts', 'top-10' )
256 );
257 ?>
258 </p>
259 <p>
260 <?php
261 printf(
262 '<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>',
263 esc_attr__( 'Are you sure you want to reset the daily popular posts?', 'top-10' ),
264 esc_attr__( 'Reset Daily Popular Posts', 'top-10' )
265 );
266 ?>
267 </p>
268 </div>
269 </div>
270
271 <?php if ( ! is_multisite() || is_network_admin() ) : ?>
272 <div class="postbox">
273 <h2><span><?php esc_html_e( 'Recreate Database Tables', 'top-10' ); ?></span></h2>
274 <div class="inside">
275 <p class="description">
276 <?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' ); ?>
277 </p>
278 <p>
279 <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>
280 </p>
281 </div>
282 </div>
283 <?php endif; ?>
284
285 <?php wp_nonce_field( 'tptn-tools-settings' ); ?>
286 </form>
287
288 </div><!-- /#post-body-content -->
289
290 <div id="postbox-container-1" class="postbox-container">
291
292 <div id="side-sortables" class="meta-box-sortables ui-sortable">
293 <?php include_once 'sidebar.php'; ?>
294 </div><!-- /#side-sortables -->
295
296 </div><!-- /#postbox-container-1 -->
297 </div><!-- /#post-body -->
298 <br class="clear" />
299 </div><!-- /#poststuff -->
300
301 </div><!-- /.wrap -->
302
303 <?php
304 echo ob_get_clean(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
305 }
306
307 /**
308 * Function to delete and create the primary keys in the database table.
309 *
310 * @since 2.5.6
311 */
312 public static function recreate_primary_key() {
313 global $wpdb;
314
315 $table_name = Database::get_table( false );
316 $table_name_daily = Database::get_table( true );
317
318 $wpdb->hide_errors();
319
320 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
321 $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
322 }
323 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
324 $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
325 }
326 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
327 $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
328 }
329
330 $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
331 $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
332 $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
333
334 $wpdb->show_errors();
335 }
336
337 /**
338 * Retrieves the SQL code to recreate the PRIMARY KEY.
339 *
340 * @since 2.5.7
341 */
342 public static function recreate_primary_key_html() {
343
344 $table_name = Database::get_table( false );
345 $table_name_daily = Database::get_table( true );
346
347 $sql = 'ALTER TABLE ' . $table_name . ' DROP PRIMARY KEY; ';
348 $sql .= '<br />';
349 $sql .= 'ALTER TABLE ' . $table_name_daily . ' DROP PRIMARY KEY; ';
350 $sql .= '<br />';
351 $sql .= 'ALTER TABLE ' . $table_name_daily . ' DROP INDEX IF EXISTS blog_date; ';
352 $sql .= '<br />';
353 $sql .= 'ALTER TABLE ' . $table_name . ' ADD PRIMARY KEY(postnumber, blog_id); ';
354 $sql .= '<br />';
355 $sql .= 'ALTER TABLE ' . $table_name_daily . ' ADD PRIMARY KEY(postnumber, dp_date, blog_id); ';
356 $sql .= '<br />';
357 $sql .= 'ALTER TABLE ' . $table_name_daily . ' ADD INDEX blog_date(blog_id, dp_date, postnumber); ';
358
359 /**
360 * Filters the SQL code to recreate the PRIMARY KEY.
361 *
362 * @since 2.5.7
363 * @param string $sql SQL code to recreate PRIMARY KEY.
364 */
365 return apply_filters( 'tptn_recreate_primary_key_html', $sql );
366 }
367
368 /**
369 * Retrieves the SQL code to recreate the PRIMARY KEY.
370 *
371 * @since 2.7.0
372 */
373 public static function recreate_tables() {
374 global $wpdb;
375
376 $table_name = Database::get_table( false );
377 $table_name_daily = Database::get_table( true );
378 $table_name_temp = $table_name . '_temp';
379 $table_name_daily_temp = $table_name_daily . '_temp';
380
381 $wpdb->hide_errors();
382
383 // Overall table.
384 $wpdb->query( "DROP TEMPORARY TABLE IF EXISTS {$table_name_temp}" ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.SchemaChange, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
385 $result_temp = $wpdb->query( "CREATE TEMPORARY TABLE {$table_name_temp} SELECT * FROM {$table_name};" ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.SchemaChange, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
386
387 if ( false !== $result_temp ) {
388 $wpdb->query( "DROP TABLE {$table_name}" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.SchemaChange, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
389 Activator::single_activate();
390 $wpdb->query( "INSERT INTO `{$table_name}` (postnumber, cntaccess, blog_id) SELECT postnumber, cntaccess, blog_id FROM `{$table_name_temp}`;" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
391 $wpdb->query( "DROP TEMPORARY TABLE {$table_name_temp}" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.SchemaChange, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
392 }
393
394 // Daily table.
395 $wpdb->query( "DROP TEMPORARY TABLE IF EXISTS {$table_name_daily_temp}" ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.SchemaChange, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
396 $result_daily_temp = $wpdb->query( "CREATE TEMPORARY TABLE {$table_name_daily_temp} SELECT * FROM {$table_name_daily};" ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.SchemaChange, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
397
398 if ( false !== $result_daily_temp ) {
399 $wpdb->query( "DROP TABLE {$table_name_daily}" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.SchemaChange, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
400 Activator::single_activate();
401 $wpdb->query( "INSERT INTO `{$table_name_daily}` (postnumber, cntaccess, dp_date, blog_id) SELECT postnumber, cntaccess, dp_date, blog_id FROM `{$table_name_daily_temp}`;" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
402 $wpdb->query( "DROP TEMPORARY TABLE {$table_name_daily_temp}" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.SchemaChange, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
403 }
404
405 $wpdb->show_errors();
406 }
407
408 /**
409 * Generates the Tools help page.
410 *
411 * @since 3.3.0
412 */
413 public static function help_tabs() {
414 $screen = get_current_screen();
415
416 $screen->set_help_sidebar(
417 /* translators: 1: Support link. */
418 '<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>' .
419 /* translators: 1: Forum link. */
420 '<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>' .
421 '<p>' . sprintf(
422 /* translators: 1: Github Issues link, 2: Github page. */
423 __( '<a href="%1$s">Post an issue</a> on <a href="%2$s">GitHub</a> (bug reports only).', 'top-10' ),
424 esc_url( 'https://github.com/WebberZone/top-10/issues' ),
425 esc_url( 'https://github.com/WebberZone/top-10' )
426 ) . '</p>'
427 );
428
429 $screen->add_help_tab(
430 array(
431 'id' => 'tptn-settings-general',
432 'title' => __( 'General', 'top-10' ),
433 'content' =>
434 '<p>' . __( 'This screen provides some tools that help maintain certain features of Top 10.', 'top-10' ) . '</p>' .
435 '<p>' . __( 'Clear the cache, reset the popular posts tables plus some miscellaneous fixes for older versions of Top 10.', 'top-10' ) . '</p>',
436 )
437 );
438
439 do_action( 'tptn_settings_tools_help', $screen );
440 }
441
442 /**
443 * Check if tables exist and create them if they don't.
444 *
445 * @since 4.2.0
446 *
447 * @return array Array of table statuses indicating whether they are installed.
448 */
449 public static function check_table_status() {
450 $tables = array(
451 'top_ten' => Database::get_table( false ),
452 'top_ten_daily' => Database::get_table( true ),
453 'top_ten_visits_funnel' => Database::get_funnel_table(),
454 'top_ten_visits_log' => Database::get_log_table(),
455 );
456
457 $statuses = array();
458
459 $installed_label = '<span style="color: #006400;">' . __( 'Installed', 'top-10' ) . '</span>';
460 $not_installed_label = '<span style="color: #8B0000;">' . __( 'Not Installed', 'top-10' ) . '</span>';
461
462 foreach ( $tables as $key => $table_name ) {
463 $statuses[ $key ] = Database::is_table_installed( $table_name ) ? $installed_label : $not_installed_label;
464 }
465
466 // Create tables if they don't exist.
467 if ( ! Database::are_tables_installed() ) {
468 // Use Activator to create tables.
469 Activator::create_tables();
470 Database::clear_table_statistics_cache();
471
472 // Refresh statuses after creating tables.
473 foreach ( $tables as $key => $table_name ) {
474 $statuses[ $key ] = Database::is_table_installed( $table_name ) ? $installed_label : $not_installed_label;
475 }
476 }
477
478 /**
479 * Filter the table statuses report.
480 *
481 * @since 4.1.0
482 *
483 * @param array $statuses Array of table statuses.
484 */
485 return apply_filters( 'tptn_table_statuses', $statuses );
486 }
487
488 /**
489 * Get database status report.
490 *
491 * @since 4.2.0
492 *
493 * @return string HTML output for the database status report.
494 */
495 public static function get_db_status_report() {
496 global $tptn_db_version;
497
498 // Get table statuses.
499 $statuses = self::check_table_status();
500
501 // Get table statistics from Database class.
502 $table_stats = Database::get_table_statistics();
503
504 ob_start();
505 ?>
506 <table class="form-table">
507 <tr>
508 <th scope="row"><?php esc_html_e( 'Database version', 'top-10' ); ?></th>
509 <td>
510 <?php esc_html_e( 'Installed version', 'top-10' ); ?> <?php echo esc_html( get_site_option( 'tptn_db_version', '0' ) ); ?> /
511 <?php esc_html_e( 'Current version', 'top-10' ); ?> <?php echo esc_html( $tptn_db_version ); ?>
512 </td>
513 </tr>
514
515 <?php
516 $table_keys = array( 'top_ten', 'top_ten_daily', 'top_ten_visits_funnel', 'top_ten_visits_log' );
517 foreach ( $table_keys as $table_key ) :
518 if ( ! isset( $statuses[ $table_key ] ) ) {
519 continue;
520 }
521 ?>
522 <tr>
523 <th scope="row"><?php printf( /* translators: %s: Table name */ esc_html__( '%s table', 'top-10' ), esc_html( $table_key ) ); ?></th>
524 <td>
525 <?php
526 echo $statuses[ $table_key ]; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
527
528 if ( isset( $table_stats[ $table_key ] ) ) {
529 $format = ( is_multisite() && ! is_network_admin() )
530 /* translators: 1: Number of entries, 2: Estimated table size */
531 ? __( 'Entries: %1$s | Est. Size: %2$s', 'top-10' )
532 /* translators: 1: Number of entries, 2: Table size */
533 : __( 'Entries: %1$s | Size: %2$s', 'top-10' );
534
535 echo '<br><span class="description">';
536 printf(
537 esc_html( $format ),
538 '<strong>' . esc_html( number_format_i18n( $table_stats[ $table_key ]['entries'] ) ) . '</strong>',
539 '<strong>' . esc_html( size_format( $table_stats[ $table_key ]['size'] ) ) . '</strong>'
540 );
541 echo '</span>';
542 }
543 ?>
544 </td>
545 </tr>
546 <?php endforeach; ?>
547
548 <?php
549 $cron_jobs = array(
550 'tptn_cron_hook' => __( 'Maintenance cron', 'top-10' ),
551 'tptn_aggregation_cron_hook' => __( 'Aggregation cron', 'top-10' ),
552 );
553 foreach ( $cron_jobs as $hook => $label ) :
554 $next_run = wp_next_scheduled( $hook );
555 ?>
556 <tr>
557 <th scope="row"><?php echo esc_html( $label ); ?></th>
558 <td>
559 <?php if ( $next_run ) : ?>
560 <span style="color: #006400;"><?php esc_html_e( 'Scheduled', 'top-10' ); ?></span>
561 <br><span class="description">
562 <?php
563 printf(
564 /* translators: %s: human-readable time difference */
565 esc_html__( 'Next run: %s', 'top-10' ),
566 esc_html( sprintf( __( 'in %s', 'top-10' ), human_time_diff( time(), $next_run ) ) )
567 );
568 ?>
569 </span>
570 <?php else : ?>
571 <span style="color: #8B0000;"><?php esc_html_e( 'Not scheduled', 'top-10' ); ?></span>
572 <?php endif; ?>
573 </td>
574 </tr>
575 <?php endforeach; ?>
576
577 <?php if ( ! Database::are_tables_installed() ) : ?>
578 <tr>
579 <th scope="row"><?php esc_html_e( 'Repair database', 'top-10' ); ?></th>
580 <td>
581 <a href="<?php echo esc_url( wp_nonce_url( admin_url( 'admin.php?page=tptn_dashboard&action=recreate_tables' ), 'tptn-recreate-tables' ) ); ?>" class="button">
582 <?php esc_html_e( 'Recreate tables', 'top-10' ); ?>
583 </a>
584 </td>
585 </tr>
586 <?php endif; ?>
587 </table>
588 <?php
589
590 return ob_get_clean();
591 }
592
593 /**
594 * Handle recreate tables action from admin area.
595 *
596 * @since 4.2.0
597 */
598 /**
599 * Handle the create-missing-tables GET action triggered from the admin notice.
600 *
601 * @since 4.3.0
602 */
603 public static function handle_create_missing_tables_action() {
604 if ( ! isset( $_GET['action'] ) || 'tptn_create_missing_tables' !== $_GET['action'] || ! isset( $_GET['_wpnonce'] ) ) {
605 return;
606 }
607
608 if ( ! wp_verify_nonce( sanitize_key( $_GET['_wpnonce'] ), 'tptn-create-missing-tables' ) ) {
609 wp_die( esc_html__( 'Security check failed', 'top-10' ) );
610 }
611
612 if ( ! current_user_can( 'manage_options' ) ) {
613 wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'top-10' ) );
614 }
615
616 Activator::create_tables();
617 Database::clear_table_statistics_cache();
618
619 $still_missing = false;
620 foreach ( array( Database::get_table( false ), Database::get_table( true ), Database::get_log_table(), Database::get_funnel_table() ) as $table ) {
621 if ( ! Database::is_table_installed( $table ) ) {
622 $still_missing = true;
623 break;
624 }
625 }
626
627 $status = $still_missing ? 'failed' : 'created';
628 $referer = wp_get_referer();
629 $fallback = admin_url( 'admin.php?page=' . ( is_network_admin() ? 'tptn_network_tools_page' : 'tptn_tools_page' ) );
630 $redirect = add_query_arg( 'tptn_tables_created', $status, $referer ? $referer : $fallback );
631
632 wp_safe_redirect( $redirect );
633 exit;
634 }
635
636 /**
637 * Handle the recreate-tables GET action from the tools page.
638 *
639 * @since 4.1.0
640 */
641 public static function handle_recreate_tables_action() {
642 if ( ! isset( $_GET['action'] ) || 'recreate_tables' !== $_GET['action'] || ! isset( $_GET['_wpnonce'] ) ) {
643 return;
644 }
645
646 if ( ! wp_verify_nonce( sanitize_key( $_GET['_wpnonce'] ), 'tptn-recreate-tables' ) ) {
647 wp_die( esc_html__( 'Security check failed', 'top-10' ) );
648 }
649
650 if ( ! current_user_can( 'manage_options' ) ) {
651 wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'top-10' ) );
652 }
653
654 // Recreate tables.
655 $result_overall = Database::recreate_overall_table( false );
656 $result_daily = Database::recreate_daily_table( false );
657
658 // Check for errors.
659 if ( is_wp_error( $result_overall ) ) {
660 add_settings_error(
661 'tptn-notices',
662 'tptn-recreate-overall-error',
663 $result_overall->get_error_message(),
664 'error'
665 );
666 }
667
668 if ( is_wp_error( $result_daily ) ) {
669 add_settings_error(
670 'tptn-notices',
671 'tptn-recreate-daily-error',
672 $result_daily->get_error_message(),
673 'error'
674 );
675 }
676
677 // If no errors, add success message.
678 if ( ! is_wp_error( $result_overall ) && ! is_wp_error( $result_daily ) ) {
679 add_settings_error(
680 'tptn-notices',
681 'tptn-recreate-success',
682 __( 'Tables have been recreated successfully.', 'top-10' ),
683 'success'
684 );
685
686 // Clear table statistics cache since tables were recreated.
687 Database::clear_table_statistics_cache();
688 }
689
690 // Redirect back to the tools page.
691 $page = is_network_admin() ? 'tptn_network_tools_page' : 'tptn_tools_page';
692 wp_safe_redirect( admin_url( 'admin.php?page=' . $page . '&settings-updated=true' ) );
693 exit;
694 }
695 }
696