| | |
| | | //Fetch the constants manually, in case they're not defined yet? |
| | | $this->JVB_SITE = apply_filters('jvb_site', []); |
| | | $this->JVB_MEMBERSHIP = apply_filters('jvb_membership', []); |
| | | $this->JVB_CONTENT = apply_filters('jvb_content', []); |
| | | $this->JVB_CONTENT = apply_filters('jvb_content', []); |
| | | $this->JVB_TAXONOMY = apply_filters('jvb_taxonomy', []); |
| | | $this->JVB_USER = apply_filters('jvb_user', []); |
| | | |
| | |
| | | $this->userIDType = $this->getColumnType($this->userTable, 'ID'); |
| | | $this->termIDType = $this->getColumnType($this->wpdb->terms, 'term_id'); |
| | | $this->postIDType = $this->getColumnType($this->wpdb->posts, 'ID'); |
| | | error_log("JVB FK Types: users.ID={$this->userIDType}, terms.term_id={$this->termIDType}, posts.ID={$this->postIDType}"); |
| | | } |
| | | |
| | | protected function getMultisiteUsersTable():string |
| | |
| | | error_log('JVB: Starting table creation process'); |
| | | error_log('JVB: Memory usage at start: ' . memory_get_usage(true) / 1024 / 1024 . ' MB'); |
| | | |
| | | $tables = $calendar = $integrations = $karma = $stats = $invitable = $verifyEntry = $approval = $trackChanges = []; |
| | | $tables = $calendar = $integrations = $karma = $stats = $verifyEntry = $approval = $trackChanges = []; |
| | | $invitable = [ |
| | | 'roles' => [], |
| | | 'terms' => [] |
| | | ]; |
| | | |
| | | |
| | | // Basic tables (these worked fine) |
| | | try { |
| | |
| | | // $tables = array_merge($tables, $this->umamiTracking()); |
| | | // } |
| | | } |
| | | if (array_key_exists('can_invite', $this->JVB_MEMBERSHIP) && is_array($this->JVB_MEMBERSHIP['can_invite'])) { |
| | | foreach ($this->JVB_MEMBERSHIP['can_invite'] as $role => $canInvite) { |
| | | $invitable[$role]['can_invite'] = $canInvite; |
| | | } |
| | | if (array_key_exists('can_invite', $this->JVB_MEMBERSHIP) && |
| | | is_array($this->JVB_MEMBERSHIP['can_invite'])) { |
| | | $invitable['roles'] = $this->JVB_MEMBERSHIP['can_invite']; |
| | | } |
| | | |
| | | // if (jvbCheck('social', $this->JVB_SITE) || jvbCheck('gmb', $this->JVB_SITE) || jvbCheck('square', $this->JVB_SITE) || jvbCheck('helcim', $this->JVB_SITE)) { |
| | |
| | | $trackChanges[$type] = $config; |
| | | } |
| | | if (array_key_exists('invitable', $config) && $config['invitable']) { |
| | | foreach ($config['for_content'] as $content) { |
| | | $invitable[$content]['to_terms'][] = $type; |
| | | } |
| | | $invitable['terms'][] = $type; |
| | | } |
| | | if (array_key_exists('verify_entry', $config) && $config['verify_entry']) { |
| | | $verifyEntry[$type] = $config; |
| | |
| | | |
| | | // RE-ENABLE other table types |
| | | try { |
| | | if (!empty($invitable)) { |
| | | error_log('JVB: Creating invitation tables...'); |
| | | if (!empty($invitable['roles']) || !empty($invitable['terms'])) { |
| | | error_log('JVB: Creating invitation table...'); |
| | | $invitationTables = $this->invitationTables($invitable); |
| | | error_log('JVB: Invitation tables created: ' . count($invitationTables)); |
| | | error_log('JVB: Invitation table created: ' . count($invitationTables)); |
| | | $tables = array_merge($tables, $invitationTables); |
| | | error_log('JVB: Memory after invitations: ' . memory_get_usage(true) / 1024 / 1024 . ' MB'); |
| | | } |
| | | } catch (Exception $e) { |
| | | error_log("JVB: Error creating invitation tables: " . $e->getMessage()); |
| | | error_log("JVB: Error creating invitation table: " . $e->getMessage()); |
| | | } |
| | | |
| | | // Store config for later use |
| | | update_option(BASE.'invitation_config', $invitable); |
| | | |
| | | try { |
| | | if (!empty($approval)) { |
| | | error_log('JVB: Creating approval tables...'); |
| | |
| | | } |
| | | } |
| | | |
| | | public function createTables(array $tables) |
| | | { |
| | | $charset_collate = $this->wpdb->get_charset_collate(); |
| | | require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); |
| | | public function createTables(array $tables) |
| | | { |
| | | require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); |
| | | |
| | | $errors = []; |
| | | $created = []; |
| | | // Match collation from existing WP tables for FK compatibility |
| | | $usersStatus = $this->wpdb->get_row("SHOW TABLE STATUS LIKE '{$this->wpdb->users}'"); |
| | | $parentCollation = $usersStatus->Collation ?? 'utf8mb4_general_ci'; |
| | | $charset_collate = "DEFAULT CHARACTER SET utf8mb4 COLLATE {$parentCollation}"; |
| | | |
| | | foreach ($tables as $name => $schema) { |
| | | $table_name = $this->prefix . BASE . $name; |
| | | $full_schema = "CREATE TABLE IF NOT EXISTS {$table_name} {$schema} {$charset_collate};"; |
| | | error_log("JVB: Using charset_collate: " . $charset_collate); |
| | | |
| | | // Clear any previous errors |
| | | $this->wpdb->flush(); |
| | | $errors = []; |
| | | $created = []; |
| | | |
| | | try { |
| | | $result = dbDelta($full_schema); |
| | | foreach ($tables as $name => $schema) { |
| | | $table_name = $this->prefix . BASE . $name; |
| | | |
| | | // Check for SQL errors |
| | | if ($this->wpdb->last_error) { |
| | | $error_msg = "SQL Error creating table {$table_name}: " . $this->wpdb->last_error; |
| | | error_log($error_msg); |
| | | error_log("Failed SQL Query: " . $full_schema); |
| | | $errors[] = $error_msg; |
| | | // Skip if exists |
| | | if ($this->wpdb->get_var("SHOW TABLES LIKE '{$table_name}'")) { |
| | | $created[] = $table_name . ' (exists)'; |
| | | continue; |
| | | } |
| | | |
| | | // Don't throw exception, just log and continue |
| | | continue; |
| | | } |
| | | $full_schema = "CREATE TABLE IF NOT EXISTS {$table_name} {$schema} {$charset_collate}"; |
| | | |
| | | // Verify table was actually created |
| | | $table_exists = $this->wpdb->get_var("SHOW TABLES LIKE '{$table_name}'"); |
| | | if ($table_exists) { |
| | | $created[] = $table_name; |
| | | error_log("Successfully created table: {$table_name}"); |
| | | } else { |
| | | $error_msg = "Table {$table_name} was not created (no error reported)"; |
| | | error_log($error_msg); |
| | | error_log("Schema used: " . $full_schema); |
| | | $errors[] = $error_msg; |
| | | } |
| | | $this->wpdb->flush(); |
| | | |
| | | } catch (Exception $e) { |
| | | $error_msg = "Exception creating table {$table_name}: " . $e->getMessage(); |
| | | error_log($error_msg); |
| | | error_log("Exception SQL Query: " . $full_schema); |
| | | $errors[] = $error_msg; |
| | | } |
| | | } |
| | | // Use direct query - dbDelta mangles FK constraints |
| | | $hasForeignKey = stripos($schema, 'FOREIGN KEY') !== false; |
| | | |
| | | // Log summary |
| | | if (!empty($created)) { |
| | | error_log("JVB Tables Created Successfully: " . implode(', ', $created)); |
| | | } |
| | | if ($hasForeignKey) { |
| | | $result = $this->wpdb->query($full_schema); |
| | | $success = ($result !== false && !$this->wpdb->last_error); |
| | | } else { |
| | | dbDelta($full_schema . ';'); |
| | | $success = !$this->wpdb->last_error; |
| | | } |
| | | |
| | | if (!empty($errors)) { |
| | | error_log("JVB Table Creation Errors (" . count($errors) . " total):"); |
| | | foreach ($errors as $error) { |
| | | error_log(" - " . $error); |
| | | } |
| | | if (!$success) { |
| | | $error_msg = "SQL Error creating table {$table_name}: " . $this->wpdb->last_error; |
| | | error_log($error_msg); |
| | | error_log("Failed SQL Query: " . $full_schema); |
| | | $errors[] = $error_msg; |
| | | } elseif ($this->wpdb->get_var("SHOW TABLES LIKE '{$table_name}'")) { |
| | | $created[] = $table_name; |
| | | error_log("Successfully created table: {$table_name}"); |
| | | } |
| | | } |
| | | |
| | | // Optionally store errors for admin display |
| | | update_option(BASE . 'table_creation_errors', $errors); |
| | | } else { |
| | | // Clear any previous errors |
| | | delete_option(BASE . 'table_creation_errors'); |
| | | } |
| | | } |
| | | // Log summary |
| | | if (!empty($created)) { |
| | | error_log("JVB Tables Created Successfully: " . implode(', ', $created)); |
| | | } |
| | | |
| | | if (!empty($errors)) { |
| | | error_log("JVB Table Creation Errors (" . count($errors) . " total):"); |
| | | foreach ($errors as $error) { |
| | | error_log(" - " . $error); |
| | | } |
| | | update_option(BASE . 'table_creation_errors', $errors); |
| | | } else { |
| | | delete_option(BASE . 'table_creation_errors'); |
| | | } |
| | | } |
| | | |
| | | /****************************************************** |
| | | * Table Definitions |
| | |
| | | `id` VARCHAR(64) NOT NULL, |
| | | `type` varchar(50) NOT NULL, |
| | | `user_id` {$this->userIDType} NOT NULL, |
| | | `request_data` JSON NOT NULL, |
| | | `count` int(11) NOT NULL DEFAULT 1, |
| | | `progress_count` int(11) DEFAULT 0, |
| | | |
| | | `request_data` JSON NOT NULL CHECK (JSON_VALID(request_data)), |
| | | |
| | | `total_items` int(11) NOT NULL DEFAULT 1, |
| | | `processed_items` int(11) DEFAULT 0, |
| | | `failed_items` JSON, |
| | | `priority` enum('low', 'normal', 'high') DEFAULT 'normal', |
| | | `status` enum('pending', 'scheduled', 'processing','failed', 'failed_permanent', 'completed', 'completed_with_errors') DEFAULT 'pending', |
| | | `metadata` JSON DEFAULT NULL, |
| | | `merge` enum('merge', 'append', 'replace') DEFAULT 'merge', |
| | | `scheduled_at` datetime DEFAULT CURRENT_TIMESTAMP, |
| | | `created_at` datetime DEFAULT CURRENT_TIMESTAMP, |
| | | `updated_at` datetime DEFAULT CURRENT_TIMESTAMP, |
| | | `started_at` datetime DEFAULT NULL, |
| | | |
| | | `priority` ENUM('high', 'normal', 'low') DEFAULT 'normal', |
| | | `state` enum('pending', 'scheduled', 'processing', 'completed') DEFAULT 'pending', |
| | | `outcome` enum('pending', 'success', 'partial', 'merged', 'failed', 'failed_permanent') DEFAULT 'pending', |
| | | |
| | | `retries` int(11) DEFAULT 0, |
| | | `last_error_hash` CHAR(32) DEFAULT NULL, |
| | | `error_message` text, |
| | | |
| | | `scheduled_at` datetime DEFAULT NULL, |
| | | `started_at` datetime DEFAULT CURRENT_TIMESTAMP, |
| | | `completed_at` datetime DEFAULT NULL, |
| | | `retries` int(11) DEFAULT 0, |
| | | |
| | | `metadata` JSON DEFAULT NULL, |
| | | `result` JSON, |
| | | `dependencies` JSON, |
| | | `error_message` text, |
| | | `merged_into` VARCHAR(64) DEFAULT NULL, |
| | | |
| | | `user_dismissed` tinyint(1) DEFAULT 0, |
| | | PRIMARY KEY (`id`), |
| | | KEY `idx_status_priority` (`status`, `priority`), |
| | | KEY `idx_user_type` (`user_id`, `type`), |
| | | KEY `idx_created_at` (`created_at`), |
| | | KEY `idx_completed_at` (`completed_at`), |
| | | KEY `idx_user_status` (`user_id`, `status`), |
| | | KEY `idx_retries_status` (`retries`, `status`), |
| | | KEY `idx_user_status_created` (`user_id`, `status`, `created_at`), |
| | | KEY `idx_scheduled_processing` (`scheduled_at`, `status`) |
| | | `created_at` DATETIME DEFAULT CURRENT_TIMESTAMP, |
| | | `updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP, |
| | | PRIMARY KEY (`id`), |
| | | KEY `idx_run_queue` (state, priority, scheduled_at), |
| | | KEY `idx_user_ops` (user_id, state), |
| | | KEY `idx_user_type_pending` (user_id, type, state), |
| | | KEY `idx_completed_at` (completed_at), |
| | | KEY `idx_processing_stuck` (`state`, `started_at`) |
| | | )", |
| | | |
| | | 'stats__operation_queue' => "( |
| | | `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, |
| | | `date` date NOT NULL, |
| | | `type` varchar(50) NOT NULL, |
| | | `total_operations` int NOT NULL DEFAULT 0, |
| | | `successful_operations` int NOT NULL DEFAULT 0, |
| | | `failed_operations` int NOT NULL DEFAULT 0, |
| | | `average_duration` float DEFAULT NULL, |
| | | `total_items_processed` int NOT NULL DEFAULT 0, |
| | | `peak_queue_size` int NOT NULL DEFAULT 0, |
| | | `peak_memory_usage` int DEFAULT NULL, |
| | | `peak_cpu_usage` float DEFAULT NULL, |
| | | `created_at` timestamp DEFAULT CURRENT_TIMESTAMP, |
| | | PRIMARY KEY (`id`), |
| | | UNIQUE KEY (`date`, `type`), |
| | | KEY `date_idx` (`date`), |
| | | KEY `type_idx` (`type`) |
| | | `id` bigint unsigned AUTO_INCREMENT, |
| | | `date` date NOT NULL, |
| | | `type` varchar(50) NOT NULL, |
| | | |
| | | `total_operations` int NOT NULL DEFAULT 0, |
| | | `successful_operations` int NOT NULL DEFAULT 0, |
| | | `partial_operations` int NOT NULL DEFAULT 0, |
| | | `failed_operations` int NOT NULL DEFAULT 0, |
| | | `failed_permanent_operations` int NOT NULL DEFAULT 0, |
| | | |
| | | `total_items_processed` int NOT NULL DEFAULT 0, |
| | | |
| | | `average_duration` float DEFAULT NULL, |
| | | `max_duration` int DEFAULT NULL, |
| | | |
| | | `peak_queue_size` int NOT NULL DEFAULT 0, |
| | | |
| | | `peak_memory_usage` int DEFAULT NULL, |
| | | `peak_cpu_usage` float DEFAULT NULL, |
| | | |
| | | `created_at` timestamp DEFAULT CURRENT_TIMESTAMP, |
| | | PRIMARY KEY (`id`), |
| | | UNIQUE KEY (date, type), |
| | | KEY `date_idx` (date), |
| | | KEY `type_idx` (type) |
| | | )" |
| | | ]; |
| | | } |
| | |
| | | KEY `requires_action` (`owner_id`, `requires_action`, `action_taken`), |
| | | KEY `acting_user_lookup` (`owner_id`, `action_user_id`, `type`, `status`, `created_at`), |
| | | CONSTRAINT `{$this->base}notify_owner` FOREIGN KEY (`owner_id`) |
| | | REFERENCES {$this->userTable} (`ID`) ON DELETE CASCADE, |
| | | REFERENCES `{$this->userTable}` (`ID`) ON DELETE CASCADE, |
| | | CONSTRAINT `{$this->base}action_id` FOREIGN KEY (`action_user_id`) |
| | | REFERENCES {$this->userTable} (`ID`) ON DELETE CASCADE |
| | | REFERENCES `{$this->userTable}` (`ID`) ON DELETE CASCADE |
| | | )", |
| | | |
| | | |
| | |
| | | UNIQUE KEY `user_content_notif` (`user_id`, `content_notification_id`), |
| | | KEY `user_status` (`user_id`, `status`), |
| | | CONSTRAINT `{$this->base}user_content_user` FOREIGN KEY (`user_id`) |
| | | REFERENCES {$this->userTable} (`ID`) ON DELETE CASCADE, |
| | | REFERENCES `{$this->userTable}` (`ID`) ON DELETE CASCADE, |
| | | CONSTRAINT `{$this->base}user_content_notification` FOREIGN KEY (`content_notification_id`) |
| | | REFERENCES `{$this->prefixed}notifications_content` (`id`) ON DELETE CASCADE |
| | | )", |
| | |
| | | KEY `user_frequency` (`user_id`, `frequency`), |
| | | KEY `frequency_lookup` (`frequency`, `last_sent`), |
| | | CONSTRAINT `{$this->base}notification_pref_user` FOREIGN KEY (`user_id`) |
| | | REFERENCES {$this->userTable} (`ID`) ON DELETE CASCADE |
| | | REFERENCES `{$this->userTable}` (`ID`) ON DELETE CASCADE |
| | | )", |
| | | |
| | | // Notification digest scheduling and tracking |
| | |
| | | KEY `scheduled_digests` (`frequency`, `scheduled_at`, `status`), |
| | | KEY `user_digests` (`user_id`, `frequency`), |
| | | CONSTRAINT `{$this->base}digest_user` FOREIGN KEY (`user_id`) |
| | | REFERENCES {$this->userTable} (`ID`) ON DELETE CASCADE |
| | | REFERENCES `{$this->userTable}` (`ID`) ON DELETE CASCADE |
| | | )", |
| | | |
| | | // Analytics on notification interactions |
| | |
| | | KEY `user_actions` (`user_id`, `action`), |
| | | KEY `action_analysis` (`action`, `action_source`), |
| | | CONSTRAINT `{$this->base}metrics_user` FOREIGN KEY (`user_id`) |
| | | REFERENCES {$this->userTable} (`ID`) ON DELETE CASCADE, |
| | | REFERENCES `{$this->userTable}` (`ID`) ON DELETE CASCADE, |
| | | CONSTRAINT `{$this->base}metrics_notification` FOREIGN KEY (`notification_id`) |
| | | REFERENCES {$this->prefixed}notifications (`id`) ON DELETE CASCADE |
| | | REFERENCES `{$this->prefixed}notifications` (`id`) ON DELETE CASCADE |
| | | )" |
| | | ]; |
| | | } |
| | |
| | | KEY `status` (`status`), |
| | | KEY `expiring_requests` (`status`, `expires_at`), |
| | | CONSTRAINT `{$this->base}{$type}_approval_requester` FOREIGN KEY (`user_id`) |
| | | REFERENCES {$this->userTable} (`ID`) ON DELETE CASCADE |
| | | REFERENCES `{$this->userTable}` (`ID`) ON DELETE CASCADE |
| | | )"; |
| | | $tables['approval_'.$type.'_votes'] = "( |
| | | `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, |
| | |
| | | UNIQUE KEY `unique_vote` (`request_id`, `user_id`), |
| | | KEY `user_votes` (`user_id`), |
| | | CONSTRAINT `{$this->base}{$type}_user_approval_request` FOREIGN KEY (`request_id`) |
| | | REFERENCES {$this->prefixed}approval_{$type}_requests (`id`) ON DELETE CASCADE, |
| | | REFERENCES `{$this->prefixed}approval_{$type}_requests` (`id`) ON DELETE CASCADE, |
| | | CONSTRAINT `{$this->base}{$type}_user_approval_voter` FOREIGN KEY (`user_id`) |
| | | REFERENCES {$this->userTable} (`ID`) ON DELETE CASCADE |
| | | REFERENCES `{$this->userTable}` (`ID`) ON DELETE CASCADE |
| | | )"; |
| | | } |
| | | if (!empty($save)) { |
| | |
| | | KEY `related_taxonomy` (`related_taxonomy`), |
| | | UNIQUE KEY `term_relation` (`term_id`, `related_term_id`, `taxonomy`, `related_taxonomy`), |
| | | CONSTRAINT `{$this->base}tax_rel_term_id` FOREIGN KEY (`term_id`) |
| | | REFERENCES {$this->wpdb->terms} (`term_id`) ON DELETE CASCADE, |
| | | REFERENCES `{$this->wpdb->terms}` (`term_id`) ON DELETE CASCADE, |
| | | CONSTRAINT `{$this->base}tax_rel_related_id` FOREIGN KEY (`related_term_id`) |
| | | REFERENCES {$this->wpdb->terms} (`term_id`) ON DELETE CASCADE |
| | | REFERENCES `{$this->wpdb->terms}` (`term_id`) ON DELETE CASCADE |
| | | )" |
| | | ]; |
| | | |
| | |
| | | KEY `user_taxonomy` (`user_id`, `taxonomy`), |
| | | KEY `taxonomy` (`taxonomy`), |
| | | CONSTRAINT `{$this->base}user_term_user_fk` FOREIGN KEY (`user_id`) |
| | | REFERENCES {$this->userTable} (`ID`) ON DELETE CASCADE, |
| | | REFERENCES `{$this->userTable}` (`ID`) ON DELETE CASCADE, |
| | | CONSTRAINT `{$this->base}user_term_term_fk` FOREIGN KEY (`term_id`) |
| | | REFERENCES {$this->wpdb->terms} (`term_id`) ON DELETE CASCADE |
| | | REFERENCES `{$this->wpdb->terms}` (`term_id`) ON DELETE CASCADE |
| | | )"; |
| | | } |
| | | |
| | |
| | | KEY `user_type` (`user_id`, `type`), |
| | | KEY `target_type` (`target_id`, `type`), |
| | | CONSTRAINT `{$this->base}favourites_user` FOREIGN KEY (`user_id`) |
| | | REFERENCES {$this->userTable} (`ID`) ON DELETE CASCADE |
| | | REFERENCES `{$this->userTable}` (`ID`) ON DELETE CASCADE |
| | | )", |
| | | 'favourites_lists' => "( |
| | | `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, |
| | |
| | | PRIMARY KEY (`id`), |
| | | KEY `user_lists` (`user_id`), |
| | | CONSTRAINT `{$this->base}list_user` FOREIGN KEY (`user_id`) |
| | | REFERENCES {$this->userTable} (`ID`) ON DELETE CASCADE |
| | | REFERENCES `{$this->userTable}` (`ID`) ON DELETE CASCADE |
| | | )", |
| | | 'favourites_list_items' => "( |
| | | `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, |
| | |
| | | KEY `list_items` (`list_id`), |
| | | KEY `favourite_id` (`favourite_id`), |
| | | CONSTRAINT `{$this->base}list_items` FOREIGN KEY (`list_id`) |
| | | REFERENCES {$this->prefixed}favourites_lists (`id`) ON DELETE CASCADE, |
| | | REFERENCES `{$this->prefixed}favourites_lists` (`id`) ON DELETE CASCADE, |
| | | CONSTRAINT `{$this->base}list_favourite` FOREIGN KEY (`favourite_id`) |
| | | REFERENCES {$this->prefixed}favourites (`id`) ON DELETE SET NULL |
| | | REFERENCES `{$this->prefixed}favourites` (`id`) ON DELETE SET NULL |
| | | )", |
| | | 'favourites_list_shares' => "( |
| | | `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, |
| | |
| | | KEY `list_shares` (`list_id`), |
| | | KEY `status_index` (`status`), |
| | | CONSTRAINT `{$this->base}share_list` FOREIGN KEY (`list_id`) |
| | | REFERENCES {$this->prefixed}favourites_lists (`id`) ON DELETE CASCADE, |
| | | REFERENCES `{$this->prefixed}favourites_lists` (`id`) ON DELETE CASCADE, |
| | | CONSTRAINT `{$this->base}share_user` FOREIGN KEY (`user_id`) |
| | | REFERENCES {$this->userTable} (`ID`) ON DELETE CASCADE |
| | | REFERENCES `{$this->userTable}` (`ID`) ON DELETE CASCADE |
| | | )", |
| | | 'favourites_list_stats' => "( |
| | | `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, |
| | |
| | | KEY `user_id` (`user_id`), |
| | | KEY `artist_id` (`artist_id`), |
| | | CONSTRAINT `{$this->base}nr_shop_news_shop` FOREIGN KEY (`shop_id`) |
| | | REFERENCES {$this->wpdb->terms} (`term_id`) ON DELETE CASCADE, |
| | | REFERENCES `{$this->wpdb->terms}` (`term_id`) ON DELETE CASCADE, |
| | | CONSTRAINT `{$this->base}nr_shop_news_user` FOREIGN KEY (`user_id`) |
| | | REFERENCES {$this->userTable} (`ID`) ON DELETE CASCADE, |
| | | REFERENCES `{$this->userTable}` (`ID`) ON DELETE CASCADE, |
| | | CONSTRAINT `{$this->base}nr_shop_news_artist` FOREIGN KEY (`artist_id`) |
| | | REFERENCES {$this->wpdb->posts} (`ID`) ON DELETE SET NULL |
| | | REFERENCES `{$this->wpdb->posts}` (`ID`) ON DELETE SET NULL |
| | | )" |
| | | ]; |
| | | } |
| | |
| | | KEY `parent_child` (`parent_id`), |
| | | KEY `karma_order` (`karma`), |
| | | CONSTRAINT `{$this->base}re_responses_news` FOREIGN KEY (`item_id`) |
| | | REFERENCES {$this->wpdb->posts} (`ID`) ON DELETE CASCADE, |
| | | REFERENCES `{$this->wpdb->posts}` (`ID`) ON DELETE CASCADE, |
| | | CONSTRAINT `{$this->base}re_responses_parent` FOREIGN KEY (`parent_id`) |
| | | REFERENCES {$this->prefixed}responses (`id`) ON DELETE SET NULL |
| | | REFERENCES `{$this->prefixed}responses` (`id`) ON DELETE SET NULL |
| | | )", |
| | | 'karma_response' => "( |
| | | `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, |
| | |
| | | KEY `item_id` (`item_id`), |
| | | KEY `user_id` (`user_id`), |
| | | CONSTRAINT `{$this->base}_response_item_id` FOREIGN KEY (`item_id`) |
| | | REFERENCES {$this->prefixed}responses (`id`) ON DELETE CASCADE, |
| | | REFERENCES `{$this->prefixed}responses` (`id`) ON DELETE CASCADE, |
| | | CONSTRAINT `{$this->base}_response_user_id` FOREIGN KEY (`user_id`) |
| | | REFERENCES {$this->userTable} (`ID`) ON DELETE CASCADE |
| | | REFERENCES `{$this->userTable}` (`ID`) ON DELETE CASCADE |
| | | )" |
| | | ]; |
| | | } |
| | |
| | | KEY `item_id` (`item_id`), |
| | | KEY `user_id` (`user_id`), |
| | | CONSTRAINT `{$this->base}kt_{$type}_item_id` FOREIGN KEY (`item_id`) |
| | | REFERENCES {$reference_table} ({$reference_column}) ON DELETE CASCADE, |
| | | REFERENCES `{$reference_table}` (`{$reference_column}`) ON DELETE CASCADE, |
| | | CONSTRAINT `{$this->base}kt_{$type}_user_id` FOREIGN KEY (`user_id`) |
| | | REFERENCES {$this->userTable} (`ID`) ON DELETE CASCADE |
| | | REFERENCES `{$this->userTable}` (`ID`) ON DELETE CASCADE |
| | | )"; |
| | | |
| | | } |
| | |
| | | KEY `rsvp_events` (`rsvp_required`, `rsvp_deadline`), |
| | | |
| | | CONSTRAINT `{$this->base}cal_{$type}_type` FOREIGN KEY (`event_type`) |
| | | REFERENCES {$this->wpdb->terms} (`term_id`) ON DELETE CASCADE, |
| | | REFERENCES `{$this->wpdb->terms}` (`term_id`) ON DELETE CASCADE, |
| | | CONSTRAINT `{$this->base}cal_{$type}_post` FOREIGN KEY (`post_id`) |
| | | REFERENCES {$this->wpdb->posts} (`ID`) ON DELETE CASCADE, |
| | | REFERENCES `{$this->wpdb->posts}` (`ID`) ON DELETE CASCADE, |
| | | CONSTRAINT `{$this->base}cal_{$type}_shop` FOREIGN KEY (`shop_id`) |
| | | REFERENCES {$this->wpdb->terms} (`term_id`) ON DELETE SET NULL, |
| | | REFERENCES `{$this->wpdb->terms}` (`term_id`) ON DELETE SET NULL, |
| | | CONSTRAINT `{$this->base}cal_{$type}_user` FOREIGN KEY (`user_id`) |
| | | REFERENCES {$this->userTable} (`ID`) ON DELETE SET NULL |
| | | REFERENCES `{$this->userTable}` (`ID`) ON DELETE SET NULL |
| | | )"; |
| | | |
| | | $tables['calendar_'.$type.'_participants'] = "( |
| | |
| | | PRIMARY KEY (`id`), |
| | | UNIQUE KEY `event_user` (`event_id`, `user_id`), |
| | | CONSTRAINT `{$this->base}cal_{$type}_participant_event` FOREIGN KEY (`event_id`) |
| | | REFERENCES {$this->prefixed}calendar_{$type} (`id`) ON DELETE CASCADE, |
| | | REFERENCES `{$this->prefixed}calendar_{$type}` (`id`) ON DELETE CASCADE, |
| | | CONSTRAINT `{$this->base}cal_{$type}_participant_user` FOREIGN KEY (`user_id`) |
| | | REFERENCES {$this->userTable} (`ID`) ON DELETE CASCADE |
| | | REFERENCES `{$this->userTable}` (`ID`) ON DELETE CASCADE |
| | | )"; |
| | | |
| | | $tables['calendar_'.$type.'_recurrence_exceptions'] = "( |
| | |
| | | KEY `user_idx` (`user_id`), |
| | | KEY `owner_idx` (`owner_id`), |
| | | CONSTRAINT `{$this->base}umami_user_id_link` FOREIGN KEY (`user_id`) |
| | | REFERENCES {$this->userTable} (`ID`) ON DELETE CASCADE, |
| | | REFERENCES `{$this->userTable}` (`ID`) ON DELETE CASCADE, |
| | | CONSTRAINT `{$this->base}umami_owner_id_link` FOREIGN KEY (`owner_id`) |
| | | REFERENCES {$this->userTable} (`ID`) ON DELETE CASCADE |
| | | REFERENCES `{$this->userTable}` (`ID`) ON DELETE CASCADE |
| | | )", |
| | | 'stats_performance' => "( |
| | | `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, |
| | |
| | | PRIMARY KEY (`id`), |
| | | KEY `user_date_idx` (`user_id`, `date`), |
| | | CONSTRAINT `{$this->base}performance_user_id_link` FOREIGN KEY (`user_id`) |
| | | REFERENCES {$this->userTable} (`ID`) ON DELETE CASCADE |
| | | REFERENCES `{$this->userTable}` (`ID`) ON DELETE CASCADE |
| | | )" |
| | | ]; |
| | | } |
| | | |
| | | protected function invitationTables($types) |
| | | { |
| | | |
| | | $tables = []; |
| | | foreach ($types as $role => $config) { |
| | | $definitions = "( |
| | | `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, |
| | | `name` varchar(255) NOT NULL, |
| | | `email` varchar(255) NOT NULL, |
| | | `invitation_token` varchar(64) NOT NULL, |
| | | `status` enum('pending', 'accepted', 'rejected', 'expired','revoked') DEFAULT 'pending', |
| | | `inviters` JSON NOT NULL,"; |
| | | foreach($config['to_terms']??[] as $term) { |
| | | $definitions .= "`to_{$term}` {$this->termIDType} DEFAULT NULL,"; |
| | | } |
| | | $definitions .= "`new_user_id` bigint(20) NOT NULL, |
| | | `expires_at` datetime NOT NULL, |
| | | `accepted_at` datetime 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 `unique_email` (`email`), |
| | | KEY `token_lookup` (`invitation_token`), |
| | | KEY `status_expiry` (`status`, `expires_at`), |
| | | KEY `name_status` (`name`, `status`) |
| | | )"; |
| | | foreach($config['to_terms']??[] as $term) { |
| | | $definitions .= "CONSTRAINT `{$this->base}_{$term}_link` FOREIGN KEY (`to_{$term}`) |
| | | REFERENCES {$this->wpdb->terms} (`term_id`) ON DELETE CASCADE"; |
| | | } |
| | | |
| | | $tables['invitations_'.$role] = $definitions; |
| | | protected function invitationTables(array $config): array |
| | | { |
| | | if (empty($config['roles']) && empty($config['terms'])) { |
| | | return []; |
| | | } |
| | | |
| | | return $tables; |
| | | } |
| | | $definitions = "( |
| | | `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, |
| | | `name` varchar(255) NOT NULL, |
| | | `email` varchar(255) NOT NULL, |
| | | `invitation_token` varchar(255) NOT NULL, |
| | | `invited_role` varchar(50) NOT NULL COMMENT 'Role being invited to', |
| | | `status` enum('pending','accepted','rejected','expired','revoked') DEFAULT 'pending', |
| | | `inviters` JSON NOT NULL COMMENT 'Array of {user_id, invited_at}', |
| | | `new_user_id` {$this->userIDType} DEFAULT NULL, |
| | | `expires_at` datetime NOT NULL, |
| | | `accepted_at` datetime DEFAULT NULL, |
| | | `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, |
| | | `updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, |
| | | "; |
| | | |
| | | // Add term columns for all invitable taxonomies |
| | | foreach ($config['terms'] ?? [] as $taxonomy) { |
| | | $definitions .= "`to_{$taxonomy}` {$this->termIDType} DEFAULT NULL,"; |
| | | } |
| | | |
| | | $definitions .= "PRIMARY KEY (`id`), |
| | | UNIQUE KEY `unique_email_role` (`email`, `invited_role`), |
| | | KEY `token_lookup` (`invitation_token`), |
| | | KEY `status_expiry` (`status`, `expires_at`), |
| | | KEY `role_status` (`invited_role`, `status`), |
| | | KEY `email_status` (`email`, `status`), |
| | | "; |
| | | |
| | | // Add foreign key constraints for terms |
| | | $constraints = []; |
| | | foreach ($config['terms'] ?? [] as $taxonomy) { |
| | | $constraints[] = "CONSTRAINT `{$this->base}invitations_{$taxonomy}_fk` |
| | | FOREIGN KEY (`to_{$taxonomy}`) |
| | | REFERENCES `{$this->wpdb->terms}` (`term_id`) |
| | | ON DELETE SET NULL"; |
| | | } |
| | | |
| | | // Add user foreign key |
| | | $constraints[] = "CONSTRAINT `{$this->base}invitations_user_fk` |
| | | FOREIGN KEY (`new_user_id`) |
| | | REFERENCES `{$this->userTable}` (`ID`) |
| | | ON DELETE SET NULL"; |
| | | |
| | | $definitions .= implode(',', $constraints); |
| | | $definitions .= ")"; |
| | | |
| | | return ['invitations' => $definitions]; |
| | | } |
| | | |
| | | protected function trackChangesTables($types) |
| | | { |
| | |
| | | UNIQUE KEY `content_term` (`content_id`, `term_id`), |
| | | KEY content_role (`term_id`, `role`), |
| | | CONSTRAINT `{$this->base}{$content}_{$type}_history_user` FOREIGN KEY (`user_id`) |
| | | REFERENCES {$this->userTable} (`ID`) ON DELETE CASCADE, |
| | | REFERENCES `{$this->userTable}` (`ID`) ON DELETE CASCADE, |
| | | CONSTRAINT `{$this->base}{$content}_{$type}_history_term` FOREIGN KEY (`term_id`) |
| | | REFERENCES {$this->wpdb->terms} (`term_id`) ON DELETE CASCADE |
| | | REFERENCES `{$this->wpdb->terms}` (`term_id`) ON DELETE CASCADE |
| | | )"; |
| | | } |
| | | } |
| | |
| | | PRIMARY KEY (`id`), |
| | | UNIQUE KEY `{$this->base}content_term` (`content_id`, `term_id`), |
| | | CONSTRAINT `{$this->base}{$content}_{$type}_request_user` FOREIGN KEY (`user_id`) |
| | | REFERENCES {$this->userTable} (`ID`) ON DELETE CASCADE, |
| | | REFERENCES `{$this->userTable}` (`ID`) ON DELETE CASCADE, |
| | | CONSTRAINT `{$this->base}{$content}_{$type}_request_term` FOREIGN KEY (`term_id`) |
| | | REFERENCES {$this->wpdb->terms} (`term_id`) ON DELETE CASCADE |
| | | REFERENCES `{$this->wpdb->terms}` (`term_id`) ON DELETE CASCADE |
| | | )"; |
| | | } |
| | | } |
| | |
| | | KEY `date_idx` (`referred_at`), |
| | | KEY `consult_idx` (`consulted_at`), |
| | | CONSTRAINT `{$this->base}referral_referrer_fk` FOREIGN KEY (`referrer_id`) |
| | | REFERENCES {$this->userTable} (`ID`) ON DELETE CASCADE, |
| | | REFERENCES `{$this->userTable}` (`ID`) ON DELETE CASCADE, |
| | | CONSTRAINT `{$this->base}referral_referee_fk` FOREIGN KEY (`referee_id`) |
| | | REFERENCES {$this->userTable} (`ID`) ON DELETE CASCADE |
| | | REFERENCES `{$this->userTable}` (`ID`) ON DELETE CASCADE |
| | | )"; |
| | | |
| | | // Create the main referrals table first |
| | |
| | | KEY `user_idx` (`user_id`), |
| | | KEY `email_idx` (`email`), |
| | | CONSTRAINT `{$this->base}jane_client_user_fk` FOREIGN KEY (`user_id`) |
| | | REFERENCES {$this->userTable} (`ID`) ON DELETE CASCADE |
| | | REFERENCES `{$this->userTable}` (`ID`) ON DELETE CASCADE |
| | | )"; |
| | | |
| | | // Third: referral_treatments (depends on referrals AND wp_users) |
| | |
| | | KEY `date_idx` (`treatment_date`), |
| | | KEY `type_idx` (`treatment_type`), |
| | | CONSTRAINT `{$this->base}treatment_referral_fk` FOREIGN KEY (`referral_id`) |
| | | REFERENCES {$this->prefixed}referrals (`id`) ON DELETE CASCADE, |
| | | REFERENCES `{$this->prefixed}referrals` (`id`) ON DELETE CASCADE, |
| | | CONSTRAINT `{$this->base}treatment_user_fk` FOREIGN KEY (`user_id`) |
| | | REFERENCES {$this->userTable} (`ID`) ON DELETE CASCADE |
| | | REFERENCES `{$this->userTable}` (`ID`) ON DELETE CASCADE |
| | | )"; |
| | | |
| | | // Fourth: referral_rewards (depends on referrals AND wp_users) |
| | |
| | | KEY `status_idx` (`status`), |
| | | KEY `type_idx` (`reward_type`), |
| | | CONSTRAINT `{$this->base}reward_referral_fk` FOREIGN KEY (`referral_id`) |
| | | REFERENCES {$this->prefixed}referrals (`id`) ON DELETE CASCADE, |
| | | REFERENCES `{$this->prefixed}referrals` (`id`) ON DELETE CASCADE, |
| | | CONSTRAINT `{$this->base}reward_user_fk` FOREIGN KEY (`user_id`) |
| | | REFERENCES {$this->userTable} (`ID`) ON DELETE CASCADE |
| | | REFERENCES `{$this->userTable}` (`ID`) ON DELETE CASCADE |
| | | )"; |
| | | |
| | | return $dependentTables; |
| | |
| | | |
| | | if ($type === 'taxonomy') { |
| | | $constraints[] = "CONSTRAINT `{$constraintName}` FOREIGN KEY (`{$fieldName}`) |
| | | REFERENCES {$this->wpdb->terms} (`term_id`) ON DELETE SET NULL"; |
| | | REFERENCES `{$this->wpdb->terms}` (`term_id`) ON DELETE SET NULL"; |
| | | } elseif ($type === 'user') { |
| | | $constraints[] = "CONSTRAINT `{$constraintName}` FOREIGN KEY (`{$fieldName}`) |
| | | REFERENCES {$this->userTable} (`ID`) ON DELETE SET NULL"; |
| | | REFERENCES `{$this->userTable}` (`ID`) ON DELETE SET NULL"; |
| | | } elseif ($type === 'image' || $type === 'file') { |
| | | $constraints[] = "CONSTRAINT `{$constraintName}` FOREIGN KEY (`{$fieldName}`) |
| | | REFERENCES {$this->wpdb->posts} (`ID`) ON DELETE SET NULL"; |
| | | REFERENCES `{$this->wpdb->posts}` (`ID`) ON DELETE SET NULL"; |
| | | } |
| | | } |
| | | |
| | |
| | | ["PRIMARY KEY (`term_id`)"], |
| | | $indexes, |
| | | $constraints, |
| | | ["CONSTRAINT `{$base}_{$type}_term` FOREIGN KEY (`term_id`) REFERENCES {$this->wpdb->terms} (`term_id`) ON DELETE CASCADE"] |
| | | ["CONSTRAINT `{$base}_{$type}_term` FOREIGN KEY (`term_id`) REFERENCES `{$this->wpdb->terms}` (`term_id`) ON DELETE CASCADE"] |
| | | ); |
| | | $sql = "(\n " . implode(",\n ", $allDefinitions) . "\n)"; |
| | | error_log("JVB: Creating content table for type: {$type}"); |
| | |
| | | |
| | | $constraints = [ |
| | | "CONSTRAINT `{$this->base}_{$userType}_stats_user` FOREIGN KEY (`user_id`) |
| | | REFERENCES {$this->userTable} (`ID`) ON DELETE CASCADE" |
| | | REFERENCES `{$this->userTable}` (`ID`) ON DELETE CASCADE" |
| | | ]; |
| | | |
| | | $allDefinitions = array_merge($columns, $indexes, $constraints); |