testing progress
This commit is contained in:
@@ -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);
|
||||
|
Reference in New Issue
Block a user