PluginProbe ʕ •ᴥ•ʔ
Advanced Database Cleaner – Optimize & Clean Database to Speed Up Site Performance / trunk
Advanced Database Cleaner – Optimize & Clean Database to Speed Up Site Performance vtrunk
4.2.0 trunk 1.0.0 1.1.0 1.1.1 1.2.0 1.2.1 1.2.2 1.2.3 1.3.0 1.3.1 1.3.5 1.3.6 1.3.7 2.0.0 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.1.0 3.1.1 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.0.5 4.0.6 4.0.7 4.1.0 4.1.1
advanced-database-cleaner / includes / classes / class-adbc-admin-init.php
advanced-database-cleaner / includes / classes Last commit date
addons 6 days ago general-cleanup 6 days ago class-adbc-admin-init.php 6 days ago class-adbc-automation.php 6 days ago class-adbc-hardcoded-items.php 6 days ago class-adbc-migration.php 6 days ago class-adbc-routes.php 6 days ago class-adbc-scan-counter.php 7 months ago class-adbc-settings.php 6 days ago class-adbc-singleton.php 7 months ago
class-adbc-admin-init.php
882 lines
1 <?php
2
3 // Exit if accessed directly
4 if ( ! defined( 'ABSPATH' ) )
5 exit;
6
7 /**
8 * ADBC admin init class.
9 *
10 * This class adds the menu and submenu pages, and enqueuing the scripts and styles.
11 */
12 class ADBC_Admin_Init extends ADBC_Singleton {
13
14 /**
15 * Holds the menu hook for the main left sidebar admin menu item.
16 */
17 private $left_menu;
18
19 /**
20 * Holds the menu hook for the submenu item under Tools.
21 */
22 private $tools_submenu;
23
24 /**
25 * Holds the menu hook for the network admin menu item.
26 */
27 private $network_menu;
28
29 /**
30 * Holds the svg icon for the menu item.
31 */
32 private $icon_svg = "";
33
34 /**
35 * Option key used to persist conflict notices across redirects/requests.
36 *
37 * @var string
38 */
39 private static $conflict_notice_option_key = 'adbc_conflict_notice';
40
41 /**
42 * Store original plugin links before other plugins modify them
43 */
44 private $original_plugin_meta_links = [];
45
46 /**
47 * Constructor.
48 */
49 protected function __construct() {
50
51 parent::__construct();
52
53 $this->icon_svg = 'data:image/svg+xml;base64,' . base64_encode( '<svg width="20" height="20" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path fill="#a0a5aa" d="M896 768q237 0 443-43t325-127v170q0 69-103 128t-280 93.5-385 34.5-385-34.5-280-93.5-103-128v-170q119 84 325 127t443 43zm0 768q237 0 443-43t325-127v170q0 69-103 128t-280 93.5-385 34.5-385-34.5-280-93.5-103-128v-170q119 84 325 127t443 43zm0-384q237 0 443-43t325-127v170q0 69-103 128t-280 93.5-385 34.5-385-34.5-280-93.5-103-128v-170q119 84 325 127t443 43zm0-1152q208 0 385 34.5t280 93.5 103 128v128q0 69-103 128t-280 93.5-385 34.5-385-34.5-280-93.5-103-128v-128q0-69 103-128t280-93.5 385-34.5z"/></svg>' );
54 }
55
56 /**
57 * Add the menu and submenu pages.
58 *
59 * @return void
60 */
61 public function add_admin_menu() {
62
63 $left_menu = ADBC_Settings::instance()->get_setting( 'left_menu' );
64 $tools_menu = ADBC_Settings::instance()->get_setting( 'tools_menu' );
65
66 if ( $left_menu === '1' ) {
67 $this->left_menu = add_menu_page( 'Advanced DB Cleaner', 'DB Cleaner', 'manage_options', 'advanced_db_cleaner', [ $this, 'main_page_callback' ], $this->icon_svg, '80.01123' );
68 }
69
70 if ( $tools_menu === '1' ) {
71 $this->tools_submenu = add_submenu_page( 'tools.php', 'Advanced DB Cleaner', 'DB Cleaner', 'manage_options', 'advanced_db_cleaner', [ $this, 'main_page_callback' ], '80.01123' );
72 }
73 }
74
75 /**
76 * Add the network admin menu page.
77 *
78 * @return void
79 */
80 public function add_network_admin_menu() {
81
82 $network_menu = ADBC_Settings::instance()->get_setting( 'network_menu' );
83
84 if ( $network_menu === '1' ) {
85 $this->network_menu = add_menu_page( 'Advanced DB Cleaner', 'DB Cleaner', 'manage_network_options', 'advanced_db_cleaner_network', [ $this, 'main_page_callback' ], $this->icon_svg, '80.01123' );
86 }
87
88 }
89
90 /**
91 * Callback for the main page.
92 *
93 * @return void
94 */
95 public function main_page_callback() {
96
97 echo "<div id='adbc-plugin-root'></div>";
98 }
99
100 /**
101 * Enqueue the scripts and styles.
102 *
103 * @param string $hook The current admin page hook.
104 * @return void
105 */
106 public function enqueue_scripts( $hook ) {
107
108 // Enqueue global scripts for global notifications such as the rating notice.
109 $this->enqueue_global_scripts();
110
111 // Load plugins scripts only in the plugin page.
112 if ( $hook != $this->left_menu && $hook != $this->tools_submenu && $hook != $this->network_menu )
113 return;
114
115 // Enqueue the scripts and styles.
116 wp_enqueue_style( 'adbc-app-style', ADBC_PLUGIN_ABSOLUTE_URL . '/assets/css/app.css', [], ADBC_PLUGIN_VERSION );
117 wp_enqueue_script( 'adbc-vendors-script', ADBC_PLUGIN_ABSOLUTE_URL . '/assets/js/vendors.js', [], ADBC_PLUGIN_VERSION, true );
118 wp_enqueue_script( 'adbc-app-script', ADBC_PLUGIN_ABSOLUTE_URL . '/assets/js/app.js', [ 'adbc-vendors-script', 'wp-i18n', 'wp-date' ], ADBC_PLUGIN_VERSION, true );
119
120 // Load the translations for the app script.
121 wp_set_script_translations( 'adbc-app-script', 'advanced-database-cleaner', ADBC_PLUGIN_DIR_PATH . 'languages' );
122
123 // Localize right after enqueuing.
124 wp_localize_script(
125 'adbc-app-script',
126 'adbc_app_settings',
127 array(
128 'rest_url' => esc_url_raw( rest_url( ADBC_REST_API_NAMESPACE ) ),
129 'nonce' => wp_create_nonce( 'wp_rest' ),
130 'version' => ADBC_PLUGIN_VERSION,
131 'version_type' => ADBC_VERSION_TYPE,
132 'is_pro_version' => ADBC_IS_PRO_VERSION ? '1' : '0',
133 'settings' => ADBC_Settings::instance()->get_settings( false ), // Send none sensitive settings.
134 'license_data' => ADBC_VERSION_TYPE === "PREMIUM" ? ADBC_License_Manager::get_license_data() : [], // Send masked license key.
135 'warnings' => ADBC_Notifications::instance()->get_warnings(),
136 'notifications' => ADBC_Notifications::instance()->get_local_notifications(),
137 'is_multisite' => is_multisite() ? '1' : '0',
138 'sites_list' => ADBC_Sites::instance()->get_sites_list(),
139 'actionscheduler_actions_exists' => ADBC_Tables::is_actionscheduler_table_exists( 'actions' ) ? '1' : '0',
140 'actionscheduler_logs_exists' => ADBC_Tables::is_actionscheduler_table_exists( 'logs' ) ? '1' : '0',
141 'php_max_execution_time' => ( $value = (int) ini_get( 'max_execution_time' ) ) > 0 ? $value : 120,
142 'is_woptimize_installed' => self::is_woptimize_installed() ? '1' : '0',
143 )
144 );
145
146 }
147
148 /**
149 * Enqueue global scripts for ADBC notifications.
150 * This runs on all admin pages, not just the plugin pages.
151 *
152 * @return void
153 */
154 private function enqueue_global_scripts() {
155
156 // Only load on admin pages and for users with manage_options capability.
157 if ( ! is_admin() || ! current_user_can( 'manage_options' ) )
158 return;
159
160 // Check if we have any global notifications to show
161 $global_notifications = ADBC_Notifications::instance()->get_global_notifications();
162
163 if ( empty( $global_notifications ) )
164 return;
165
166 $has_rating = isset( $global_notifications['rating_notice'] );
167 $has_ltd = isset( $global_notifications['ltd_migration_notice'] );
168
169 // Only enqueue shared assets if at least one of our custom global notices is active.
170 if ( ! $has_rating && ! $has_ltd )
171 return;
172
173 // Single combined CSS/JS for all global notices (rating + LTD migration).
174 wp_enqueue_style(
175 'adbc-global-style',
176 ADBC_PLUGIN_ABSOLUTE_URL . '/assets/css/global-style.css',
177 array(),
178 ADBC_PLUGIN_VERSION
179 );
180
181 wp_enqueue_script(
182 'adbc-global-script',
183 ADBC_PLUGIN_ABSOLUTE_URL . '/assets/js/global-js.js',
184 array( 'jquery' ),
185 ADBC_PLUGIN_VERSION,
186 true
187 );
188
189 // Single localized object shared by all notice JS.
190 wp_localize_script(
191 'adbc-global-script',
192 'adbc_global_settings',
193 array(
194 'rest_url' => esc_url_raw( rest_url( ADBC_REST_API_NAMESPACE ) ),
195 'nonce' => wp_create_nonce( 'wp_rest' ),
196 )
197 );
198 }
199
200 /**
201 * Prioritize plugin's translation files over global ones.
202 *
203 * This ensures that translations in the plugin's languages folder
204 * take precedence over those in wp-content/languages/plugins/
205 *
206 * @param string $mofile Path to the MO file.
207 * @param string $domain Text domain.
208 * @return string Modified path to the MO file.
209 */
210 public static function prioritize_plugin_translations( $mofile, $domain ) {
211
212 // Only apply to our plugin's text domain
213 if ( 'advanced-database-cleaner' !== $domain ) {
214 return $mofile;
215 }
216
217 // Get the current locale
218 $locale = apply_filters( 'plugin_locale', determine_locale(), $domain );
219
220 // Build the path to our plugin's translation file
221 $plugin_mofile = ADBC_PLUGIN_DIR_PATH . 'languages/' . $domain . '-' . $locale . '.mo';
222
223 // If our plugin's translation file exists, use it instead
224 if ( file_exists( $plugin_mofile ) ) {
225 return $plugin_mofile;
226 }
227
228 // Fallback to the original file
229 return $mofile;
230
231 }
232
233 /**
234 * Change the script translation file name to load the correct file.
235 *
236 * This is necessary because WordPress generates the MD5 hash based on the script file name,
237 * and we want to ensure that our translations are loaded from the correct file.
238 * The MD5 of the translation file built by WP will use assets/js/translations.js instead of assets/js/app.js
239 *
240 * @param string $file The path to the translation file.
241 * @param string $handle The script handle.
242 * @param string $domain The text domain.
243 * @return string The modified path to the translation file.
244 */
245 public static function change_script_translation_file_name( $file, $handle, $domain ) {
246
247 if ( $domain !== 'advanced-database-cleaner' ) {
248 return $file;
249 }
250
251 $file = str_replace( md5( 'assets/js/app.js' ), md5( 'assets/js/translations.js' ), $file );
252
253 return $file;
254
255 }
256
257 /**
258 * What happens upon plugin activation.
259 *
260 * @return void
261 */
262 public static function activate() {
263 // Nothing to do here for now
264 }
265
266 /**
267 * What happens upon plugin deactivation.
268 *
269 * @return void
270 */
271 public static function deactivate() {
272
273 // unschedule analytics cron
274 wp_clear_scheduled_hook( 'adbc_cron_analytics' );
275
276 // Deactivate all automation tasks
277 ADBC_Automation::instance()->deactivate_all_tasks();
278
279 }
280
281 /**
282 * Generate ADBC uploads folder if missing.
283 *
284 * @return void
285 */
286 public static function create_adbc_uploads_folder_with_its_content() {
287
288 // Check if the WP filesystem is initialized. If it fails, no need to continue.
289 if ( ! ADBC_Files::instance()->is_wp_fs_initialized() )
290 return;
291
292 // Try to create the ADBC uploads folder. If it fails, no need to continue.
293 if ( ! self::create_adbc_uploads_folder() )
294 return;
295
296 // Create files and folders inside the ADBC uploads folder.
297 ADBC_Files::instance()->create_file( ADBC_Logging::DEBUG_LOG_FILE_PATH );
298
299 // Create the scan folder.
300 if ( ADBC_VERSION_TYPE === "PREMIUM" ) {
301 ADBC_Files::instance()->create_folder( ADBC_Scan_Paths::SCAN_FOLDER_PATH, true );
302 ADBC_Files::instance()->create_folder( ADBC_Analytics::ADBC_ANALYTICS_DIR, true );
303 ADBC_Files::instance()->create_folder( ADBC_Analytics::ADBC_ANALYTICS_DATABASE_DIR, true );
304 ADBC_Files::instance()->create_folder( ADBC_Analytics::ADBC_ANALYTICS_TABLES_DIR, true );
305 ADBC_Files::instance()->create_folder( ADBC_Automation::AUTOMATION_EVENT_DIR, true );
306 ADBC_Files::instance()->create_file( ADBC_Addons_Activity::ADDONS_ACTIVITY_LOG_FILE_PATH );
307 ADBC_Files::instance()->create_file( ADBC_Addons_Activity::ADDONS_ACTIVITY_DICTIONARY );
308 ADBC_Files::instance()->create_file( ADBC_Registered_Post_Types_Dict_Tracker::DICT_FILE_PATH );
309 ADBC_Files::instance()->create_file( ADBC_Scan_Paths::get_custom_addons_dictionary_file_path() );
310 }
311
312 }
313
314 /**
315 * Setup the ADBC uploads folder (with its content).
316 *
317 * @return boolean True if successful, false otherwise.
318 */
319 private static function create_adbc_uploads_folder() {
320
321 // If the ADBC uploads folder doesn't exist, delete all folders starting with the ADBC uploads folder prefix before creating a new one.
322 if ( ! ADBC_Files::instance()->is_dir( ADBC_UPLOADS_DIR_PATH ) ) {
323
324 $files = glob( ADBC_WP_UPLOADS_DIR_PATH . '/' . ADBC_UPLOADS_DIR_PREFIX . '*' );
325 $adbc_folder_length = strlen( ADBC_UPLOADS_DIR_PREFIX ) + ADBC_SECURITY_CODE_LENGTH;
326
327 foreach ( $files as $file ) {
328 $folder_name = basename( $file );
329 // Delete if the folder name = $adbc_folder_length and composed only of alphanumeric, underscores and F.
330 if ( strlen( $folder_name ) === $adbc_folder_length && preg_match( '/^[a-z0-9_F]+$/', $folder_name ) )
331 ADBC_Files::instance()->delete_folder( $file );
332 }
333 }
334
335 // Create the new ADBC uploads folder if it doesn't exist.
336 ADBC_Files::instance()->create_folder( ADBC_UPLOADS_DIR_PATH, true );
337
338 // At this point the ADBC uploads folder should exist. Verify if it's readable and writable.
339 if ( ADBC_Files::instance()->is_readable_and_writable( ADBC_UPLOADS_DIR_PATH ) ) {
340 ADBC_Notifications::instance()->delete_notification( "uploads_folder" ); // Clear the warning form the DB if it exists.
341 } else {
342 ADBC_Notifications::instance()->add_notification( 'uploads_folder' );
343 return false;
344 }
345
346 return true;
347 }
348
349 /**
350 * Ensure the integrity of automation tasks.
351 *
352 * This method checks all automation tasks, validates their structure, and ensures they are scheduled or unscheduled as needed.
353 * It also cleans up any orphaned cron hooks that no longer have a corresponding task with their events files.
354 */
355 public static function ensure_automation_integrity() {
356
357 $tasks = ADBC_Automation::instance()->tasks();
358 $cron_ids = ADBC_Automation::get_scheduled_automation_crons_ids();
359
360 foreach ( $tasks as $id => $task ) {
361
362 $valid = ADBC_Automation_Validator::validate_task_structure( $task );
363
364 // task invalid → remove unschedule & cleanup
365 if ( $valid === false ) {
366 ADBC_Automation::instance()->delete( $id );
367 continue;
368 }
369
370 $is_active = (bool) $task['active'];
371 $is_scheduled = in_array( $id, $cron_ids, true );
372
373 // valid & inactive → ensure NOT scheduled
374 if ( ! $is_active && $is_scheduled ) {
375 ADBC_Automation::instance()->unschedule( $id ); // unschedule the task
376 // keep events file (user may re-enable later)
377 }
378
379 // valid & active → ensure scheduled
380 if ( $is_active && ! $is_scheduled ) {
381 ADBC_Automation::instance()->schedule( $task ); // schedule the task
382 }
383
384 }
385
386 // orphan cron hooks (hook exists but task record gone)
387 foreach ( $cron_ids as $id ) {
388 if ( ! isset( $tasks[ $id ] ) ) {
389 // The task is not found in the tasks array, so we can unschedule it and delete its events file.
390 ADBC_Automation::instance()->unschedule( $id );
391 ADBC_Automation::delete_events_file( $id );
392 }
393 }
394
395 }
396
397 /**
398 * Register the cron_schedules filter to add ADBC custom schedules.
399 *
400 * @return void
401 */
402 public static function register_cron_schedules_filter() {
403 add_filter( 'cron_schedules', [ self::class, 'add_adbc_schedules_frequencies' ] );
404 }
405
406 /**
407 * Add custom schedules for ADBC cron jobs.
408 *
409 * @param array $schedules The existing schedules.
410 *
411 * @return array The modified schedules with ADBC custom frequencies added.
412 */
413 public static function add_adbc_schedules_frequencies( $schedules ) {
414
415 // Add adbc_hourly schedule (1 hour)
416 $schedules['adbc_hourly'] = array(
417 'interval' => HOUR_IN_SECONDS,
418 'display' => __( 'Once hourly', 'advanced-database-cleaner' )
419 );
420
421 // Add adbc_twicedaily schedule (12 hours)
422 $schedules['adbc_twicedaily'] = array(
423 'interval' => 12 * HOUR_IN_SECONDS,
424 'display' => __( 'Twice daily', 'advanced-database-cleaner' )
425 );
426
427 // Add adbc_daily schedule (1 day)
428 $schedules['adbc_daily'] = array(
429 'interval' => DAY_IN_SECONDS,
430 'display' => __( 'Once daily', 'advanced-database-cleaner' )
431 );
432
433 // Add adbc_weekly schedule (1 week)
434 $schedules['adbc_weekly'] = array(
435 'interval' => WEEK_IN_SECONDS,
436 'display' => __( 'Once weekly', 'advanced-database-cleaner' )
437 );
438
439 // Add adbc_monthly schedule (approx. 1 month)
440 $schedules['adbc_monthly'] = array(
441 'interval' => 30 * DAY_IN_SECONDS, // 30 days as a rough monthly interval
442 'display' => __( 'Once monthly', 'advanced-database-cleaner' )
443 );
444
445 return $schedules;
446
447 }
448
449 /**
450 * Initialize settings with defaults if not set.
451 *
452 * @return void
453 */
454 public static function maybe_show_global_notifications() {
455
456 // Only load on admin pages and for users with manage_options capability.
457 if ( ! is_admin() || ! current_user_can( 'manage_options' ) )
458 return;
459
460 $notifications = ADBC_Notifications::instance()->get_global_notifications();
461
462 foreach ( $notifications as $key => $notification ) {
463
464 // Special handling for rating_notice notification
465 if ( $key === 'rating_notice' ) {
466
467 $rating_url = 'https://wordpress.org/support/plugin/advanced-database-cleaner/reviews/?filter=5';
468
469 $text = sprintf(
470 /* translators: 1: Plugin name with link */
471 esc_html__( 'You have been using %s for more than 1 week. Would you mind taking a few seconds to give it a 5-star rating on WordPress? Thank you in advance :)', 'advanced-database-cleaner' ),
472 '<a href="admin.php?page=advanced_db_cleaner">Advanced DB Cleaner</a>'
473 );
474
475 if ( is_multisite() )
476 $text = esc_html__( 'You have been using the "Advanced DB Cleaner" for more than 1 week. Would you mind taking a few seconds to give it a 5-star rating on WordPress? Thank you in advance :)', 'advanced-database-cleaner' );
477
478 printf(
479 '<div id="adbc-rating-notice" class="adbc-notice">
480 <div class="adbc-content">
481 <div class="adbc-header">
482 <div class="adbc-star">
483 <span>⭐</span>
484 </div>
485 <span class="adbc-title">%s</span>
486 </div>
487
488 <p class="adbc-text">%s</p>
489
490 <div class="adbc-buttons">
491 <div class="adbc-buttons-left">
492 <a href="%s" target="_blank" class="adbc-btn adbc-btn-primary">
493 <span>⭐</span>%s
494 </a>
495 <a class="adbc-btn adbc-btn-secondary adbc-rating-dismiss">
496 %s
497 </a>
498 <a class="adbc-btn adbc-btn-secondary adbc-rating-dismiss">
499 %s
500 </a>
501 </div>
502
503 <a id="adbc-rating-btn-remind-me" class="adbc-btn adbc-btn-secondary">
504 <span class="dashicons dashicons-clock"></span>%s
505 </a>
506 </div>
507 </div>
508
509 <div>
510 <a title="%s" class="adbc-btn-dismiss adbc-rating-dismiss">
511 <span class="dashicons dashicons-dismiss"></span>
512 </a>
513 </div>
514 </div>',
515 esc_html__( 'Awesome!', 'advanced-database-cleaner' ),
516 $text,
517 $rating_url,
518 esc_html__( 'Ok, you deserved it', 'advanced-database-cleaner' ),
519 esc_html__( 'I already did', 'advanced-database-cleaner' ),
520 esc_html__( 'No, not good enough', 'advanced-database-cleaner' ),
521 esc_html__( 'Remind me later', 'advanced-database-cleaner' ),
522 esc_html__( 'Dismiss', 'advanced-database-cleaner' )
523 );
524
525 } elseif ( $key === 'ltd_migration_notice' ) {
526
527 $upgrade_url = 'https://sigmaplugin.com/upgrade-to-full-v4?utm_source=pro-v4-notification&utm_medium=adbc-pro&utm_campaign=plugins&utm_content=upgrade-to-v4';
528
529 $message_intro = sprintf(
530 /* translators: 1: opening <a> tag, 2: closing </a> tag */
531 __( 'Your %1$sAdvanced DB Cleaner Pro%2$s has been upgraded to version 4.x, a major update featuring a new architecture, a redesigned interface, and many new features.', 'advanced-database-cleaner' ),
532 '<a href="admin.php?page=advanced_db_cleaner" class="adbc-ltd-text-link">',
533 '</a>'
534 );
535
536 if ( is_multisite() )
537 $message_intro = esc_html__( 'Your "Advanced DB Cleaner Pro" has been upgraded to version 4.x, a major update featuring a new architecture, a redesigned interface, and many new features.', 'advanced-database-cleaner' );
538
539 $phrase1 = sprintf(
540 /* translators: 1: opening <strong> tag, 2: closing </strong> tag */
541 esc_html__(
542 'We have introduced a new Remote Scan feature, designed to detect orphaned database items with much higher accuracy. Because this feature requires ongoing infrastructure and maintenance costs, %1$sit is not included in the lifetime plan%2$s.',
543 'advanced-database-cleaner'
544 ),
545 '<strong>',
546 '</strong>'
547 );
548
549 $phrase2 = esc_html__( 'To access it, we created a separate plugin version under an annual pricing model, bundled with the Remote Scan feature and upcoming AI and cloud capabilities. As a lifetime customer, you can benefit from a 50% discount on the first purchase and all renewals (discount available for a limited time).', 'advanced-database-cleaner' );
550
551 echo '<div id="adbc-ltd-migration-notice" class="adbc-ltd-migration-notice">';
552 echo '<div class="adbc-ltd-icon">';
553 echo '<svg id="fi_8606859" enable-background="new 0 0 500 500" height="80" viewBox="0 0 500 500" width="80" xmlns="http://www.w3.org/2000/svg"><path clip-rule="evenodd" d="m350.8 245.46c2.85 6.65 4.75 12.35 7.6 19 1.9 5.7 3.8 13.3 5.7 19.95 3.8 8.55 11.4 34.2 10.45 42.75-.95 7.6 1.9 19 0 21.85-1.9 5.7-6.65 12.35-12.35 13.3 0-2.85 1.9-5.7 1.9-9.5.95-12.35 0-19.95-2.85-31.35-3.8-13.3-19.95-56.05-24.7-70.31l-21.85-49.4c-7.6-16.15-16.15-30.4-23.75-47.5-6.65-18.05-23.75-45.6-38.95-55.1 0-2.85 12.35-10.45 17.1-8.55s10.45 9.5 12.35 13.3c3.8 6.65 6.65 9.5 10.45 16.15 3.8 5.7 6.65 11.4 9.5 17.1 6.65 10.45 13.3 22.8 19 34.2 4.75 12.35 9.5 24.7 15.2 37.05s10.45 23.75 15.2 37.06zm-7.6-61.76c.95 4.75 7.6 19.95 10.45 24.7 1.9 3.8 3.8 8.55 5.7 12.35s2.85 9.5 5.7 12.35c14.26-9.5 12.36-51.3-21.85-49.4zm-246.07 57.95c0 3.8 14.25 42.75 16.15 48.45 6.65 16.15 12.35 33.25 19 48.45 35.15-1.9 41.8-.95 79.81 0 12.35.95 75.06 11.4 86.46 13.3 13.3 1.9 27.55 7.6 40.85 9.5.95 0 6.65-5.7 6.65-5.7 3.8-3.8.95-23.75-.95-29.45-3.8-13.3-7.6-22.8-11.4-34.2-1.9-5.7-3.8-10.45-5.7-17.1-4.75-14.25-15.2-35.15-20.9-48.45-6.65-16.15-5.7-15.2-14.25-31.35-5.7-10.45-10.45-19-15.2-29.45-4.75-12.35-6.65-18.05-15.2-30.4-4.75-6.65-16.15-21.85-22.8-23.75-5.7 4.75-17.1 24.7-30.4 38-6.65 5.7-12.35 11.4-17.1 17.1-6.65 6.65-11.4 9.5-19 15.2-6.65 3.8-12.35 9.5-18.05 15.2l-47.5 37.05c-3.82 2.85-7.62 4.75-10.47 7.6zm17.1 98.81c-1.9 1.9-13.3 2.85-17.1 3.8-5.7.95-10.45 2.85-16.15 4.75-10.45 2.85-21.85 5.7-32.3 8.55-4.75.95-11.4 2.85-18.05 2.85-3.8-.95-3.8-1.9-5.7-4.75-1.9-1.9-3.8-2.85-4.75-4.75-11.4-14.25-21.85-43.7-19.95-61.76 1.9-12.35 6.65-18.05 17.1-22.8 24.7-10.45 37.05-8.55 48.45-12.35 4.75-.95 11.4-3.8 15.2-4.75l32.3 87.41c1.9 3.8 1.9 2.85.95 3.8zm114.96-186.21c5.7-1.9 10.45 2.85 10.45 7.6-.95 3.8-5.7 9.5-8.55 11.4-10.45 11.4-7.6 7.6-19 21.85-9.5 10.45-13.3 11.4-21.85 19.95l-21.85 17.1c-5.7 2.85-12.35-.95-11.4-7.6 0-4.75 5.7-8.55 8.55-11.4l22.8-18.05c7.6-6.65 11.4-13.3 19.95-21.85 2.85-2.85 6.65-5.7 9.5-9.5 3.8-4.75 6.65-7.6 11.4-9.5zm120.67-102.61c-4.75 1.9-4.75 4.75-5.7 12.35-.95 6.65-4.75 27.55-2.85 33.25.95 4.75 6.65 7.6 11.4 5.7 5.7-1.9 4.75-6.65 4.75-13.3.95-7.6 4.75-27.55 3.8-32.3-1.9-4.75-5.7-7.6-11.4-5.7zm110.2 271.72-14.25-15.2c-3.8-2.85-11.4-11.4-15.2-13.3-8.55-.95-14.25 6.65-9.5 13.3l13.3 13.3c4.75 4.75 15.2 20.9 23.75 12.35 3.8-3.8 1.9-5.7 1.9-10.45zm23.76-159.61c-5.7 1.9-7.6 4.75-12.35 6.65s-11.4 2.85-16.15 3.8c-17.1 3.8-7.6 19.95 1.9 18.05 2.85-.95 2.85-1.9 6.65-2.85 2.85-.95 4.75-.95 7.6-1.9 5.7-1.9 9.5-2.85 14.25-5.7 3.8-3.8 9.5-2.85 9.5-10.45 0-5.7-5.7-9.5-11.4-7.6zm-43.71-80.76c-5.7 2.85-5.7 5.7-9.5 11.4-2.85 3.8-5.7 7.6-8.55 11.4-1.9 2.85-6.65 9.5-6.65 13.3 0 6.65 5.7 10.45 11.4 8.55 3.8-.95 5.7-7.6 8.55-11.4 3.8-5.7 15.2-19.95 15.2-24.7 0-5.7-4.75-11.4-10.45-8.55zm18.05 157.71c-4.75.95-8.55 5.7-5.7 11.4s33.25 16.15 40.85 14.25c4.75-.95 8.55-6.65 5.7-11.4-1.9-5.7-10.45-2.85-26.6-10.45-4.75-1.9-8.54-5.7-14.25-3.8zm-410.43 36.11c-9.5 3.8-17.1 5.7-23.75 9.5-10.45 6.65-4.75 19.95 6.65 15.2 3.8-1.9 7.6-2.85 11.4-4.75s7.6-2.85 12.35-4.75c9.5-3.8 5.7-19-6.65-15.2zm14.25 95.01c.95-.95 0 0 1.9-.95l15.2-4.75c4.75-.95 12.35-3.8 17.1-4.75.95.95 2.85 6.65 5.7 11.4 1.9 3.8 4.75 7.6 6.65 11.4 8.55 15.2 5.7 9.5 14.25 21.85 3.8 4.75 13.3 17.1 15.2 20.9 1.9 5.7 2.85 8.55-1.9 12.35-6.65 5.7-16.15 7.6-25.65 9.5-6.65.95-9.5-1.9-12.35-5.7-4.75-6.65-9.5-15.2-13.3-21.85-3.8-8.55-8.55-15.2-12.35-23.75-1.9-4.75-9.5-20.9-10.45-25.65z" fill="#5EA500" fill-rule="evenodd"></path></svg>';
554 echo '</div>';
555 echo '<div class="adbc-ltd-content">';
556 echo '<span class="adbc-ltd-title">' . esc_html__( 'Advanced DB Cleaner Pro V4 is finally here!', 'advanced-database-cleaner' ) . '</span>';
557 echo '<p class="adbc-ltd-text">' . wp_kses_post( $message_intro ) . '</p>';
558 echo '<div style="background-color:#EFF6FF;padding:16px 20px;margin:10px 0px;border-radius:4px;border:1.5px solid #3b82f6;box-shadow:0 4px 4px rgba(0,0,0,0.06);">';
559 echo '<p style="margin:0 0 8px 0;font-weight:700;font-size:14px;color:#FF6900;letter-spacing:0.05em;">ⓘ IMPORTANT!</p>';
560 echo '<ul class="adbc-ltd-list"><li>' . $phrase1 . '</li><li>' . $phrase2 . '</li></ul>';
561 echo '</div>';
562 echo '<div class="adbc-ltd-buttons">';
563 echo '<a href="' . esc_url( $upgrade_url ) . '" target="_blank" class="button button-primary adbc-ltd-btn-primary">';
564 echo '<span class="dashicons dashicons-arrow-right-alt" style="margin-top:5px;margin-right:5px;"></span>';
565 echo '<b>' . esc_html__( 'How to get to the full version - 50% OFF forever', 'advanced-database-cleaner' ) . '</b>';
566 echo '</a>';
567 echo '<a href="#" id="adbc-ltd-btn-remind-me" class="button adbc-ltd-btn-secondary">';
568 echo '<span class="dashicons dashicons-clock" style="margin-top:5px;margin-right:5px;"></span>';
569 echo esc_html__( 'Remind me in a week', 'advanced-database-cleaner' );
570 echo '</a>';
571 echo '</div>';
572 echo '</div>';
573 echo '<div class="adbc-ltd-dismiss">';
574 echo '<a href="#" title="' . esc_attr__( 'Dismiss', 'advanced-database-cleaner' ) . '"><span class="dashicons dashicons-dismiss"></span></a>';
575 echo '</div>';
576 echo '</div>';
577
578 } else {
579 // Standard notification rendering. For now, we are not rendering these notifications in the admin area.
580 // $type = esc_attr( $notification['type'] );
581 // $message = wp_kses_post( $notification['message'] );
582 // $dismissible = ! empty( $notification['dismissible'] ) ? ' is-dismissible' : '';
583 // printf(
584 // '<div class="notice notice-%1$s%2$s adbc-notice" data-adbc-notice-key="%3$s"><p>%4$s</p></div>',
585 // $type,
586 // $dismissible,
587 // esc_attr( $key ),
588 // $message
589 // );
590 }
591 }
592 }
593
594 /**
595 * Static proxy for add_admin_menu to be used in the hooks.
596 *
597 * @return void
598 */
599 public static function _add_admin_menu() {
600 self::instance()->add_admin_menu();
601 }
602
603 /**
604 * Static proxy for add_network_admin_menu to be used in the hooks.
605 *
606 * @return void
607 */
608 public static function _add_network_admin_menu() {
609 self::instance()->add_network_admin_menu();
610 }
611
612 /**
613 * Static proxy for enqueue_scripts to be used in the hooks.
614 *
615 * @param string $hook The current admin page hook.
616 * @return void
617 */
618 public static function _enqueue_scripts( $hook ) {
619 self::instance()->enqueue_scripts( $hook );
620 }
621
622 /**
623 * Load all cleanup type handlers.
624 *
625 * This method includes all PHP files in the type-handlers directories,
626 * allowing each handler to self-register with the General Cleanup system.
627 * It supports both free and premium handlers based on the plugin version.
628 *
629 * @return void
630 */
631 public static function load_general_cleanup_handlers() {
632
633 $base_dir = ADBC_PLUGIN_DIR_PATH . '/includes/classes/general-cleanup/type-handlers';
634 $premium_dir = ADBC_PLUGIN_DIR_PATH . 'includes/premium/classes/general-cleanup/type-handlers';
635
636 $dirs = [ $base_dir ];
637
638 // Only add premium if this is the premium build
639 if ( ADBC_VERSION_TYPE === "PREMIUM" ) {
640 $dirs[] = $premium_dir;
641 }
642
643 foreach ( $dirs as $dir ) {
644
645 if ( ! is_dir( $dir ) ) {
646 continue;
647 }
648
649 foreach ( glob( $dir . '/*.php' ) as $file ) {
650 include_once $file; // triggers the self-registration logic
651 }
652
653 }
654
655 }
656
657 /**
658 * Detect and handle conflicts between free, pro and premium versions.
659 *
660 * @return bool True if a conflict was detected and handled, false otherwise.
661 */
662 public static function has_conflict() {
663
664 if ( ! function_exists( 'deactivate_plugins' ) )
665 include_once( ABSPATH . 'wp-admin/includes/plugin.php' );
666
667 $premium_active = is_plugin_active( 'advanced-database-cleaner-premium/advanced-db-cleaner.php' );
668 $pro_active = is_plugin_active( 'advanced-database-cleaner-pro/advanced-db-cleaner.php' );
669 $free_active = is_plugin_active( 'advanced-database-cleaner/advanced-db-cleaner.php' );
670
671 if ( $premium_active && $free_active ) {
672
673 $network_wide = function_exists( 'is_plugin_active_for_network' ) && is_plugin_active_for_network( 'advanced-database-cleaner/advanced-db-cleaner.php' );
674 deactivate_plugins( 'advanced-database-cleaner/advanced-db-cleaner.php', true, $network_wide );
675 ADBC_Common_Utils::old_plugin_version_deactivation_cleaning();
676 self::schedule_conflict_notice( 'free_premium' );
677
678 return true;
679
680 }
681
682 if ( $premium_active && $pro_active ) {
683
684 $network_wide = function_exists( 'is_plugin_active_for_network' ) && is_plugin_active_for_network( 'advanced-database-cleaner-pro/advanced-db-cleaner.php' );
685 deactivate_plugins( 'advanced-database-cleaner-pro/advanced-db-cleaner.php', true, $network_wide );
686 ADBC_Common_Utils::old_plugin_version_deactivation_cleaning();
687 self::schedule_conflict_notice( 'pro_premium' );
688
689 return true;
690
691 }
692
693 if ( $pro_active && $free_active ) {
694
695 $network_wide = function_exists( 'is_plugin_active_for_network' ) && is_plugin_active_for_network( 'advanced-database-cleaner/advanced-db-cleaner.php' );
696 deactivate_plugins( 'advanced-database-cleaner/advanced-db-cleaner.php', true, $network_wide );
697 ADBC_Common_Utils::old_plugin_version_deactivation_cleaning();
698 self::schedule_conflict_notice( 'free_pro' );
699
700 return true;
701
702 }
703
704 return false;
705
706 }
707
708 /**
709 * Check whether the WOptimize pro is installed on the site.
710 *
711 * @return bool True if WOptimize Pro plugin is installed, false otherwise.
712 */
713 public static function is_woptimize_installed() {
714
715 if ( ! function_exists( 'get_plugins' ) )
716 include_once( ABSPATH . 'wp-admin/includes/plugin.php' );
717
718 foreach ( array_keys( get_plugins() ) as $plugin_file ) {
719
720 if ( strpos( $plugin_file, 'woptimize-pro' ) === 0 )
721 return true;
722
723 }
724
725 return false;
726
727 }
728
729 /**
730 * Maybe schedule a conflict notice.
731 *
732 * @return void
733 */
734 public static function maybe_schedule_conflict_notice() {
735
736 global $pagenow;
737
738 if ( $pagenow === 'plugins.php' && get_option( self::$conflict_notice_option_key, '' ) !== '' ) {
739 add_action( 'all_admin_notices', [ self::class, 'render_scheduled_conflict_notice' ] );
740 }
741
742 }
743
744 /**
745 * Persist a conflict notice so it is reliably displayed even after redirects.
746 *
747 * @param string $type Conflict type identifier.
748 * @return void
749 */
750 private static function schedule_conflict_notice( $type ) {
751
752 // Store the last conflict type; it will be rendered (and cleared) on the next admin page load.
753 update_option( self::$conflict_notice_option_key, $type, false );
754
755 // Also attempt to render it in the current request when possible.
756 add_action( 'all_admin_notices', [ self::class, 'render_scheduled_conflict_notice' ] );
757 }
758
759 /**
760 * Render the stored conflict notice (if any) and clear it.
761 *
762 * @return void
763 */
764 public static function render_scheduled_conflict_notice() {
765
766 $type = get_option( self::$conflict_notice_option_key, '' );
767
768 if ( $type === '' ) {
769 return;
770 }
771
772 // Clear immediately so the notice is shown only once.
773 delete_option( self::$conflict_notice_option_key );
774
775 switch ( $type ) {
776 case 'free_premium':
777 self::free_premium_conflict_notice();
778 break;
779 case 'pro_premium':
780 self::pro_premium_conflict_notice();
781 break;
782 case 'free_pro':
783 self::free_pro_conflict_notice();
784 break;
785 }
786 }
787
788 /**
789 * Display a notice if the free version was deactivated due to the premium version activation.
790 *
791 * @return void
792 */
793 public static function free_premium_conflict_notice() {
794 echo '<div class="notice notice-warning"><p>';
795 esc_html_e( 'The free version of Advanced DB Cleaner has been de-activated since the premium version is active.', 'advanced-database-cleaner' );
796 echo "</p></div>";
797 }
798
799 /**
800 * Display a notice if the free version was deactivated due to the pro version activation.
801 *
802 * @return void
803 */
804 public static function free_pro_conflict_notice() {
805 echo '<div class="notice notice-warning"><p>';
806 esc_html_e( 'The free version of Advanced DB Cleaner has been de-activated since the pro version is active.', 'advanced-database-cleaner' );
807 echo "</p></div>";
808 }
809
810 /**
811 * Display a notice if the pro version was deactivated due to the premium version activation.
812 *
813 * @return void
814 */
815 public static function pro_premium_conflict_notice() {
816 echo '<div class="notice notice-warning"><p>';
817 esc_html_e( 'The pro version of Advanced DB Cleaner has been de-activated since the newest premium version is active.', 'advanced-database-cleaner' );
818 echo "</p></div>";
819 }
820
821 /**
822 * Static proxy for capture_original_plugin_meta_links to be used in the hooks.
823 *
824 * @param array $links The current plugin meta links.
825 * @param string $file The plugin file.
826 * @return array The modified plugin meta links.
827 */
828 public static function _capture_original_plugin_meta_links( $links, $file ) {
829 return self::instance()->capture_original_plugin_meta_links( $links, $file );
830 }
831
832 /**
833 * Static proxy for restore_plugin_meta_links to be used in the hooks.
834 *
835 * @param array $links The current plugin meta links.
836 * @param string $file The plugin file.
837 * @return array The modified plugin meta links.
838 */
839 public static function _restore_plugin_meta_links( $links, $file ) {
840 return self::instance()->restore_plugin_meta_links( $links, $file );
841 }
842
843 /**
844 * Capture the original WordPress default plugin meta links before any plugin modifies them
845 */
846 public function capture_original_plugin_meta_links( $links, $file ) {
847
848 $plugin_file = plugin_basename( ADBC_MAIN_PLUGIN_FILE_PATH );
849
850 if ( $file === $plugin_file && empty( $this->original_plugin_meta_links ) ) {
851 $this->original_plugin_meta_links = $links;
852 }
853
854 return $links;
855
856 }
857
858 /**
859 * Remove all third-party links
860 */
861 public function restore_plugin_meta_links( $links, $file ) {
862
863 $plugin_file = plugin_basename( ADBC_MAIN_PLUGIN_FILE_PATH );
864
865 if ( $file !== $plugin_file ) {
866 return $links;
867 }
868
869 return $this->original_plugin_meta_links;
870
871 }
872
873 /**
874 * Load the plugin text domain for translations.
875 *
876 * @return void
877 */
878 public static function load_text_domain() {
879 load_plugin_textdomain( 'advanced-database-cleaner', false, ADBC_PLUGIN_DIR_NAME . '/languages/' );
880 }
881
882 }