efined('WP_CLI') && WP_CLI && class_exists('WP_CLI')) { \WP_CLI::add_command('mhli smoke-test', \MHLI\CLI\SmokeTestCommand::class); \WP_CLI::add_command('mhli enrich', \MHLI\CLI\EnrichCommand::class); } // Import UI is handled by AdminMenu. Keep ImportPage class available for legacy // installs, but do not instantiate it here to avoid duplicate mhli-import menus. if (get_option('mhli_onboarding_completed')) { $this->schedule_cron_jobs(); } } private function create_tables() { global $wpdb; $charset_collate = $wpdb->get_charset_collate(); $tables = []; $tables[] = "CREATE TABLE {$wpdb->prefix}mhli_pricing ( id bigint(20) unsigned NOT NULL AUTO_INCREMENT, country_code char(2) NOT NULL, tier_slug varchar(30) NOT NULL, billing_period enum('monthly','yearly','onetime') NOT NULL DEFAULT 'monthly', amount_cents bigint(20) unsigned NOT NULL DEFAULT 0, currency char(3) NOT NULL, features_json longtext, is_active tinyint(1) NOT NULL DEFAULT 1, effective_from date DEFAULT NULL, effective_to date DEFAULT NULL, created_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uq_country_tier_period (country_code, tier_slug, billing_period, effective_from), KEY idx_country_active (country_code, is_active) ) $charset_collate;"; $tables[] = "CREATE TABLE {$wpdb->prefix}mhli_countries ( code char(2) NOT NULL, name_en varchar(100) NOT NULL, name_fr varchar(100) DEFAULT NULL, currency char(3) NOT NULL, currency_symbol varchar(10) NOT NULL, currency_position enum('left','right') NOT NULL DEFAULT 'left', decimal_places tinyint(1) NOT NULL DEFAULT 2, thousands_separator char(1) NOT NULL DEFAULT ',', decimal_separator char(1) NOT NULL DEFAULT '.', tax_inclusive tinyint(1) NOT NULL DEFAULT 0, stripe_country char(2) NOT NULL, supported_payment_methods json NOT NULL, tax_number_format varchar(100) DEFAULT NULL, phone_country_code varchar(5) NOT NULL, timezone_default varchar(50) NOT NULL, languages json NOT NULL, default_language char(5) NOT NULL, is_active tinyint(1) NOT NULL DEFAULT 1, is_default tinyint(1) NOT NULL DEFAULT 0, sort_order int(11) NOT NULL DEFAULT 0, PRIMARY KEY (code), KEY idx_active_default (is_active, is_default) ) $charset_collate;"; $tables[] = "CREATE TABLE {$wpdb->prefix}mhli_tiers ( slug varchar(30) NOT NULL, name_en varchar(100) NOT NULL, name_fr varchar(100) DEFAULT NULL, description_en text, description_fr text, billing_period enum('monthly','yearly','onetime') NOT NULL DEFAULT 'monthly', features_json longtext NOT NULL, is_active tinyint(1) NOT NULL DEFAULT 1, is_public tinyint(1) NOT NULL DEFAULT 1, sort_order int(11) NOT NULL DEFAULT 0, stripe_product_id varchar(100) DEFAULT NULL, created_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (slug) ) $charset_collate;"; $tables[] = "CREATE TABLE {$wpdb->prefix}mhli_security_events ( id bigint(20) unsigned NOT NULL AUTO_INCREMENT, user_id bigint(20) unsigned DEFAULT NULL, event_type varchar(50) NOT NULL, risk_score tinyint(3) unsigned NOT NULL DEFAULT 0, ip_address varchar(45) NOT NULL, country_code char(2) DEFAULT NULL, billing_country char(2) DEFAULT NULL, phone_country char(2) DEFAULT NULL, device_fingerprint json DEFAULT NULL, metadata json DEFAULT NULL, action_taken enum('allow','challenge','block','manual_review','kyc_required') NOT NULL DEFAULT 'allow', created_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_user_created (user_id, created_at), KEY idx_ip_created (ip_address, created_at), KEY idx_risk_created (risk_score, created_at), KEY idx_event_type (event_type) ) $charset_collate;"; $tables[] = "CREATE TABLE {$wpdb->prefix}mhli_import_jobs ( id bigint(20) unsigned NOT NULL AUTO_INCREMENT, batch_id varchar(64) NOT NULL, source varchar(30) NOT NULL, total_items int(11) NOT NULL DEFAULT 0, processed_items int(11) NOT NULL DEFAULT 0, successful_items int(11) NOT NULL DEFAULT 0, failed_items int(11) NOT NULL DEFAULT 0, status enum('pending','running','completed','failed','paused') NOT NULL DEFAULT 'pending', config_json longtext, error_log longtext, started_at datetime DEFAULT NULL, completed_at datetime DEFAULT NULL, created_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uq_batch_id (batch_id), KEY idx_status (status), KEY idx_source (source) ) $charset_collate;"; $tables[] = "CREATE TABLE {$wpdb->prefix}mhli_user_country ( user_id bigint(20) unsigned NOT NULL, country_code char(2) NOT NULL, locked_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, lock_method enum('billing','phone','kyc','manual') NOT NULL DEFAULT 'billing', ip_at_lock varchar(45) NOT NULL, device_fingerprint_hash char(64) NOT NULL, override_count int(11) NOT NULL DEFAULT 0, last_override_attempt datetime DEFAULT NULL, status enum('active','flagged','locked','pending_verification') NOT NULL DEFAULT 'active', PRIMARY KEY (user_id), KEY idx_country (country_code), KEY idx_status (status) ) $charset_collate;"; if (!defined('ABSPATH')) { return; } require_once ABSPATH . 'wp-admin/includes/upgrade.php'; foreach ($tables as $sql) { dbDelta($sql); } } private function schedule_cron_jobs() { if (!class_exists('ActionScheduler') || !function_exists('as_next_scheduled_action') || !function_exists('as_schedule_recurring_action')) { return; } if (!as_next_scheduled_action('mhli_daily_import')) { $timestamp = strtotime('tomorrow 6:00 AM'); as_schedule_recurring_action($timestamp, DAY_IN_SECONDS, 'mhli_daily_import'); } if (!as_next_scheduled_action('mhli_weekly_cleanup')) { $timestamp = strtotime('next sunday 3:00 AM'); as_schedule_recurring_action($timestamp, WEEK_IN_SECONDS, 'mhli_weekly_cleanup'); } } public function admin_notices() { $errors = get_option('mhli_activation_errors', []); if (!empty($errors)) { foreach ($errors as $error) { echo '

' . esc_html(\MHLI\Core\SettingsRepository::get('plugin_label', 'Listeo Importer')) . ': ' . esc_html($error) . '

'; } delete_option('mhli_activation_errors'); } if (!get_option('mhli_onboarding_completed') && current_user_can('manage_options')) { $screen = get_current_screen(); if ($screen && $screen->base !== 'tools_page_mhli-dashboard') { echo '

MinouHub Listings Importer: ' . __('Plugin requires initial setup. Please complete the onboarding wizard.', 'mhli') . ' ' . __('Run Setup Wizard', 'mhli') . '

'; } } } } MHLI_Plugin::instance(); function mhli_uninstall() { if (!current_user_can('manage_options')) return; // v0.2: Default is to PRESERVE data on uninstall. Only drop/delete // if the admin explicitly opted in via mhli_delete_data_on_uninstall. $delete_data = (bool) get_option('mhli_delete_data_on_uninstall', false); // Always unschedule cron jobs — they are meaningless without the plugin. if (class_exists('ActionScheduler')) { as_unschedule_all_actions('mhli_daily_import'); as_unschedule_all_actions('mhli_weekly_cleanup'); } if (!$delete_data) { // Keep all tables, options, postmeta and usermeta intact. flush_rewrite_rules(); return; } // Explicit destructive cleanup (opt-in only). global $wpdb; $tables = [ $wpdb->prefix . 'mhli_pricing', $wpdb->prefix . 'mhli_countries', $wpdb->prefix . 'mhli_tiers', $wpdb->prefix . 'mhli_security_events', $wpdb->prefix . 'mhli_import_jobs', $wpdb->prefix . 'mhli_user_country', ]; foreach ($tables as $table) { $wpdb->query("DROP TABLE IF EXISTS $table"); } $options = [ 'mhli_version', 'mhli_onboarding_completed', 'mhli_onboarding_version', 'mhli_countries', 'mhli_tiers', 'mhli_country_pricing', 'mhli_integrations', 'mhli_default_country', 'mhli_claim_email_subject_en', 'mhli_claim_email_subject_fr', 'mhli_claim_email_body_en', 'mhli_claim_email_body_fr', 'mhli_activation_errors', 'mhli_delete_data_on_uninstall', ]; foreach ($options as $opt) { delete_option($opt); } $wpdb->query("DELETE FROM {$wpdb->postmeta} WHERE meta_key LIKE '_mhli_%'"); $wpdb->query("DELETE FROM {$wpdb->usermeta} WHERE meta_key LIKE '_mhli_%'"); flush_rewrite_rules(); } register_uninstall_hook(__FILE__, 'mhli_uninstall'); https://minouhub.com/wp-sitemap-posts-post-1.xmlhttps://minouhub.com/wp-sitemap-posts-page-1.xmlhttps://minouhub.com/wp-sitemap-posts-listing-1.xmlhttps://minouhub.com/wp-sitemap-posts-product-1.xmlhttps://minouhub.com/wp-sitemap-posts-testimonial-1.xmlhttps://minouhub.com/wp-sitemap-taxonomies-category-1.xmlhttps://minouhub.com/wp-sitemap-taxonomies-post_tag-1.xmlhttps://minouhub.com/wp-sitemap-taxonomies-listing_category-1.xmlhttps://minouhub.com/wp-sitemap-taxonomies-region-1.xmlhttps://minouhub.com/wp-sitemap-taxonomies-product_cat-1.xmlhttps://minouhub.com/wp-sitemap-users-1.xml