false, 'error' => 'User not found'); } // Generate extension if not provided if (!$extension) { $extension = self::generate_unique_extension(); } // Check if extension already exists $existing = $wpdb->get_var($wpdb->prepare( "SELECT extension FROM {$wpdb->prefix}twp_user_extensions WHERE extension = %s", $extension )); if ($existing) { return array('success' => false, 'error' => 'Extension already exists'); } // Create personal queue $personal_queue_name = sprintf('%s (%s)', $user->display_name, $extension); $personal_queue_id = $wpdb->insert( $wpdb->prefix . 'twp_call_queues', array( 'queue_name' => $personal_queue_name, 'queue_type' => 'personal', 'user_id' => $user_id, 'extension' => $extension, 'max_size' => 10, 'timeout_seconds' => 300, // 5 minutes for logged-in users 'voicemail_prompt' => sprintf('You have reached %s. Please leave a message after the tone.', $user->display_name), 'is_hold_queue' => 0 ), array('%s', '%s', '%d', '%s', '%d', '%d', '%s', '%d') ); if (!$personal_queue_id) { return array('success' => false, 'error' => 'Failed to create personal queue'); } $personal_queue_id = $wpdb->insert_id; // Create hold queue $hold_queue_name = sprintf('Hold - %s', $user->display_name); $hold_queue_id = $wpdb->insert( $wpdb->prefix . 'twp_call_queues', array( 'queue_name' => $hold_queue_name, 'queue_type' => 'hold', 'user_id' => $user_id, 'extension' => null, // Hold queues don't have extensions 'max_size' => 5, 'timeout_seconds' => 0, // No timeout for hold queues 'tts_message' => 'Your call is on hold. Please wait.', 'is_hold_queue' => 1 ), array('%s', '%s', '%d', '%s', '%d', '%d', '%s', '%d') ); if (!$hold_queue_id) { // Rollback personal queue creation $wpdb->delete($wpdb->prefix . 'twp_call_queues', array('id' => $personal_queue_id)); return array('success' => false, 'error' => 'Failed to create hold queue'); } $hold_queue_id = $wpdb->insert_id; // Create user extension record $extension_result = $wpdb->insert( $wpdb->prefix . 'twp_user_extensions', array( 'user_id' => $user_id, 'extension' => $extension, 'personal_queue_id' => $personal_queue_id, 'hold_queue_id' => $hold_queue_id ), array('%d', '%s', '%d', '%d') ); if (!$extension_result) { // Rollback queue creations $wpdb->delete($wpdb->prefix . 'twp_call_queues', array('id' => $personal_queue_id)); $wpdb->delete($wpdb->prefix . 'twp_call_queues', array('id' => $hold_queue_id)); return array('success' => false, 'error' => 'Failed to create extension record'); } // Auto-assign user to their personal queue $wpdb->insert( $wpdb->prefix . 'twp_queue_assignments', array( 'user_id' => $user_id, 'queue_id' => $personal_queue_id, 'is_primary' => 1 ), array('%d', '%d', '%d') ); // Auto-assign user to their hold queue $wpdb->insert( $wpdb->prefix . 'twp_queue_assignments', array( 'user_id' => $user_id, 'queue_id' => $hold_queue_id, 'is_primary' => 0 ), array('%d', '%d', '%d') ); return array( 'success' => true, 'extension' => $extension, 'personal_queue_id' => $personal_queue_id, 'hold_queue_id' => $hold_queue_id ); } /** * Generate a unique extension number * * @return string Extension number (3-4 digits) */ private static function generate_unique_extension() { global $wpdb; // Start with 100 for 3-digit extensions $start = 100; $max_attempts = 900; // Up to 999 for ($i = 0; $i < $max_attempts; $i++) { $extension = (string)($start + $i); $exists = $wpdb->get_var($wpdb->prepare( "SELECT extension FROM {$wpdb->prefix}twp_user_extensions WHERE extension = %s", $extension )); if (!$exists) { return $extension; } } // If all 3-digit extensions are taken, try 4-digit starting at 1000 $start = 1000; $max_attempts = 9000; // Up to 9999 for ($i = 0; $i < $max_attempts; $i++) { $extension = (string)($start + $i); $exists = $wpdb->get_var($wpdb->prepare( "SELECT extension FROM {$wpdb->prefix}twp_user_extensions WHERE extension = %s", $extension )); if (!$exists) { return $extension; } } return null; // All extensions taken (unlikely) } /** * Get user's extension and queue information * * @param int $user_id WordPress user ID * @return array|null Extension and queue data */ public static function get_user_extension_data($user_id) { global $wpdb; $data = $wpdb->get_row($wpdb->prepare( "SELECT * FROM {$wpdb->prefix}twp_user_extensions WHERE user_id = %d", $user_id ), ARRAY_A); return $data; } /** * Get user by extension * * @param string $extension Extension number * @return int|null User ID */ public static function get_user_by_extension($extension) { global $wpdb; $user_id = $wpdb->get_var($wpdb->prepare( "SELECT user_id FROM {$wpdb->prefix}twp_user_extensions WHERE extension = %s", $extension )); return $user_id ? intval($user_id) : null; } /** * Get all queues assigned to a user * * @param int $user_id WordPress user ID * @return array Array of queue data */ public static function get_user_assigned_queues($user_id) { global $wpdb; $query = $wpdb->prepare(" SELECT q.*, qa.is_primary, (SELECT COUNT(*) FROM {$wpdb->prefix}twp_queued_calls qc WHERE qc.queue_id = q.id AND qc.status = 'waiting') as waiting_calls FROM {$wpdb->prefix}twp_call_queues q INNER JOIN {$wpdb->prefix}twp_queue_assignments qa ON q.id = qa.queue_id WHERE qa.user_id = %d ORDER BY qa.is_primary DESC, q.queue_name ASC ", $user_id); $queues = $wpdb->get_results($query, ARRAY_A); return $queues; } /** * Update queue timeout based on user login status * * @param int $user_id WordPress user ID * @param bool $is_logged_in Whether user is logged in */ public static function update_queue_timeout_for_login($user_id, $is_logged_in) { global $wpdb; // Get user's personal queue $extension_data = self::get_user_extension_data($user_id); if (!$extension_data || !$extension_data['personal_queue_id']) { return; } // Update timeout: 5 minutes if logged in, 0 (immediate voicemail) if logged out $timeout = $is_logged_in ? 300 : 0; $wpdb->update( $wpdb->prefix . 'twp_call_queues', array('timeout_seconds' => $timeout), array('id' => $extension_data['personal_queue_id']), array('%d'), array('%d') ); } /** * Transfer call to user's hold queue * * @param int $user_id WordPress user ID * @param string $call_sid Call SID to transfer * @return array Result array */ public static function transfer_to_hold_queue($user_id, $call_sid) { global $wpdb; // Get user's hold queue $extension_data = self::get_user_extension_data($user_id); if (!$extension_data || !$extension_data['hold_queue_id']) { return array('success' => false, 'error' => 'Hold queue not found for user'); } // Check if call exists in any queue $current_queue = $wpdb->get_row($wpdb->prepare( "SELECT * FROM {$wpdb->prefix}twp_queued_calls WHERE call_sid = %s AND status = 'waiting'", $call_sid ), ARRAY_A); if (!$current_queue) { return array('success' => false, 'error' => 'Call not found in queue'); } // Move call to hold queue $result = $wpdb->update( $wpdb->prefix . 'twp_queued_calls', array( 'queue_id' => $extension_data['hold_queue_id'], 'position' => 1 // Reset position in hold queue ), array('id' => $current_queue['id']), array('%d', '%d'), array('%d') ); if ($result === false) { return array('success' => false, 'error' => 'Failed to transfer to hold queue'); } return array( 'success' => true, 'hold_queue_id' => $extension_data['hold_queue_id'] ); } /** * Transfer call from hold queue back to original or specified queue * * @param int $user_id WordPress user ID * @param string $call_sid Call SID to transfer * @param int $target_queue_id Optional target queue ID * @return array Result array */ public static function resume_from_hold($user_id, $call_sid, $target_queue_id = null) { global $wpdb; // Get user's hold queue $extension_data = self::get_user_extension_data($user_id); if (!$extension_data || !$extension_data['hold_queue_id']) { return array('success' => false, 'error' => 'Hold queue not found for user'); } // Check if call is in hold queue $held_call = $wpdb->get_row($wpdb->prepare( "SELECT * FROM {$wpdb->prefix}twp_queued_calls WHERE call_sid = %s AND queue_id = %d AND status = 'waiting'", $call_sid, $extension_data['hold_queue_id'] ), ARRAY_A); if (!$held_call) { return array('success' => false, 'error' => 'Call not found in hold queue'); } // Determine target queue if (!$target_queue_id) { // Default to user's personal queue $target_queue_id = $extension_data['personal_queue_id']; } // Get next position in target queue $next_position = $wpdb->get_var($wpdb->prepare( "SELECT COALESCE(MAX(position), 0) + 1 FROM {$wpdb->prefix}twp_queued_calls WHERE queue_id = %d AND status = 'waiting'", $target_queue_id )); // Move call to target queue $result = $wpdb->update( $wpdb->prefix . 'twp_queued_calls', array( 'queue_id' => $target_queue_id, 'position' => $next_position ), array('id' => $held_call['id']), array('%d', '%d'), array('%d') ); if ($result === false) { return array('success' => false, 'error' => 'Failed to resume from hold'); } return array( 'success' => true, 'target_queue_id' => $target_queue_id, 'position' => $next_position ); } /** * Initialize user queues for all existing users * This should be called during plugin activation or upgrade */ public static function initialize_all_user_queues() { $users = get_users(array( 'fields' => 'ID', 'meta_key' => 'twp_phone_number', 'meta_compare' => 'EXISTS' )); $results = array( 'success' => 0, 'failed' => 0, 'skipped' => 0 ); foreach ($users as $user_id) { // Check if user already has queues $existing = self::get_user_extension_data($user_id); if ($existing) { $results['skipped']++; continue; } $result = self::create_user_queues($user_id); if ($result['success']) { $results['success']++; } else { $results['failed']++; error_log('TWP: Failed to create queues for user ' . $user_id . ': ' . $result['error']); } } return $results; } }