true, 'jat_use_driver_app' => true, 'jat_manage_own_profile' => true, 'jat_manage_own_assets' => true, 'jat_manage_own_sos_contacts' => true, 'jat_start_tracking' => true, 'jat_send_sos' => true, 'jat_view_own_qr' => true, ); $caps_operator = array_merge( $caps_driver, array( 'jat_manage_operator_account' => true, 'jat_manage_multiple_assets' => true, 'jat_assign_drivers' => true, 'jat_view_operator_reports' => true, ) ); $caps_monitor = array( 'read' => true, 'jat_view_monitor_dashboard' => true, 'jat_view_live_map' => true, 'jat_view_sos_alerts' => true, 'jat_acknowledge_sos' => true, 'jat_add_sos_notes' => true, ); $caps_supervisor = array_merge( $caps_monitor, array( 'jat_resolve_sos' => true, 'jat_view_monitor_reports' => true, 'jat_view_audit_logs' => true, 'jat_manage_monitor_notes' => true, ) ); add_role('ja_tracker_driver', 'JA Tracker Driver', $caps_driver); add_role('ja_tracker_operator', 'JA Tracker Operator', $caps_operator); add_role('ja_tracker_monitor', 'JA Tracker Monitor', $caps_monitor); add_role('ja_tracker_supervisor', 'JA Tracker Supervisor', $caps_supervisor); self::sync_role_caps('ja_tracker_driver', $caps_driver); self::sync_role_caps('ja_tracker_operator', $caps_operator); self::sync_role_caps('ja_tracker_monitor', $caps_monitor); self::sync_role_caps('ja_tracker_supervisor', $caps_supervisor); $admin_role = get_role('administrator'); if ($admin_role) { $admin_caps = array_merge( $caps_driver, $caps_operator, $caps_monitor, $caps_supervisor, array( 'jat_manage_system' => true, 'jat_manage_settings' => true, 'jat_manage_all_accounts' => true, 'jat_manage_all_assets' => true, 'jat_suspend_assets' => true, 'jat_regenerate_qr' => true, 'jat_view_all_reports' => true, 'jat_manage_data_retention' => true, 'jat_run_diagnostics' => true, ) ); foreach ($admin_caps as $cap => $grant) { if ($grant) { $admin_role->add_cap($cap); } } } } private static function sync_role_caps($role_key, array $caps) { $role = get_role($role_key); if (!$role) { return; } foreach ($caps as $cap => $grant) { if ($grant) { $role->add_cap($cap); } } } private static function install_tables() { global $wpdb; require_once ABSPATH . 'wp-admin/includes/upgrade.php'; $charset_collate = $wpdb->get_charset_collate(); $tables = self::tables(); $sql = array(); $sql[] = "CREATE TABLE {$tables['accounts']} ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, wp_user_id BIGINT UNSIGNED NOT NULL DEFAULT 0, account_type VARCHAR(60) NOT NULL DEFAULT '', account_name VARCHAR(190) NOT NULL DEFAULT '', status VARCHAR(40) NOT NULL DEFAULT 'pending', primary_email VARCHAR(190) NOT NULL DEFAULT '', primary_phone VARCHAR(60) NOT NULL DEFAULT '', billing_mode VARCHAR(40) NOT NULL DEFAULT 'per_asset', notes LONGTEXT NULL, created_at_utc DATETIME NOT NULL, updated_at_utc DATETIME NULL, PRIMARY KEY (id), KEY wp_user_id (wp_user_id), KEY account_type (account_type), KEY status (status), KEY primary_email (primary_email) ) $charset_collate;"; $sql[] = "CREATE TABLE {$tables['profiles']} ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, account_id BIGINT UNSIGNED NOT NULL DEFAULT 0, wp_user_id BIGINT UNSIGNED NOT NULL DEFAULT 0, profile_type VARCHAR(60) NOT NULL DEFAULT '', display_name VARCHAR(190) NOT NULL DEFAULT '', legal_name VARCHAR(190) NOT NULL DEFAULT '', email VARCHAR(190) NOT NULL DEFAULT '', phone VARCHAR(60) NOT NULL DEFAULT '', badge_number VARCHAR(100) NOT NULL DEFAULT '', licence_number VARCHAR(100) NOT NULL DEFAULT '', status VARCHAR(40) NOT NULL DEFAULT 'pending', profile_photo_id BIGINT UNSIGNED NOT NULL DEFAULT 0, meta_json LONGTEXT NULL, created_at_utc DATETIME NOT NULL, updated_at_utc DATETIME NULL, PRIMARY KEY (id), KEY account_id (account_id), KEY wp_user_id (wp_user_id), KEY profile_type (profile_type), KEY status (status), KEY badge_number (badge_number) ) $charset_collate;"; $sql[] = "CREATE TABLE {$tables['assets']} ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, account_id BIGINT UNSIGNED NOT NULL DEFAULT 0, asset_type VARCHAR(60) NOT NULL DEFAULT '', asset_label VARCHAR(190) NOT NULL DEFAULT '', plate_number VARCHAR(60) NOT NULL DEFAULT '', make VARCHAR(100) NOT NULL DEFAULT '', model VARCHAR(100) NOT NULL DEFAULT '', colour VARCHAR(100) NOT NULL DEFAULT '', route_area VARCHAR(190) NOT NULL DEFAULT '', parish VARCHAR(100) NOT NULL DEFAULT '', badge_number VARCHAR(100) NOT NULL DEFAULT '', licence_number VARCHAR(100) NOT NULL DEFAULT '', verification_status VARCHAR(40) NOT NULL DEFAULT 'pending', subscription_status VARCHAR(40) NOT NULL DEFAULT 'unknown', qr_status VARCHAR(40) NOT NULL DEFAULT 'inactive', tracking_status VARCHAR(40) NOT NULL DEFAULT 'offline', asset_photo_id BIGINT UNSIGNED NOT NULL DEFAULT 0, meta_json LONGTEXT NULL, created_at_utc DATETIME NOT NULL, updated_at_utc DATETIME NULL, PRIMARY KEY (id), KEY account_id (account_id), KEY asset_type (asset_type), KEY plate_number (plate_number), KEY verification_status (verification_status), KEY subscription_status (subscription_status), KEY tracking_status (tracking_status) ) $charset_collate;"; $sql[] = "CREATE TABLE {$tables['asset_assignments']} ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, account_id BIGINT UNSIGNED NOT NULL DEFAULT 0, asset_id BIGINT UNSIGNED NOT NULL DEFAULT 0, profile_id BIGINT UNSIGNED NOT NULL DEFAULT 0, assigned_by_user_id BIGINT UNSIGNED NOT NULL DEFAULT 0, status VARCHAR(40) NOT NULL DEFAULT 'active', started_at_utc DATETIME NOT NULL, ended_at_utc DATETIME NULL, notes LONGTEXT NULL, created_at_utc DATETIME NOT NULL, PRIMARY KEY (id), KEY account_id (account_id), KEY asset_id (asset_id), KEY profile_id (profile_id), KEY status (status), KEY started_at_utc (started_at_utc) ) $charset_collate;"; $sql[] = "CREATE TABLE {$tables['qr_tokens']} ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, account_id BIGINT UNSIGNED NOT NULL DEFAULT 0, asset_id BIGINT UNSIGNED NOT NULL DEFAULT 0, token_hash CHAR(64) NOT NULL DEFAULT '', token_label VARCHAR(190) NOT NULL DEFAULT '', status VARCHAR(40) NOT NULL DEFAULT 'active', generated_by_user_id BIGINT UNSIGNED NOT NULL DEFAULT 0, generated_at_utc DATETIME NOT NULL, revoked_at_utc DATETIME NULL, last_scanned_at_utc DATETIME NULL, scan_count BIGINT UNSIGNED NOT NULL DEFAULT 0, PRIMARY KEY (id), KEY account_id (account_id), KEY asset_id (asset_id), KEY token_hash (token_hash), KEY status (status) ) $charset_collate;"; $sql[] = "CREATE TABLE {$tables['sos_contacts']} ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, account_id BIGINT UNSIGNED NOT NULL DEFAULT 0, profile_id BIGINT UNSIGNED NOT NULL DEFAULT 0, asset_id BIGINT UNSIGNED NOT NULL DEFAULT 0, contact_name VARCHAR(190) NOT NULL DEFAULT '', contact_label VARCHAR(100) NOT NULL DEFAULT '', email VARCHAR(190) NOT NULL DEFAULT '', phone VARCHAR(60) NOT NULL DEFAULT '', notify_email TINYINT(1) NOT NULL DEFAULT 1, notify_sms TINYINT(1) NOT NULL DEFAULT 1, status VARCHAR(40) NOT NULL DEFAULT 'active', sort_order INT UNSIGNED NOT NULL DEFAULT 0, created_at_utc DATETIME NOT NULL, updated_at_utc DATETIME NULL, PRIMARY KEY (id), KEY account_id (account_id), KEY profile_id (profile_id), KEY asset_id (asset_id), KEY status (status), KEY email (email), KEY phone (phone) ) $charset_collate;"; $sql[] = "CREATE TABLE {$tables['app_tokens']} ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, wp_user_id BIGINT UNSIGNED NOT NULL DEFAULT 0, account_id BIGINT UNSIGNED NOT NULL DEFAULT 0, profile_id BIGINT UNSIGNED NOT NULL DEFAULT 0, device_id VARCHAR(190) NOT NULL DEFAULT '', device_label VARCHAR(190) NOT NULL DEFAULT '', platform VARCHAR(40) NOT NULL DEFAULT '', token_hash CHAR(64) NOT NULL DEFAULT '', onesignal_external_id VARCHAR(190) NOT NULL DEFAULT '', onesignal_subscription_id VARCHAR(190) NOT NULL DEFAULT '', status VARCHAR(40) NOT NULL DEFAULT 'active', issued_at_utc DATETIME NOT NULL, last_used_at_utc DATETIME NULL, expires_at_utc DATETIME NULL, revoked_at_utc DATETIME NULL, PRIMARY KEY (id), KEY wp_user_id (wp_user_id), KEY account_id (account_id), KEY profile_id (profile_id), KEY device_id (device_id), KEY token_hash (token_hash), KEY status (status) ) $charset_collate;"; $sql[] = "CREATE TABLE {$tables['tracking_sessions']} ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, account_id BIGINT UNSIGNED NOT NULL DEFAULT 0, asset_id BIGINT UNSIGNED NOT NULL DEFAULT 0, profile_id BIGINT UNSIGNED NOT NULL DEFAULT 0, app_token_id BIGINT UNSIGNED NOT NULL DEFAULT 0, status VARCHAR(40) NOT NULL DEFAULT 'active', started_at_utc DATETIME NOT NULL, stopped_at_utc DATETIME NULL, last_ping_at_utc DATETIME NULL, stale_at_utc DATETIME NULL, expired_at_utc DATETIME NULL, stop_reason VARCHAR(100) NOT NULL DEFAULT '', start_lat DECIMAL(10,7) NULL, start_lng DECIMAL(10,7) NULL, last_lat DECIMAL(10,7) NULL, last_lng DECIMAL(10,7) NULL, meta_json LONGTEXT NULL, PRIMARY KEY (id), KEY account_id (account_id), KEY asset_id (asset_id), KEY profile_id (profile_id), KEY app_token_id (app_token_id), KEY status (status), KEY started_at_utc (started_at_utc), KEY last_ping_at_utc (last_ping_at_utc) ) $charset_collate;"; $sql[] = "CREATE TABLE {$tables['location_pings']} ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, session_id BIGINT UNSIGNED NOT NULL DEFAULT 0, account_id BIGINT UNSIGNED NOT NULL DEFAULT 0, asset_id BIGINT UNSIGNED NOT NULL DEFAULT 0, profile_id BIGINT UNSIGNED NOT NULL DEFAULT 0, lat DECIMAL(10,7) NOT NULL, lng DECIMAL(10,7) NOT NULL, accuracy_m DECIMAL(10,2) NULL, speed_mps DECIMAL(10,2) NULL, heading_deg DECIMAL(10,2) NULL, altitude_m DECIMAL(10,2) NULL, source VARCHAR(40) NOT NULL DEFAULT 'mobile_app', device_timestamp_utc DATETIME NULL, server_received_at_utc DATETIME NOT NULL, meta_json LONGTEXT NULL, PRIMARY KEY (id), KEY session_id (session_id), KEY account_id (account_id), KEY asset_id (asset_id), KEY profile_id (profile_id), KEY server_received_at_utc (server_received_at_utc) ) $charset_collate;"; $sql[] = "CREATE TABLE {$tables['last_locations']} ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, account_id BIGINT UNSIGNED NOT NULL DEFAULT 0, asset_id BIGINT UNSIGNED NOT NULL DEFAULT 0, profile_id BIGINT UNSIGNED NOT NULL DEFAULT 0, session_id BIGINT UNSIGNED NOT NULL DEFAULT 0, lat DECIMAL(10,7) NOT NULL, lng DECIMAL(10,7) NOT NULL, accuracy_m DECIMAL(10,2) NULL, speed_mps DECIMAL(10,2) NULL, heading_deg DECIMAL(10,2) NULL, status VARCHAR(40) NOT NULL DEFAULT 'live', last_ping_at_utc DATETIME NOT NULL, updated_at_utc DATETIME NOT NULL, PRIMARY KEY (id), UNIQUE KEY asset_id (asset_id), KEY account_id (account_id), KEY profile_id (profile_id), KEY session_id (session_id), KEY status (status), KEY last_ping_at_utc (last_ping_at_utc) ) $charset_collate;"; $sql[] = "CREATE TABLE {$tables['sos_events']} ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, account_id BIGINT UNSIGNED NOT NULL DEFAULT 0, asset_id BIGINT UNSIGNED NOT NULL DEFAULT 0, profile_id BIGINT UNSIGNED NOT NULL DEFAULT 0, session_id BIGINT UNSIGNED NOT NULL DEFAULT 0, status VARCHAR(40) NOT NULL DEFAULT 'new', severity VARCHAR(40) NOT NULL DEFAULT 'sos', lat DECIMAL(10,7) NULL, lng DECIMAL(10,7) NULL, accuracy_m DECIMAL(10,2) NULL, location_type VARCHAR(60) NOT NULL DEFAULT 'current', message LONGTEXT NULL, triggered_at_utc DATETIME NOT NULL, acknowledged_by_user_id BIGINT UNSIGNED NOT NULL DEFAULT 0, acknowledged_at_utc DATETIME NULL, resolved_by_user_id BIGINT UNSIGNED NOT NULL DEFAULT 0, resolved_at_utc DATETIME NULL, meta_json LONGTEXT NULL, PRIMARY KEY (id), KEY account_id (account_id), KEY asset_id (asset_id), KEY profile_id (profile_id), KEY session_id (session_id), KEY status (status), KEY triggered_at_utc (triggered_at_utc) ) $charset_collate;"; $sql[] = "CREATE TABLE {$tables['sos_event_notes']} ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, sos_event_id BIGINT UNSIGNED NOT NULL DEFAULT 0, user_id BIGINT UNSIGNED NOT NULL DEFAULT 0, note_type VARCHAR(60) NOT NULL DEFAULT 'note', note LONGTEXT NULL, created_at_utc DATETIME NOT NULL, PRIMARY KEY (id), KEY sos_event_id (sos_event_id), KEY user_id (user_id), KEY note_type (note_type), KEY created_at_utc (created_at_utc) ) $charset_collate;"; $sql[] = "CREATE TABLE {$tables['notification_logs']} ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, account_id BIGINT UNSIGNED NOT NULL DEFAULT 0, asset_id BIGINT UNSIGNED NOT NULL DEFAULT 0, profile_id BIGINT UNSIGNED NOT NULL DEFAULT 0, sos_event_id BIGINT UNSIGNED NOT NULL DEFAULT 0, channel VARCHAR(40) NOT NULL DEFAULT '', recipient VARCHAR(190) NOT NULL DEFAULT '', subject VARCHAR(190) NOT NULL DEFAULT '', status VARCHAR(40) NOT NULL DEFAULT 'pending', provider VARCHAR(60) NOT NULL DEFAULT '', provider_ref VARCHAR(190) NOT NULL DEFAULT '', error_message LONGTEXT NULL, sent_at_utc DATETIME NULL, created_at_utc DATETIME NOT NULL, PRIMARY KEY (id), KEY account_id (account_id), KEY asset_id (asset_id), KEY profile_id (profile_id), KEY sos_event_id (sos_event_id), KEY channel (channel), KEY status (status), KEY created_at_utc (created_at_utc) ) $charset_collate;"; $sql[] = "CREATE TABLE {$tables['audit_logs']} ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, user_id BIGINT UNSIGNED NOT NULL DEFAULT 0, account_id BIGINT UNSIGNED NOT NULL DEFAULT 0, asset_id BIGINT UNSIGNED NOT NULL DEFAULT 0, profile_id BIGINT UNSIGNED NOT NULL DEFAULT 0, event_type VARCHAR(100) NOT NULL DEFAULT '', event_label VARCHAR(190) NOT NULL DEFAULT '', ip_hash CHAR(64) NOT NULL DEFAULT '', user_agent_hash CHAR(64) NOT NULL DEFAULT '', details_json LONGTEXT NULL, created_at_utc DATETIME NOT NULL, PRIMARY KEY (id), KEY user_id (user_id), KEY account_id (account_id), KEY asset_id (asset_id), KEY profile_id (profile_id), KEY event_type (event_type), KEY created_at_utc (created_at_utc) ) $charset_collate;"; foreach ($sql as $statement) { dbDelta($statement); } } public static function tables() { global $wpdb; return array( 'accounts' => $wpdb->prefix . 'jat_accounts', 'profiles' => $wpdb->prefix . 'jat_profiles', 'assets' => $wpdb->prefix . 'jat_assets', 'asset_assignments' => $wpdb->prefix . 'jat_asset_assignments', 'qr_tokens' => $wpdb->prefix . 'jat_qr_tokens', 'sos_contacts' => $wpdb->prefix . 'jat_sos_contacts', 'app_tokens' => $wpdb->prefix . 'jat_app_tokens', 'tracking_sessions' => $wpdb->prefix . 'jat_tracking_sessions', 'location_pings' => $wpdb->prefix . 'jat_location_pings', 'last_locations' => $wpdb->prefix . 'jat_last_locations', 'sos_events' => $wpdb->prefix . 'jat_sos_events', 'sos_event_notes' => $wpdb->prefix . 'jat_sos_event_notes', 'notification_logs' => $wpdb->prefix . 'jat_notification_logs', 'audit_logs' => $wpdb->prefix . 'jat_audit_logs', ); } public static function utc_now_mysql() { return gmdate('Y-m-d H:i:s'); } public static function format_jamaica_time($utc_mysql, $format = 'M j, Y g:i A') { $utc_mysql = trim((string) $utc_mysql); if ($utc_mysql === '' || $utc_mysql === '0000-00-00 00:00:00') { return ''; } try { $dt = new DateTimeImmutable($utc_mysql, new DateTimeZone('UTC')); $dt = $dt->setTimezone(new DateTimeZone(self::TIMEZONE)); return $dt->format($format) . ' Jamaica time'; } catch (Exception $e) { return ''; } } public static function current_user_can_any(array $caps) { foreach ($caps as $cap) { if (current_user_can($cap)) { return true; } } return false; } public static function privacy_hash($value) { $value = (string) $value; if ($value === '') { return ''; } return hash_hmac('sha256', $value, wp_salt('auth')); } } JA_Tracker_Core_Installer_v101::init(); endif; if (!function_exists('jat_tables')) { function jat_tables() { if (class_exists('JA_Tracker_Core_Installer_v101')) { return JA_Tracker_Core_Installer_v101::tables(); } return array(); } } if (!function_exists('jat_utc_now_mysql')) { function jat_utc_now_mysql() { if (class_exists('JA_Tracker_Core_Installer_v101')) { return JA_Tracker_Core_Installer_v101::utc_now_mysql(); } return gmdate('Y-m-d H:i:s'); } } if (!function_exists('jat_format_jamaica_time')) { function jat_format_jamaica_time($utc_mysql, $format = 'M j, Y g:i A') { if (class_exists('JA_Tracker_Core_Installer_v101')) { return JA_Tracker_Core_Installer_v101::format_jamaica_time($utc_mysql, $format); } return ''; } } if (!function_exists('jat_current_user_can_any')) { function jat_current_user_can_any(array $caps) { if (class_exists('JA_Tracker_Core_Installer_v101')) { return JA_Tracker_Core_Installer_v101::current_user_can_any($caps); } foreach ($caps as $cap) { if (current_user_can($cap)) { return true; } } return false; } } if (!function_exists('jat_privacy_hash')) { function jat_privacy_hash($value) { if (class_exists('JA_Tracker_Core_Installer_v101')) { return JA_Tracker_Core_Installer_v101::privacy_hash($value); } return hash('sha256', (string) $value); } }