Jake Vanderwerf
2026-03-08 c19264ac916707096fe294d996a1b7fb85206b34
inc/registry/CheckCustomTables.php
@@ -6,6 +6,7 @@
}
use Exception;
use JVBase\registrar\Registrar;
class CheckCustomTables
{
@@ -35,7 +36,7 @@
      //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', []);
@@ -103,7 +104,12 @@
         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 {
@@ -113,10 +119,9 @@
//                $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)) {
@@ -126,13 +131,13 @@
            error_log("JVB: Error in notification section: " . $e->getMessage());
         }
         try {
//       try {
//          if (array_key_exists('dashboard', $this->JVB_SITE) && $this->JVB_SITE['dashboard']) {
               $tables = array_merge($tables, $this->queueTables(), $this->errorLogTables());
//             $tables = array_merge($tables, $this->queueTables(), $this->errorLogTables());
//          }
         } catch (Exception $e) {
            error_log("JVB: Error in dashboard section: " . $e->getMessage());
         }
//       } catch (Exception $e) {
//          error_log("JVB: Error in dashboard section: " . $e->getMessage());
//       }
         try {
            if (array_key_exists('referrals', $this->JVB_SITE) && $this->JVB_SITE['referrals']) {
@@ -208,9 +213,7 @@
               $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;
@@ -295,17 +298,19 @@
         // 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...');
@@ -436,77 +441,78 @@
     * Table Definitions
     *****************************************************/
    protected function queueTables():array
    {
        return [
      '_operation_queue' => "(
            `id` VARCHAR(64) NOT NULL,
            `type` varchar(50) NOT NULL,
            `user_id` {$this->userIDType} NOT NULL,
            `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('high', 'normal', 'low') DEFAULT 'normal',
            `state` enum('pending', 'scheduled', 'processing', 'completed') DEFAULT 'pending',
            `outcome` enum('pending', 'success', 'partial', 'failed','failed_permanent') DEFAULT 'pending',
         `retries` int(11) DEFAULT 0,
            `last_error_hash` CHAR(32) DEFAULT NULL,
            `error_message` text,
         `scheduled_at` datetime DEFAULT CURRENT_TIMESTAMP,
            `started_at` datetime DEFAULT CURRENT_TIMESTAMP,
            `completed_at` datetime DEFAULT NULL,
            `metadata` JSON DEFAULT NULL,
            `result` JSON,
            `dependencies` JSON,
            `user_dismissed` tinyint(1) DEFAULT 0,
            `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 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)
        )"
        ];
    }
//    protected function queueTables():array
//    {
//
//        return [
//    '_operation_queue' => "(
//            `id` VARCHAR(64) NOT NULL,
//            `type` varchar(50) NOT NULL,
//            `user_id` {$this->userIDType} NOT NULL,
//
//            `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('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,
//
//            `metadata` JSON DEFAULT NULL,
//            `result` JSON,
//            `dependencies` JSON,
//            `merged_into` VARCHAR(64) DEFAULT NULL,
//
//            `user_dismissed` tinyint(1) DEFAULT 0,
//            `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 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)
//        )"
//        ];
//    }
   protected function errorLogTables():array
   {
@@ -1153,42 +1159,60 @@
        ];
    }
    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)
    {
@@ -1496,7 +1520,7 @@
      }
      try {
         $contentFields = jvbGetFields($type);
         $contentFields = Registrar::getFieldsFor($type);
         if (!$contentFields || empty($contentFields)) {
            return;