| 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 |
'network_confirm_message' => esc_html__( 'Are you sure you want to clear the Top 10 cache across all active sites?', 'top-10' ), |
| 112 |
'clearing_text' => esc_html__( 'Clearing...', 'top-10' ), |
| 113 |
'fail_message' => esc_html__( 'Failed to clear cache. Please try again.', 'top-10' ), |
| 114 |
'request_fail_message' => esc_html__( 'Request failed: ', 'top-10' ), |
| 115 |
), |
| 116 |
) |
| 117 |
); |
| 118 |
} |
| 119 |
} |
| 120 |
|
| 121 |
/** |
| 122 |
* Render the tools settings page. |
| 123 |
* |
| 124 |
* @since 2.5.0 |
| 125 |
* |
| 126 |
* @return void |
| 127 |
*/ |
| 128 |
public function render_page() { |
| 129 |
$screen = get_current_screen(); |
| 130 |
$network_wide = is_multisite() && is_network_admin(); |
| 131 |
|
| 132 |
// Keep the screen check as a fallback for admin screen implementations that |
| 133 |
// do not expose is_network_admin() reliably. |
| 134 |
if ( $screen && $screen->id === $this->parent_id . '-network' ) { |
| 135 |
$network_wide = true; |
| 136 |
} |
| 137 |
|
| 138 |
/* Recreate the shared table indexes. */ |
| 139 |
if ( ( ! is_multisite() || $network_wide ) && isset( $_POST['tptn_recreate_primary_key'] ) && check_admin_referer( 'tptn-tools-settings' ) ) { |
| 140 |
self::recreate_primary_key(); |
| 141 |
add_settings_error( 'tptn-notices', '', esc_html__( 'Primary Key has been recreated', 'top-10' ), 'updated' ); |
| 142 |
} |
| 143 |
|
| 144 |
/* Truncate overall posts table */ |
| 145 |
if ( isset( $_POST['tptn_reset_overall'] ) && check_admin_referer( 'tptn-tools-settings' ) ) { |
| 146 |
if ( ! is_multisite() || $network_wide ) { |
| 147 |
Database::truncate_table( Database::get_table( false ) ); |
| 148 |
} else { |
| 149 |
Counter::delete_counts( array( 'daily' => false ) ); |
| 150 |
} |
| 151 |
Dashboard_Widgets::clear_network_dashboard_cache(); |
| 152 |
add_settings_error( 'tptn-notices', '', esc_html__( 'Top 10 popular posts reset', 'top-10' ), 'updated' ); |
| 153 |
} |
| 154 |
|
| 155 |
/* Truncate daily posts table */ |
| 156 |
if ( isset( $_POST['tptn_reset_daily'] ) && check_admin_referer( 'tptn-tools-settings' ) ) { |
| 157 |
if ( ! is_multisite() || $network_wide ) { |
| 158 |
Database::truncate_table( Database::get_table( true ) ); |
| 159 |
} else { |
| 160 |
Counter::delete_counts( array( 'daily' => true ) ); |
| 161 |
} |
| 162 |
Dashboard_Widgets::clear_network_dashboard_cache(); |
| 163 |
add_settings_error( 'tptn-notices', '', esc_html__( 'Top 10 daily popular posts reset', 'top-10' ), 'updated' ); |
| 164 |
} |
| 165 |
|
| 166 |
/* Recreate the shared tables. */ |
| 167 |
if ( ( ! is_multisite() || $network_wide ) && isset( $_POST['tptn_recreate_tables'] ) && check_admin_referer( 'tptn-tools-settings' ) ) { |
| 168 |
$result = self::recreate_tables(); |
| 169 |
if ( is_wp_error( $result ) ) { |
| 170 |
add_settings_error( 'tptn-notices', '', $result->get_error_message(), 'error' ); |
| 171 |
} else { |
| 172 |
add_settings_error( 'tptn-notices', '', esc_html__( 'Top 10 tables have been recreated', 'top-10' ), 'updated' ); |
| 173 |
} |
| 174 |
} |
| 175 |
|
| 176 |
/* Sync funnel (run aggregation now) */ |
| 177 |
if ( isset( $_POST['tptn_sync_funnel'] ) && check_admin_referer( 'tptn-tools-settings' ) ) { |
| 178 |
$blog_id = ( is_multisite() && ! $network_wide ) ? get_current_blog_id() : null; |
| 179 |
$result = Database::aggregate_visit_log( 10000, $blog_id ); |
| 180 |
if ( true === $result ) { |
| 181 |
$message = $network_wide |
| 182 |
? __( 'The network funnel has been synced. Buffered visits from all sites have been aggregated.', 'top-10' ) |
| 183 |
: __( 'The site funnel has been synced. Buffered visits for this site have been aggregated.', 'top-10' ); |
| 184 |
add_settings_error( 'tptn-notices', '', esc_html( $message ), 'updated' ); |
| 185 |
} elseif ( 0 === $result ) { |
| 186 |
$message = $network_wide |
| 187 |
? __( 'Nothing to sync. The network funnel is empty.', 'top-10' ) |
| 188 |
: __( 'Nothing to sync. This site funnel is empty.', 'top-10' ); |
| 189 |
add_settings_error( 'tptn-notices', '', esc_html( $message ), 'updated' ); |
| 190 |
} elseif ( false === $result ) { |
| 191 |
add_settings_error( 'tptn-notices', '', esc_html__( 'Sync skipped: another aggregation is already in progress. Please try again in a moment.', 'top-10' ), 'updated' ); |
| 192 |
} elseif ( is_wp_error( $result ) ) { |
| 193 |
add_settings_error( |
| 194 |
'tptn-notices', |
| 195 |
'', |
| 196 |
sprintf( |
| 197 |
/* translators: %s: error message from the database */ |
| 198 |
esc_html__( 'Sync failed: %s', 'top-10' ), |
| 199 |
esc_html( $result->get_error_message() ) |
| 200 |
), |
| 201 |
'error' |
| 202 |
); |
| 203 |
} |
| 204 |
} |
| 205 |
|
| 206 |
/* Fix cron schedules */ |
| 207 |
if ( isset( $_POST['tptn_fix_crons'] ) && check_admin_referer( 'tptn-tools-settings' ) ) { |
| 208 |
$result = self::fix_crons( $network_wide ); |
| 209 |
if ( is_wp_error( $result ) ) { |
| 210 |
add_settings_error( 'tptn-notices', '', implode( ' ', array_map( 'esc_html', $result->get_error_messages() ) ), 'error' ); |
| 211 |
} else { |
| 212 |
add_settings_error( 'tptn-notices', '', esc_html( $result ), 'updated' ); |
| 213 |
} |
| 214 |
} |
| 215 |
|
| 216 |
ob_start(); |
| 217 |
?> |
| 218 |
<div class="wrap"> |
| 219 |
<h1><?php esc_html_e( 'Top 10 Tools', 'top-10' ); ?></h1> |
| 220 |
<?php |
| 221 |
/** This action is documented in includes/admin/class-dashboard.php */ |
| 222 |
do_action( 'tptn_settings_page_header' ); |
| 223 |
?> |
| 224 |
|
| 225 |
<?php settings_errors(); ?> |
| 226 |
|
| 227 |
<div id="poststuff"> |
| 228 |
<div id="post-body" class="metabox-holder columns-2"> |
| 229 |
<div id="post-body-content"> |
| 230 |
|
| 231 |
<form method="post" > |
| 232 |
|
| 233 |
<div class="postbox"> |
| 234 |
<h2><span><?php esc_html_e( 'Status', 'top-10' ); ?></span></h2> |
| 235 |
<div class="inside"> |
| 236 |
<div class="tptn-db-status"> |
| 237 |
<?php echo self::get_db_status_report(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> |
| 238 |
</div> |
| 239 |
</div> |
| 240 |
</div> |
| 241 |
|
| 242 |
<div class="postbox"> |
| 243 |
<h2><span><?php esc_html_e( 'Clear cache', 'top-10' ); ?></span></h2> |
| 244 |
<div class="inside"> |
| 245 |
<p> |
| 246 |
<?php |
| 247 |
printf( |
| 248 |
'<button type="button" name="tptn_cache_clear" class="button button-secondary tptn_cache_clear" data-network-wide="%1$s" aria-label="%2$s">%2$s</button>', |
| 249 |
$network_wide ? '1' : '0', |
| 250 |
esc_html( $network_wide ? __( 'Clear Cache Across Network', 'top-10' ) : __( 'Clear cache', 'top-10' ) ) |
| 251 |
); |
| 252 |
?> |
| 253 |
</p> |
| 254 |
<p class="description"> |
| 255 |
<?php |
| 256 |
if ( $network_wide ) { |
| 257 |
esc_html_e( 'Clear the Top 10 cache for all active sites in the network.', 'top-10' ); |
| 258 |
} else { |
| 259 |
esc_html_e( 'Clear the Top 10 cache. This will also be cleared automatically when you save the settings page.', 'top-10' ); |
| 260 |
} |
| 261 |
?> |
| 262 |
</p> |
| 263 |
</div> |
| 264 |
</div> |
| 265 |
|
| 266 |
<div class="postbox"> |
| 267 |
<h2><span><?php echo esc_html( $network_wide ? __( 'Sync Network Funnel', 'top-10' ) : __( 'Sync Funnel', 'top-10' ) ); ?></span></h2> |
| 268 |
<div class="inside"> |
| 269 |
<p> |
| 270 |
<button name="tptn_sync_funnel" type="submit" id="tptn_sync_funnel" class="button button-secondary"><?php echo esc_html( $network_wide ? __( 'Sync Network Funnel Now', 'top-10' ) : __( 'Sync Funnel Now', 'top-10' ) ); ?></button> |
| 271 |
</p> |
| 272 |
<p class="description"> |
| 273 |
<?php |
| 274 |
if ( $network_wide ) { |
| 275 |
esc_html_e( 'Drain buffered visits from all sites into the count tables immediately. This is equivalent to running the aggregation cron job.', 'top-10' ); |
| 276 |
} else { |
| 277 |
esc_html_e( 'Drain this site\'s buffered visits into the count tables immediately. Other sites\' buffered visits are not affected.', 'top-10' ); |
| 278 |
} |
| 279 |
?> |
| 280 |
</p> |
| 281 |
</div> |
| 282 |
</div> |
| 283 |
|
| 284 |
<?php |
| 285 |
/** |
| 286 |
* Fires between the action boxes on the Tools page. |
| 287 |
* |
| 288 |
* @since 4.5.0 |
| 289 |
* |
| 290 |
* @param bool $network_wide Whether the Tools page is being rendered in network admin. |
| 291 |
*/ |
| 292 |
do_action( 'tptn_tools_page_actions', $network_wide ); |
| 293 |
?> |
| 294 |
|
| 295 |
<div class="postbox"> |
| 296 |
<h2><span><?php echo esc_html( $network_wide ? __( 'Fix Network Cron Schedules', 'top-10' ) : __( 'Fix Cron Schedules', 'top-10' ) ); ?></span></h2> |
| 297 |
<div class="inside"> |
| 298 |
<p> |
| 299 |
<button name="tptn_fix_crons" type="submit" id="tptn_fix_crons" class="button button-secondary"><?php echo esc_html( $network_wide ? __( 'Fix Network Cron Schedules', 'top-10' ) : __( 'Fix Cron Schedules', 'top-10' ) ); ?></button> |
| 300 |
</p> |
| 301 |
<p class="description"> |
| 302 |
<?php |
| 303 |
if ( $network_wide ) { |
| 304 |
esc_html_e( 'Clears and reschedules the Top 10 maintenance and aggregation jobs for all active sites. Use this if a site\'s job is not scheduled or if your error log reports cron reschedule errors.', 'top-10' ); |
| 305 |
} else { |
| 306 |
esc_html_e( 'Clears and reschedules the Top 10 cron jobs for this site. Use this if the status above shows a job as not scheduled or if your error log reports cron reschedule errors for the Top 10 hooks.', 'top-10' ); |
| 307 |
} |
| 308 |
?> |
| 309 |
</p> |
| 310 |
</div> |
| 311 |
</div> |
| 312 |
|
| 313 |
<?php if ( ! is_multisite() || $network_wide ) : ?> |
| 314 |
<div class="postbox"> |
| 315 |
<h2><span><?php esc_html_e( 'Recreate Primary Key', 'top-10' ); ?></span></h2> |
| 316 |
<div class="inside"> |
| 317 |
<p> |
| 318 |
<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> |
| 319 |
</p> |
| 320 |
<p class="description"> |
| 321 |
<?php esc_html_e( 'Deletes and reinitializes the primary key in the shared database tables. Run this from Network Admin on multisite installations. Remember to back up your database first!', 'top-10' ); ?> |
| 322 |
</p> |
| 323 |
<p> |
| 324 |
<code style="display:block;"><?php echo self::recreate_primary_key_html(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></code> |
| 325 |
</p> |
| 326 |
</div> |
| 327 |
</div> |
| 328 |
<?php endif; ?> |
| 329 |
|
| 330 |
<div class="postbox"> |
| 331 |
<h2><span><?php esc_html_e( 'Reset database', 'top-10' ); ?></span></h2> |
| 332 |
<div class="inside"> |
| 333 |
<p class="description"> |
| 334 |
<?php |
| 335 |
if ( $network_wide ) { |
| 336 |
esc_html_e( 'This will reset the Top 10 popular posts across all sites in the network. This cannot be reversed. Make sure that your database has been backed up before proceeding.', 'top-10' ); |
| 337 |
} elseif ( is_multisite() ) { |
| 338 |
esc_html_e( 'This will reset the Top 10 popular posts for the current site only. Other sites in the network will not be affected. This cannot be reversed. Make sure that your database has been backed up before proceeding.', 'top-10' ); |
| 339 |
} else { |
| 340 |
esc_html_e( 'This will reset the Top 10 popular posts for this site. This cannot be reversed. Make sure that your database has been backed up before proceeding.', 'top-10' ); |
| 341 |
} |
| 342 |
?> |
| 343 |
</p> |
| 344 |
<p> |
| 345 |
<?php |
| 346 |
printf( |
| 347 |
'<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>', |
| 348 |
esc_attr__( $network_wide ? 'Are you sure you want to reset popular posts across the network?' : 'Are you sure you want to reset the popular posts for this site?', 'top-10' ), |
| 349 |
esc_attr__( $network_wide ? 'Reset Popular Posts Across Network' : 'Reset Popular Posts', 'top-10' ) |
| 350 |
); |
| 351 |
?> |
| 352 |
</p> |
| 353 |
<p> |
| 354 |
<?php |
| 355 |
printf( |
| 356 |
'<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>', |
| 357 |
esc_attr__( $network_wide ? 'Are you sure you want to reset daily popular posts across the network?' : 'Are you sure you want to reset the daily popular posts for this site?', 'top-10' ), |
| 358 |
esc_attr__( $network_wide ? 'Reset Daily Popular Posts Across Network' : 'Reset Daily Popular Posts', 'top-10' ) |
| 359 |
); |
| 360 |
?> |
| 361 |
</p> |
| 362 |
</div> |
| 363 |
</div> |
| 364 |
|
| 365 |
<?php if ( ! is_multisite() || $network_wide ) : ?> |
| 366 |
<div class="postbox"> |
| 367 |
<h2><span><?php esc_html_e( 'Recreate Database Tables', 'top-10' ); ?></span></h2> |
| 368 |
<div class="inside"> |
| 369 |
<p class="description"> |
| 370 |
<?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' ); ?> |
| 371 |
</p> |
| 372 |
<p> |
| 373 |
<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> |
| 374 |
</p> |
| 375 |
</div> |
| 376 |
</div> |
| 377 |
<?php endif; ?> |
| 378 |
|
| 379 |
<?php wp_nonce_field( 'tptn-tools-settings' ); ?> |
| 380 |
</form> |
| 381 |
|
| 382 |
</div><!-- /#post-body-content --> |
| 383 |
|
| 384 |
<div id="postbox-container-1" class="postbox-container"> |
| 385 |
|
| 386 |
<div id="side-sortables" class="meta-box-sortables ui-sortable"> |
| 387 |
<?php include_once 'sidebar.php'; ?> |
| 388 |
</div><!-- /#side-sortables --> |
| 389 |
|
| 390 |
</div><!-- /#postbox-container-1 --> |
| 391 |
</div><!-- /#post-body --> |
| 392 |
<br class="clear" /> |
| 393 |
</div><!-- /#poststuff --> |
| 394 |
|
| 395 |
</div><!-- /.wrap --> |
| 396 |
|
| 397 |
<?php |
| 398 |
echo ob_get_clean(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped |
| 399 |
} |
| 400 |
|
| 401 |
/** |
| 402 |
* Function to delete and create the primary keys in the database table. |
| 403 |
* |
| 404 |
* @since 2.5.6 |
| 405 |
*/ |
| 406 |
public static function recreate_primary_key() { |
| 407 |
global $wpdb; |
| 408 |
|
| 409 |
$table_name = Database::get_table( false ); |
| 410 |
$table_name_daily = Database::get_table( true ); |
| 411 |
|
| 412 |
$show_errors = $wpdb->hide_errors(); |
| 413 |
|
| 414 |
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 |
| 415 |
$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 |
| 416 |
} |
| 417 |
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 |
| 418 |
$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 |
| 419 |
} |
| 420 |
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 |
| 421 |
$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 |
| 422 |
} |
| 423 |
|
| 424 |
$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 |
| 425 |
$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 |
| 426 |
$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 |
| 427 |
|
| 428 |
$wpdb->show_errors( $show_errors ); |
| 429 |
} |
| 430 |
|
| 431 |
/** |
| 432 |
* Retrieves the SQL code to recreate the PRIMARY KEY. |
| 433 |
* |
| 434 |
* @since 2.5.7 |
| 435 |
*/ |
| 436 |
public static function recreate_primary_key_html() { |
| 437 |
|
| 438 |
$table_name = Database::get_table( false ); |
| 439 |
$table_name_daily = Database::get_table( true ); |
| 440 |
|
| 441 |
$sql = 'ALTER TABLE ' . $table_name . ' DROP PRIMARY KEY; '; |
| 442 |
$sql .= '<br />'; |
| 443 |
$sql .= 'ALTER TABLE ' . $table_name_daily . ' DROP PRIMARY KEY; '; |
| 444 |
$sql .= '<br />'; |
| 445 |
$sql .= 'ALTER TABLE ' . $table_name_daily . ' DROP INDEX IF EXISTS blog_date; '; |
| 446 |
$sql .= '<br />'; |
| 447 |
$sql .= 'ALTER TABLE ' . $table_name . ' ADD PRIMARY KEY(postnumber, blog_id); '; |
| 448 |
$sql .= '<br />'; |
| 449 |
$sql .= 'ALTER TABLE ' . $table_name_daily . ' ADD PRIMARY KEY(postnumber, dp_date, blog_id); '; |
| 450 |
$sql .= '<br />'; |
| 451 |
$sql .= 'ALTER TABLE ' . $table_name_daily . ' ADD INDEX blog_date(blog_id, dp_date, postnumber); '; |
| 452 |
|
| 453 |
/** |
| 454 |
* Filters the SQL code to recreate the PRIMARY KEY. |
| 455 |
* |
| 456 |
* @since 2.5.7 |
| 457 |
* @param string $sql SQL code to recreate PRIMARY KEY. |
| 458 |
*/ |
| 459 |
return apply_filters( 'tptn_recreate_primary_key_html', $sql ); |
| 460 |
} |
| 461 |
|
| 462 |
/** |
| 463 |
* Retrieves the SQL code to recreate the PRIMARY KEY. |
| 464 |
* |
| 465 |
* @since 2.7.0 |
| 466 |
*/ |
| 467 |
public static function recreate_tables() { |
| 468 |
$errors = new \WP_Error(); |
| 469 |
|
| 470 |
$result = Database::recreate_overall_table( false ); |
| 471 |
if ( is_wp_error( $result ) ) { |
| 472 |
$errors->merge_from( $result ); |
| 473 |
} |
| 474 |
|
| 475 |
$result = Database::recreate_daily_table( false ); |
| 476 |
if ( is_wp_error( $result ) ) { |
| 477 |
$errors->merge_from( $result ); |
| 478 |
} |
| 479 |
|
| 480 |
$result = Database::recreate_funnel_table( false ); |
| 481 |
if ( is_wp_error( $result ) ) { |
| 482 |
$errors->merge_from( $result ); |
| 483 |
} |
| 484 |
|
| 485 |
$result = Database::recreate_log_table( false ); |
| 486 |
if ( is_wp_error( $result ) ) { |
| 487 |
$errors->merge_from( $result ); |
| 488 |
} |
| 489 |
|
| 490 |
Database::clear_table_installation_cache(); |
| 491 |
Dashboard_Widgets::clear_network_dashboard_cache(); |
| 492 |
|
| 493 |
return $errors->has_errors() ? $errors : true; |
| 494 |
} |
| 495 |
|
| 496 |
/** |
| 497 |
* Clear and reschedule the Top 10 cron jobs. |
| 498 |
* |
| 499 |
* @since 4.4.0 |
| 500 |
* |
| 501 |
* @param bool $network_wide Whether to repair cron schedules for all active sites. |
| 502 |
* @return string|\WP_Error Success message or WP_Error if a job could not be rescheduled. |
| 503 |
*/ |
| 504 |
public static function fix_crons( $network_wide = false ) { |
| 505 |
if ( $network_wide && is_multisite() ) { |
| 506 |
$site_ids = wp_list_pluck( |
| 507 |
get_sites( |
| 508 |
array( |
| 509 |
'archived' => 0, |
| 510 |
'spam' => 0, |
| 511 |
'deleted' => 0, |
| 512 |
'number' => 0, |
| 513 |
) |
| 514 |
), |
| 515 |
'blog_id' |
| 516 |
); |
| 517 |
$errors = new \WP_Error(); |
| 518 |
$sites_processed = 0; |
| 519 |
$current_blog_id = get_current_blog_id(); |
| 520 |
|
| 521 |
foreach ( $site_ids as $site_id ) { |
| 522 |
$site_id = absint( $site_id ); |
| 523 |
$switched = $site_id !== $current_blog_id; |
| 524 |
if ( $switched ) { |
| 525 |
switch_to_blog( $site_id ); |
| 526 |
} |
| 527 |
|
| 528 |
try { |
| 529 |
$result = self::fix_crons_for_site(); |
| 530 |
if ( is_wp_error( $result ) ) { |
| 531 |
foreach ( $result->get_error_messages() as $message ) { |
| 532 |
$errors->add( |
| 533 |
'tptn-cron-site-' . $site_id, |
| 534 |
sprintf( |
| 535 |
/* translators: 1: Site ID, 2: Error message. */ |
| 536 |
__( 'Site %1$d: %2$s', 'top-10' ), |
| 537 |
$site_id, |
| 538 |
$message |
| 539 |
) |
| 540 |
); |
| 541 |
} |
| 542 |
} else { |
| 543 |
++$sites_processed; |
| 544 |
} |
| 545 |
} finally { |
| 546 |
if ( $switched ) { |
| 547 |
restore_current_blog(); |
| 548 |
} |
| 549 |
} |
| 550 |
} |
| 551 |
|
| 552 |
if ( $errors->has_errors() ) { |
| 553 |
return $errors; |
| 554 |
} |
| 555 |
|
| 556 |
return sprintf( |
| 557 |
/* translators: %s: Number of sites. */ |
| 558 |
__( 'Cron schedules have been repaired for %s active site(s).', 'top-10' ), |
| 559 |
number_format_i18n( $sites_processed ) |
| 560 |
); |
| 561 |
} |
| 562 |
|
| 563 |
return self::fix_crons_for_site(); |
| 564 |
} |
| 565 |
|
| 566 |
/** |
| 567 |
* Clear and reschedule the Top 10 cron jobs for the current site. |
| 568 |
* |
| 569 |
* @since 4.5.0 |
| 570 |
* |
| 571 |
* @return string|\WP_Error Success message or WP_Error if a job could not be rescheduled. |
| 572 |
*/ |
| 573 |
private static function fix_crons_for_site() { |
| 574 |
$errors = new \WP_Error(); |
| 575 |
$messages = array(); |
| 576 |
$success = true; |
| 577 |
|
| 578 |
if ( self::clear_scheduled_hook_or_error( 'tptn_cron_hook', $errors ) ) { |
| 579 |
if ( tptn_get_option( 'cron_on' ) ) { |
| 580 |
Cron::enable_run( |
| 581 |
(int) tptn_get_option( 'cron_hour' ), |
| 582 |
(int) tptn_get_option( 'cron_min' ), |
| 583 |
tptn_get_option( 'cron_recurrence' ) |
| 584 |
); |
| 585 |
if ( wp_next_scheduled( 'tptn_cron_hook' ) ) { |
| 586 |
$messages[] = __( 'Maintenance cron has been rescheduled.', 'top-10' ); |
| 587 |
} else { |
| 588 |
$errors->add( 'tptn_cron_hook', __( 'The maintenance cron could not be rescheduled as the cron event list could not be saved.', 'top-10' ) ); |
| 589 |
$success = false; |
| 590 |
} |
| 591 |
} else { |
| 592 |
$messages[] = __( 'Maintenance cron is disabled in the settings and was left unscheduled.', 'top-10' ); |
| 593 |
} |
| 594 |
} else { |
| 595 |
$success = false; |
| 596 |
} |
| 597 |
|
| 598 |
if ( self::clear_scheduled_hook_or_error( 'tptn_aggregation_cron_hook', $errors ) ) { |
| 599 |
Cron::enable_aggregation_run(); |
| 600 |
if ( wp_next_scheduled( 'tptn_aggregation_cron_hook' ) ) { |
| 601 |
$messages[] = __( 'Aggregation cron has been rescheduled.', 'top-10' ); |
| 602 |
} else { |
| 603 |
$errors->add( 'tptn_aggregation_cron_hook', __( 'The aggregation cron could not be rescheduled as the cron event list could not be saved. Check for object cache or database issues.', 'top-10' ) ); |
| 604 |
$success = false; |
| 605 |
} |
| 606 |
} else { |
| 607 |
$success = false; |
| 608 |
} |
| 609 |
|
| 610 |
// Only drop the previously recorded WP-Cron errors once both jobs are confirmed rebuilt (or intentionally left off). |
| 611 |
if ( $success ) { |
| 612 |
Cron::clear_reschedule_errors(); |
| 613 |
} |
| 614 |
|
| 615 |
return $errors->has_errors() ? $errors : implode( ' ', $messages ); |
| 616 |
} |
| 617 |
|
| 618 |
/** |
| 619 |
* Clear a scheduled hook, surfacing any WP_Error from WordPress core instead of silently ignoring it. |
| 620 |
* |
| 621 |
* If the existing event cannot be cleared, the caller must not attempt to reschedule it: the stale |
| 622 |
* event would still satisfy wp_next_scheduled(), making a broken cron write look like a successful repair. |
| 623 |
* |
| 624 |
* @since 4.4.0 |
| 625 |
* |
| 626 |
* @param string $hook Hook name to clear. |
| 627 |
* @param \WP_Error $errors Error collector to append to on failure. |
| 628 |
* @return bool True if the hook was cleared (or had nothing scheduled), false on failure. |
| 629 |
*/ |
| 630 |
private static function clear_scheduled_hook_or_error( $hook, \WP_Error $errors ) { |
| 631 |
$result = wp_clear_scheduled_hook( $hook, array(), true ); |
| 632 |
|
| 633 |
if ( is_wp_error( $result ) ) { |
| 634 |
$errors->add( |
| 635 |
$hook, |
| 636 |
sprintf( |
| 637 |
/* translators: 1: Hook name, 2: Error message from WordPress core. */ |
| 638 |
__( 'Could not clear the existing schedule for %1$s: %2$s', 'top-10' ), |
| 639 |
$hook, |
| 640 |
$result->get_error_message() |
| 641 |
) |
| 642 |
); |
| 643 |
return false; |
| 644 |
} |
| 645 |
|
| 646 |
return true; |
| 647 |
} |
| 648 |
|
| 649 |
/** |
| 650 |
* Generates the Tools help page. |
| 651 |
* |
| 652 |
* @since 3.3.0 |
| 653 |
*/ |
| 654 |
public static function help_tabs() { |
| 655 |
$screen = get_current_screen(); |
| 656 |
|
| 657 |
$screen->set_help_sidebar( |
| 658 |
/* translators: 1: Support link. */ |
| 659 |
'<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>' . |
| 660 |
/* translators: 1: Forum link. */ |
| 661 |
'<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>' . |
| 662 |
'<p>' . sprintf( |
| 663 |
/* translators: 1: Github Issues link, 2: Github page. */ |
| 664 |
__( '<a href="%1$s">Post an issue</a> on <a href="%2$s">GitHub</a> (bug reports only).', 'top-10' ), |
| 665 |
esc_url( 'https://github.com/WebberZone/top-10/issues' ), |
| 666 |
esc_url( 'https://github.com/WebberZone/top-10' ) |
| 667 |
) . '</p>' |
| 668 |
); |
| 669 |
|
| 670 |
$screen->add_help_tab( |
| 671 |
array( |
| 672 |
'id' => 'tptn-settings-general', |
| 673 |
'title' => __( 'General', 'top-10' ), |
| 674 |
'content' => |
| 675 |
'<p>' . __( 'This screen provides some tools that help maintain certain features of Top 10.', 'top-10' ) . '</p>' . |
| 676 |
'<p>' . __( 'Clear the cache, reset the popular posts tables plus some miscellaneous fixes for older versions of Top 10.', 'top-10' ) . '</p>', |
| 677 |
) |
| 678 |
); |
| 679 |
|
| 680 |
/** |
| 681 |
* Fires after the Tools page help tabs have been registered. |
| 682 |
* |
| 683 |
* @since 2.5.0 |
| 684 |
* |
| 685 |
* @param \WP_Screen $screen The current screen object. |
| 686 |
*/ |
| 687 |
do_action( 'tptn_settings_tools_help', $screen ); |
| 688 |
} |
| 689 |
|
| 690 |
/** |
| 691 |
* Check if tables exist and create them if they don't. |
| 692 |
* |
| 693 |
* @since 4.2.0 |
| 694 |
* |
| 695 |
* @return array Array of table statuses indicating whether they are installed. |
| 696 |
*/ |
| 697 |
public static function check_table_status() { |
| 698 |
$tables = array( |
| 699 |
'top_ten' => Database::get_table( false ), |
| 700 |
'top_ten_daily' => Database::get_table( true ), |
| 701 |
'top_ten_visits_funnel' => Database::get_funnel_table(), |
| 702 |
'top_ten_visits_log' => Database::get_log_table(), |
| 703 |
); |
| 704 |
|
| 705 |
$statuses = array(); |
| 706 |
|
| 707 |
$installed_label = '<span style="color: #006400;">' . __( 'Installed', 'top-10' ) . '</span>'; |
| 708 |
$not_installed_label = '<span style="color: #8B0000;">' . __( 'Not Installed', 'top-10' ) . '</span>'; |
| 709 |
$table_statuses = Database::get_table_installation_status(); |
| 710 |
|
| 711 |
foreach ( $tables as $key => $table_name ) { |
| 712 |
$statuses[ $key ] = ! empty( $table_statuses[ $table_name ] ) ? $installed_label : $not_installed_label; |
| 713 |
} |
| 714 |
|
| 715 |
// Create tables if they don't exist. |
| 716 |
if ( empty( $table_statuses[ Database::get_table( false ) ] ) || empty( $table_statuses[ Database::get_table( true ) ] ) ) { |
| 717 |
// Use Activator to create tables. |
| 718 |
Activator::create_tables(); |
| 719 |
Database::clear_table_statistics_cache(); |
| 720 |
$table_statuses = Database::get_table_installation_status( true ); |
| 721 |
|
| 722 |
// Refresh statuses after creating tables. |
| 723 |
foreach ( $tables as $key => $table_name ) { |
| 724 |
$statuses[ $key ] = ! empty( $table_statuses[ $table_name ] ) ? $installed_label : $not_installed_label; |
| 725 |
} |
| 726 |
} |
| 727 |
|
| 728 |
/** |
| 729 |
* Filter the table statuses report. |
| 730 |
* |
| 731 |
* @since 4.1.0 |
| 732 |
* |
| 733 |
* @param array $statuses Array of table statuses. |
| 734 |
*/ |
| 735 |
return apply_filters( 'tptn_table_statuses', $statuses ); |
| 736 |
} |
| 737 |
|
| 738 |
/** |
| 739 |
* Get database status report. |
| 740 |
* |
| 741 |
* @since 4.2.0 |
| 742 |
* |
| 743 |
* @return string HTML output for the database status report. |
| 744 |
*/ |
| 745 |
public static function get_db_status_report() { |
| 746 |
global $tptn_db_version; |
| 747 |
|
| 748 |
// Get table statuses. |
| 749 |
$statuses = self::check_table_status(); |
| 750 |
|
| 751 |
// Get table statistics from Database class. |
| 752 |
$table_stats = Database::get_table_statistics(); |
| 753 |
|
| 754 |
ob_start(); |
| 755 |
?> |
| 756 |
<table class="form-table"> |
| 757 |
<tr> |
| 758 |
<th scope="row"><?php esc_html_e( 'Database version', 'top-10' ); ?></th> |
| 759 |
<td> |
| 760 |
<?php esc_html_e( 'Installed version', 'top-10' ); ?> <?php echo esc_html( get_site_option( 'tptn_db_version', '0' ) ); ?> / |
| 761 |
<?php esc_html_e( 'Current version', 'top-10' ); ?> <?php echo esc_html( $tptn_db_version ); ?> |
| 762 |
</td> |
| 763 |
</tr> |
| 764 |
|
| 765 |
<?php |
| 766 |
$table_keys = array( |
| 767 |
'top_ten', |
| 768 |
'top_ten_daily', |
| 769 |
'top_ten_visits_funnel', |
| 770 |
'top_ten_visits_log', |
| 771 |
); |
| 772 |
foreach ( $table_keys as $table_key ) : |
| 773 |
if ( ! isset( $statuses[ $table_key ] ) ) { |
| 774 |
continue; |
| 775 |
} |
| 776 |
?> |
| 777 |
<tr> |
| 778 |
<th scope="row"><?php printf( /* translators: %s: Table name */ esc_html__( '%s table', 'top-10' ), esc_html( $table_key ) ); ?></th> |
| 779 |
<td> |
| 780 |
<?php |
| 781 |
echo $statuses[ $table_key ]; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped |
| 782 |
|
| 783 |
if ( isset( $table_stats[ $table_key ] ) ) { |
| 784 |
$format = is_multisite() |
| 785 |
? __( 'Network-wide estimated entries: %1$s | Network-wide estimated size: %2$s', 'top-10' ) |
| 786 |
: __( 'Estimated entries: %1$s | Estimated size: %2$s', 'top-10' ); |
| 787 |
|
| 788 |
echo '<br><span class="description">'; |
| 789 |
printf( |
| 790 |
esc_html( $format ), |
| 791 |
'<strong>' . esc_html( number_format_i18n( $table_stats[ $table_key ]['entries'] ) ) . '</strong>', |
| 792 |
'<strong>' . esc_html( size_format( $table_stats[ $table_key ]['size'] ) ) . '</strong>' |
| 793 |
); |
| 794 |
echo '</span>'; |
| 795 |
} |
| 796 |
?> |
| 797 |
</td> |
| 798 |
</tr> |
| 799 |
<?php endforeach; ?> |
| 800 |
|
| 801 |
<?php |
| 802 |
$cron_jobs = array( |
| 803 |
'tptn_cron_hook' => __( 'Maintenance cron', 'top-10' ), |
| 804 |
'tptn_aggregation_cron_hook' => __( 'Aggregation cron', 'top-10' ), |
| 805 |
); |
| 806 |
foreach ( $cron_jobs as $hook => $label ) : |
| 807 |
$next_run = wp_next_scheduled( $hook ); |
| 808 |
$recurrence = $next_run ? wp_get_schedule( $hook ) : false; |
| 809 |
$schedules = wp_get_schedules(); |
| 810 |
$interval_display = ''; |
| 811 |
if ( $recurrence && isset( $schedules[ $recurrence ]['display'] ) ) { |
| 812 |
$interval_display = $schedules[ $recurrence ]['display']; |
| 813 |
} |
| 814 |
?> |
| 815 |
<tr> |
| 816 |
<th scope="row"><?php echo esc_html( $label ); ?></th> |
| 817 |
<td> |
| 818 |
<?php if ( $next_run ) : ?> |
| 819 |
<span style="color: #006400;"><?php esc_html_e( 'Scheduled', 'top-10' ); ?></span> |
| 820 |
<br><span class="description"> |
| 821 |
<?php |
| 822 |
if ( $interval_display ) { |
| 823 |
printf( |
| 824 |
/* translators: %s: schedule display name e.g. "Every two minutes" */ |
| 825 |
esc_html__( 'Runs: %s', 'top-10' ), |
| 826 |
esc_html( $interval_display ) |
| 827 |
); |
| 828 |
echo '<br>'; |
| 829 |
} |
| 830 |
printf( |
| 831 |
/* translators: %s: human-readable time difference */ |
| 832 |
esc_html__( 'Next run: %s', 'top-10' ), |
| 833 |
/* translators: %s: human-readable time difference */ |
| 834 |
esc_html( sprintf( __( 'in %s', 'top-10' ), human_time_diff( time(), $next_run ) ) ) |
| 835 |
); |
| 836 |
?> |
| 837 |
</span> |
| 838 |
<?php else : ?> |
| 839 |
<span style="color: #8B0000;"><?php esc_html_e( 'Not scheduled', 'top-10' ); ?></span> |
| 840 |
<?php endif; ?> |
| 841 |
<?php |
| 842 |
$reschedule_error = Cron::get_reschedule_error( $hook ); |
| 843 |
if ( $reschedule_error ) : |
| 844 |
?> |
| 845 |
<br><span class="description" style="color: #8B0000;"> |
| 846 |
<?php |
| 847 |
printf( |
| 848 |
/* translators: 1: Error message from WP-Cron, 2: Human-readable time difference. */ |
| 849 |
esc_html__( 'WP-Cron reported an error rescheduling this job %2$s ago: %1$s', 'top-10' ), |
| 850 |
esc_html( $reschedule_error['message'] ), |
| 851 |
esc_html( human_time_diff( $reschedule_error['time'] ) ) |
| 852 |
); |
| 853 |
?> |
| 854 |
</span> |
| 855 |
<?php endif; ?> |
| 856 |
</td> |
| 857 |
</tr> |
| 858 |
<?php endforeach; ?> |
| 859 |
|
| 860 |
<?php if ( ( ! is_multisite() || is_network_admin() ) && ! Database::are_tables_installed() ) : ?> |
| 861 |
<tr> |
| 862 |
<th scope="row"><?php esc_html_e( 'Repair database', 'top-10' ); ?></th> |
| 863 |
<td> |
| 864 |
<a href="<?php echo esc_url( wp_nonce_url( ( is_multisite() && is_network_admin() ? network_admin_url( 'admin.php?page=tptn_network_tools_page&action=recreate_tables' ) : admin_url( 'admin.php?page=tptn_tools_page&action=recreate_tables' ) ), 'tptn-recreate-tables' ) ); ?>" class="button"> |
| 865 |
<?php esc_html_e( 'Recreate tables', 'top-10' ); ?> |
| 866 |
</a> |
| 867 |
</td> |
| 868 |
</tr> |
| 869 |
<?php endif; ?> |
| 870 |
</table> |
| 871 |
<?php |
| 872 |
|
| 873 |
return ob_get_clean(); |
| 874 |
} |
| 875 |
|
| 876 |
/** |
| 877 |
* Handle recreate tables action from admin area. |
| 878 |
* |
| 879 |
* @since 4.2.0 |
| 880 |
*/ |
| 881 |
/** |
| 882 |
* Handle the create-missing-tables GET action triggered from the admin notice. |
| 883 |
* |
| 884 |
* @since 4.3.0 |
| 885 |
*/ |
| 886 |
public static function handle_create_missing_tables_action() { |
| 887 |
if ( ! isset( $_GET['action'] ) || 'tptn_create_missing_tables' !== $_GET['action'] || ! isset( $_GET['_wpnonce'] ) ) { |
| 888 |
return; |
| 889 |
} |
| 890 |
|
| 891 |
if ( ! wp_verify_nonce( sanitize_key( $_GET['_wpnonce'] ), 'tptn-create-missing-tables' ) ) { |
| 892 |
wp_die( esc_html__( 'Security check failed', 'top-10' ) ); |
| 893 |
} |
| 894 |
|
| 895 |
if ( is_multisite() && ! is_network_admin() ) { |
| 896 |
wp_die( esc_html__( 'Database table repair must be run from Network Admin.', 'top-10' ) ); |
| 897 |
} |
| 898 |
|
| 899 |
$capability = is_network_admin() ? 'manage_network_options' : 'manage_options'; |
| 900 |
if ( ! current_user_can( $capability ) ) { |
| 901 |
wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'top-10' ) ); |
| 902 |
} |
| 903 |
|
| 904 |
Activator::create_tables(); |
| 905 |
Database::clear_table_statistics_cache(); |
| 906 |
Dashboard_Widgets::clear_network_dashboard_cache(); |
| 907 |
$table_statuses = Database::get_table_installation_status( true ); |
| 908 |
|
| 909 |
$still_missing = false; |
| 910 |
foreach ( $table_statuses as $installed ) { |
| 911 |
if ( ! $installed ) { |
| 912 |
$still_missing = true; |
| 913 |
break; |
| 914 |
} |
| 915 |
} |
| 916 |
|
| 917 |
$status = $still_missing ? 'failed' : 'created'; |
| 918 |
$referer = wp_get_referer(); |
| 919 |
$fallback = is_network_admin() ? network_admin_url( 'admin.php?page=tptn_network_tools_page' ) : admin_url( 'admin.php?page=tptn_tools_page' ); |
| 920 |
$redirect = add_query_arg( 'tptn_tables_created', $status, $referer ? $referer : $fallback ); |
| 921 |
|
| 922 |
wp_safe_redirect( $redirect ); |
| 923 |
exit; |
| 924 |
} |
| 925 |
|
| 926 |
/** |
| 927 |
* Handle the recreate-tables GET action from the tools page. |
| 928 |
* |
| 929 |
* @since 4.1.0 |
| 930 |
*/ |
| 931 |
public static function handle_recreate_tables_action() { |
| 932 |
if ( ! isset( $_GET['action'] ) || 'recreate_tables' !== $_GET['action'] || ! isset( $_GET['_wpnonce'] ) ) { |
| 933 |
return; |
| 934 |
} |
| 935 |
|
| 936 |
if ( ! wp_verify_nonce( sanitize_key( $_GET['_wpnonce'] ), 'tptn-recreate-tables' ) ) { |
| 937 |
wp_die( esc_html__( 'Security check failed', 'top-10' ) ); |
| 938 |
} |
| 939 |
|
| 940 |
if ( is_multisite() && ! is_network_admin() ) { |
| 941 |
wp_die( esc_html__( 'Database table repair must be run from Network Admin.', 'top-10' ) ); |
| 942 |
} |
| 943 |
|
| 944 |
$capability = is_network_admin() ? 'manage_network_options' : 'manage_options'; |
| 945 |
if ( ! current_user_can( $capability ) ) { |
| 946 |
wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'top-10' ) ); |
| 947 |
} |
| 948 |
|
| 949 |
// Recreate tables. |
| 950 |
$result_overall = Database::recreate_overall_table( false ); |
| 951 |
$result_daily = Database::recreate_daily_table( false ); |
| 952 |
$result_funnel = Database::recreate_funnel_table( false ); |
| 953 |
$result_log = Database::recreate_log_table( false ); |
| 954 |
Database::clear_table_installation_cache(); |
| 955 |
Dashboard_Widgets::clear_network_dashboard_cache(); |
| 956 |
|
| 957 |
// Check for errors. |
| 958 |
if ( is_wp_error( $result_overall ) ) { |
| 959 |
add_settings_error( |
| 960 |
'tptn-notices', |
| 961 |
'tptn-recreate-overall-error', |
| 962 |
$result_overall->get_error_message(), |
| 963 |
'error' |
| 964 |
); |
| 965 |
} |
| 966 |
|
| 967 |
if ( is_wp_error( $result_daily ) ) { |
| 968 |
add_settings_error( |
| 969 |
'tptn-notices', |
| 970 |
'tptn-recreate-daily-error', |
| 971 |
$result_daily->get_error_message(), |
| 972 |
'error' |
| 973 |
); |
| 974 |
} |
| 975 |
|
| 976 |
if ( is_wp_error( $result_funnel ) ) { |
| 977 |
add_settings_error( |
| 978 |
'tptn-notices', |
| 979 |
'tptn-recreate-funnel-error', |
| 980 |
$result_funnel->get_error_message(), |
| 981 |
'error' |
| 982 |
); |
| 983 |
} |
| 984 |
|
| 985 |
if ( is_wp_error( $result_log ) ) { |
| 986 |
add_settings_error( |
| 987 |
'tptn-notices', |
| 988 |
'tptn-recreate-log-error', |
| 989 |
$result_log->get_error_message(), |
| 990 |
'error' |
| 991 |
); |
| 992 |
} |
| 993 |
|
| 994 |
// If no errors, add success message. |
| 995 |
if ( ! is_wp_error( $result_overall ) && ! is_wp_error( $result_daily ) && ! is_wp_error( $result_funnel ) && ! is_wp_error( $result_log ) ) { |
| 996 |
add_settings_error( |
| 997 |
'tptn-notices', |
| 998 |
'tptn-recreate-success', |
| 999 |
__( 'Tables have been recreated successfully.', 'top-10' ), |
| 1000 |
'success' |
| 1001 |
); |
| 1002 |
|
| 1003 |
// Clear table statistics cache since tables were recreated. |
| 1004 |
Database::clear_table_statistics_cache(); |
| 1005 |
} |
| 1006 |
|
| 1007 |
// Redirect back to the tools page. |
| 1008 |
if ( is_network_admin() ) { |
| 1009 |
wp_safe_redirect( network_admin_url( 'admin.php?page=tptn_network_tools_page&settings-updated=true' ) ); |
| 1010 |
} else { |
| 1011 |
wp_safe_redirect( admin_url( 'admin.php?page=tptn_tools_page&settings-updated=true' ) ); |
| 1012 |
} |
| 1013 |
exit; |
| 1014 |
} |
| 1015 |
} |
| 1016 |
|