testing progress

This commit is contained in:
2025-08-12 09:54:32 -07:00
parent e18e046431
commit 2d6767033b
3 changed files with 654 additions and 256 deletions

View File

@@ -19,132 +19,175 @@ class TWP_Admin {
* Register admin menu
*/
public function add_plugin_admin_menu() {
add_menu_page(
'Twilio WP Plugin',
'Twilio Phone',
'manage_options',
'twilio-wp-plugin',
array($this, 'display_plugin_dashboard'),
'dashicons-phone',
30
);
// Determine if user has any agent access
$has_agent_access = current_user_can('twp_access_voicemails') ||
current_user_can('twp_access_call_log') ||
current_user_can('twp_access_agent_queue') ||
current_user_can('twp_access_sms_inbox') ||
current_user_can('twp_access_browser_phone');
add_submenu_page(
'twilio-wp-plugin',
'Dashboard',
'Dashboard',
'manage_options',
'twilio-wp-plugin',
array($this, 'display_plugin_dashboard')
);
// Only show menu if user is admin or has agent access
if (!current_user_can('manage_options') && !$has_agent_access) {
return;
}
add_submenu_page(
'twilio-wp-plugin',
'Settings',
'Settings',
'manage_options',
'twilio-wp-settings',
array($this, 'display_plugin_settings')
);
// Main menu - show dashboard for admins, redirect to first available page for agents
if (current_user_can('manage_options')) {
add_menu_page(
'Twilio WP Plugin',
'Twilio Phone',
'manage_options',
'twilio-wp-plugin',
array($this, 'display_plugin_dashboard'),
'dashicons-phone',
30
);
add_submenu_page(
'twilio-wp-plugin',
'Dashboard',
'Dashboard',
'manage_options',
'twilio-wp-plugin',
array($this, 'display_plugin_dashboard')
);
} else {
// For agents, determine first available page
$first_page = 'twilio-wp-browser-phone'; // Default to browser phone
if (current_user_can('twp_access_voicemails')) $first_page = 'twilio-wp-voicemails';
elseif (current_user_can('twp_access_call_log')) $first_page = 'twilio-wp-call-logs';
elseif (current_user_can('twp_access_agent_queue')) $first_page = 'twilio-wp-agent-queue';
elseif (current_user_can('twp_access_sms_inbox')) $first_page = 'twilio-wp-sms-inbox';
elseif (current_user_can('twp_access_browser_phone')) $first_page = 'twilio-wp-browser-phone';
add_menu_page(
'Twilio Phone',
'Twilio Phone',
'read',
$first_page,
null,
'dashicons-phone',
30
);
}
add_submenu_page(
'twilio-wp-plugin',
'Phone Schedules',
'Schedules',
'manage_options',
'twilio-wp-schedules',
array($this, 'display_schedules_page')
);
// Admin-only pages
if (current_user_can('manage_options')) {
add_submenu_page(
'twilio-wp-plugin',
'Settings',
'Settings',
'manage_options',
'twilio-wp-settings',
array($this, 'display_plugin_settings')
);
add_submenu_page(
'twilio-wp-plugin',
'Phone Schedules',
'Schedules',
'manage_options',
'twilio-wp-schedules',
array($this, 'display_schedules_page')
);
add_submenu_page(
'twilio-wp-plugin',
'Workflows',
'Workflows',
'manage_options',
'twilio-wp-workflows',
array($this, 'display_workflows_page')
);
add_submenu_page(
'twilio-wp-plugin',
'Call Queues',
'Queues',
'manage_options',
'twilio-wp-queues',
array($this, 'display_queues_page')
);
add_submenu_page(
'twilio-wp-plugin',
'Phone Numbers',
'Phone Numbers',
'manage_options',
'twilio-wp-numbers',
array($this, 'display_numbers_page')
);
add_submenu_page(
'twilio-wp-plugin',
'Agent Groups',
'Agent Groups',
'manage_options',
'twilio-wp-groups',
array($this, 'display_groups_page')
);
}
add_submenu_page(
'twilio-wp-plugin',
'Workflows',
'Workflows',
'manage_options',
'twilio-wp-workflows',
array($this, 'display_workflows_page')
);
// Agent-accessible pages
$menu_parent = current_user_can('manage_options') ? 'twilio-wp-plugin' : null;
add_submenu_page(
'twilio-wp-plugin',
'Call Queues',
'Queues',
'manage_options',
'twilio-wp-queues',
array($this, 'display_queues_page')
);
if (current_user_can('manage_options') || current_user_can('twp_access_voicemails')) {
add_submenu_page(
$menu_parent,
'Voicemails',
'Voicemails',
current_user_can('manage_options') ? 'manage_options' : 'twp_access_voicemails',
'twilio-wp-voicemails',
array($this, 'display_voicemails_page')
);
}
add_submenu_page(
'twilio-wp-plugin',
'Phone Numbers',
'Phone Numbers',
'manage_options',
'twilio-wp-numbers',
array($this, 'display_numbers_page')
);
if (current_user_can('manage_options') || current_user_can('twp_access_call_log')) {
add_submenu_page(
$menu_parent,
'Call Logs',
'Call Logs',
current_user_can('manage_options') ? 'manage_options' : 'twp_access_call_log',
'twilio-wp-call-logs',
array($this, 'display_call_logs_page')
);
}
add_submenu_page(
'twilio-wp-plugin',
'Voicemails',
'Voicemails',
'manage_options',
'twilio-wp-voicemails',
array($this, 'display_voicemails_page')
);
if (current_user_can('manage_options') || current_user_can('twp_access_agent_queue')) {
add_submenu_page(
$menu_parent,
'Agent Queue',
'Agent Queue',
current_user_can('manage_options') ? 'manage_options' : 'twp_access_agent_queue',
'twilio-wp-agent-queue',
array($this, 'display_agent_queue_page')
);
}
add_submenu_page(
'twilio-wp-plugin',
'Call Logs',
'Call Logs',
'manage_options',
'twilio-wp-call-logs',
array($this, 'display_call_logs_page')
);
// Outbound Calls page removed - functionality merged into Browser Phone
// Keeping capability 'twp_access_outbound_calls' for backwards compatibility
add_submenu_page(
'twilio-wp-plugin',
'Agent Groups',
'Agent Groups',
'manage_options',
'twilio-wp-groups',
array($this, 'display_groups_page')
);
if (current_user_can('manage_options') || current_user_can('twp_access_sms_inbox')) {
add_submenu_page(
$menu_parent,
'SMS Inbox',
'SMS Inbox',
current_user_can('manage_options') ? 'manage_options' : 'twp_access_sms_inbox',
'twilio-wp-sms-inbox',
array($this, 'display_sms_inbox_page')
);
}
add_submenu_page(
'twilio-wp-plugin',
'Agent Queue',
'Agent Queue',
'manage_options',
'twilio-wp-agent-queue',
array($this, 'display_agent_queue_page')
);
add_submenu_page(
'twilio-wp-plugin',
'Outbound Calls',
'Outbound Calls',
'manage_options',
'twilio-wp-outbound',
array($this, 'display_outbound_calls_page')
);
add_submenu_page(
'twilio-wp-plugin',
'SMS Inbox',
'SMS Inbox',
'manage_options',
'twilio-wp-sms-inbox',
array($this, 'display_sms_inbox_page')
);
add_submenu_page(
'twilio-wp-plugin',
'Browser Phone',
'Browser Phone',
'manage_options',
'twilio-wp-browser-phone',
array($this, 'display_browser_phone_page')
);
if (current_user_can('manage_options') || current_user_can('twp_access_browser_phone')) {
add_submenu_page(
$menu_parent,
'Browser Phone',
'Browser Phone',
current_user_can('manage_options') ? 'manage_options' : 'twp_access_browser_phone',
'twilio-wp-browser-phone',
array($this, 'display_browser_phone_page')
);
}
}
/**
@@ -5448,105 +5491,185 @@ class TWP_Admin {
}
</style>
<script src="https://sdk.twilio.com/js/client/v1.14/twilio.min.js"></script>
<!-- Twilio Voice SDK v2 from unpkg CDN -->
<script src="https://unpkg.com/@twilio/voice-sdk@2.11.0/dist/twilio.min.js"></script>
<script>
jQuery(document).ready(function($) {
var device = null;
var currentConnection = null;
var currentCall = null;
var callTimer = null;
var callStartTime = null;
// Wait for SDK to load
function waitForTwilioSDK(callback) {
if (typeof Twilio !== 'undefined' && Twilio.Device) {
callback();
} else {
console.log('Waiting for Twilio Voice SDK to load...');
setTimeout(function() {
waitForTwilioSDK(callback);
}, 100);
}
}
// Initialize the browser phone
function initializeBrowserPhone() {
$('#phone-status').text('Initializing...');
// Get capability token
$.post(ajaxurl, {
action: 'twp_generate_capability_token',
nonce: '<?php echo wp_create_nonce('twp_ajax_nonce'); ?>'
}, function(response) {
if (response.success) {
$('#browser-phone-error').hide();
setupTwilioDevice(response.data.token);
} else {
showError('Failed to initialize: ' + response.error);
}
}).fail(function() {
showError('Failed to connect to server');
// Wait for SDK before proceeding
waitForTwilioSDK(function() {
// Get capability token (access token for v2)
$.post(ajaxurl, {
action: 'twp_generate_capability_token',
nonce: '<?php echo wp_create_nonce('twp_ajax_nonce'); ?>'
}, function(response) {
if (response.success) {
$('#browser-phone-error').hide();
setupTwilioDevice(response.data.token);
} else {
showError('Failed to initialize: ' + response.error);
}
}).fail(function() {
showError('Failed to connect to server');
});
});
}
function setupTwilioDevice(token) {
async function setupTwilioDevice(token) {
try {
// Setup Twilio Device
Twilio.Device.setup(token, {
debug: true,
codecPreferences: ['opus', 'pcmu']
// Check if Twilio SDK is available
if (typeof Twilio === 'undefined' || !Twilio.Device) {
throw new Error('Twilio Voice SDK not loaded');
}
// Clean up existing device if any
if (device) {
await device.destroy();
}
// Setup Twilio Voice SDK v2 Device
// Note: Voice SDK v2 uses Twilio.Device directly, not Twilio.Voice.Device
device = new Twilio.Device(token, {
logLevel: 1, // 0 = TRACE, 1 = DEBUG
codecPreferences: ['opus', 'pcmu'],
edge: 'sydney' // Or closest edge location
});
// Use modern EventEmitter interface instead of deprecated callbacks
Twilio.Device.on('ready', function(device) {
// Set up event handlers BEFORE registering
// Device registered and ready
device.on('registered', function() {
console.log('Device registered successfully');
$('#phone-status').text('Ready').css('color', '#4CAF50');
$('#call-btn').prop('disabled', false);
});
Twilio.Device.on('error', function(error) {
// Handle errors
device.on('error', function(error) {
console.error('Twilio Device Error:', error);
var errorMsg = error.message;
var errorMsg = error.message || error.toString();
// Provide specific help for common errors
if (error.message.includes('valid callerId must be provided')) {
if (errorMsg.includes('valid callerId must be provided')) {
errorMsg = 'Caller ID error: Make sure you select a verified Twilio phone number as Caller ID. The number must be purchased through your Twilio account.';
} else if (error.message.includes('TwiML App')) {
} else if (errorMsg.includes('TwiML App')) {
errorMsg = 'TwiML App error: Check that your TwiML App SID is correctly configured in Settings.';
} else if (error.message.includes('token')) {
errorMsg = 'Token error: ' + error.message + ' - The page will automatically try to refresh the token.';
} else if (errorMsg.includes('token') || errorMsg.includes('Token')) {
errorMsg = 'Token error: ' + errorMsg + ' - The page will automatically try to refresh the token.';
// Try to reinitialize after token error
setTimeout(initializeBrowserPhone, 5000);
}
showError(errorMsg);
});
Twilio.Device.on('connect', function(conn) {
currentConnection = conn;
$('#phone-status').text('Connected').css('color', '#2196F3');
$('#call-btn').hide();
$('#hangup-btn').show();
$('#phone-controls-extra').show();
startCallTimer();
});
Twilio.Device.on('disconnect', function(conn) {
currentConnection = null;
$('#phone-status').text('Ready').css('color', '#4CAF50');
$('#hangup-btn').hide();
$('#answer-btn').hide();
$('#call-btn').show();
$('#phone-controls-extra').hide();
$('#call-timer').hide();
stopCallTimer();
});
Twilio.Device.on('incoming', function(conn) {
currentConnection = conn;
// Handle incoming calls
device.on('incoming', function(call) {
currentCall = call;
$('#phone-status').text('Incoming Call').css('color', '#FF9800');
$('#phone-number-display').text(conn.parameters.From || 'Unknown Number');
$('#phone-number-display').text(call.parameters.From || 'Unknown Number');
$('#call-btn').hide();
$('#answer-btn').show();
// Setup call event handlers
setupCallHandlers(call);
if ($('#auto-answer').is(':checked')) {
conn.accept();
call.accept();
}
});
// Token about to expire
device.on('tokenWillExpire', function() {
console.log('Token will expire soon, refreshing...');
refreshToken();
});
// Register device AFTER setting up event handlers
await device.register();
} catch (error) {
console.error('Error setting up Twilio Device:', error);
showError('Failed to setup device: ' + error.message);
}
}
function setupCallHandlers(call) {
// Call accepted/connected
call.on('accept', function() {
$('#phone-status').text('Connected').css('color', '#2196F3');
$('#call-btn').hide();
$('#answer-btn').hide();
$('#hangup-btn').show();
$('#phone-controls-extra').show();
startCallTimer();
});
// Call disconnected
call.on('disconnect', function() {
currentCall = null;
$('#phone-status').text('Ready').css('color', '#4CAF50');
$('#hangup-btn').hide();
$('#answer-btn').hide();
$('#call-btn').show();
$('#phone-controls-extra').hide();
$('#call-timer').hide();
stopCallTimer();
});
// Call rejected
call.on('reject', function() {
currentCall = null;
$('#phone-status').text('Ready').css('color', '#4CAF50');
$('#answer-btn').hide();
$('#call-btn').show();
});
// Call cancelled (by caller before answer)
call.on('cancel', function() {
currentCall = null;
$('#phone-status').text('Missed Call').css('color', '#FF9800');
$('#answer-btn').hide();
$('#call-btn').show();
setTimeout(function() {
$('#phone-status').text('Ready').css('color', '#4CAF50');
}, 3000);
});
}
function refreshToken() {
$.post(ajaxurl, {
action: 'twp_generate_capability_token',
nonce: '<?php echo wp_create_nonce('twp_ajax_nonce'); ?>'
}, function(response) {
if (response.success && device) {
device.updateToken(response.data.token);
}
}).fail(function() {
console.error('Failed to refresh token');
});
}
function showError(message) {
$('#browser-phone-error').html('<p><strong>Error:</strong> ' + message + '</p>').show();
$('#phone-status').text('Error').css('color', '#f44336');
@@ -5597,7 +5720,7 @@ class TWP_Admin {
});
// Call button
$('#call-btn').on('click', function() {
$('#call-btn').on('click', async function() {
var phoneNumber = $('#phone-number-input').val().trim();
var callerId = $('#caller-id-select').val();
@@ -5611,6 +5734,11 @@ class TWP_Admin {
return;
}
if (!device) {
alert('Phone is not initialized. Please refresh the page.');
return;
}
// Format phone number
phoneNumber = phoneNumber.replace(/\D/g, '');
if (phoneNumber.length === 10) {
@@ -5631,7 +5759,8 @@ class TWP_Admin {
};
console.log('Making call with params:', params);
currentConnection = Twilio.Device.connect(params);
currentCall = await device.connect({params: params});
setupCallHandlers(currentCall);
} catch (error) {
console.error('Call error:', error);
showError('Failed to make call: ' + error.message);
@@ -5641,30 +5770,40 @@ class TWP_Admin {
// Hangup button
$('#hangup-btn').on('click', function() {
if (currentConnection) {
currentConnection.disconnect();
if (currentCall) {
currentCall.disconnect();
}
});
// Answer button
$('#answer-btn').on('click', function() {
if (currentConnection) {
currentConnection.accept();
if (currentCall) {
currentCall.accept();
}
});
// Mute button
$('#mute-btn').on('click', function() {
if (currentConnection) {
var muted = currentConnection.isMuted();
currentConnection.mute(!muted);
if (currentCall) {
var muted = currentCall.isMuted();
currentCall.mute(!muted);
$(this).text(muted ? 'Mute' : 'Unmute');
$(this).find('.dashicons').toggleClass('dashicons-microphone dashicons-microphone');
}
});
// Initialize on page load
initializeBrowserPhone();
// Check if SDK loaded and initialize
$(window).on('load', function() {
setTimeout(function() {
if (typeof Twilio === 'undefined') {
showError('Twilio Voice SDK failed to load. Please check your internet connection and try refreshing the page.');
console.error('Twilio SDK not found. Script may be blocked or failed to load.');
} else {
console.log('Twilio SDK loaded successfully');
initializeBrowserPhone();
}
}, 1000);
});
// Refresh token every 50 minutes (tokens expire in 1 hour)
setInterval(initializeBrowserPhone, 50 * 60 * 1000);