- Fix 403 Forbidden error in voicemail AJAX by correcting nonce validation - Create accordion-style collapsible voicemail section with persistent state - Restructure queue controls to show alert toggle button consistently - Add global queue actions always visible when user has assigned queues - Implement smooth slide animations and localStorage state management - Update dark mode support for new UI elements and improved accessibility 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
250 lines
11 KiB
PHP
250 lines
11 KiB
PHP
<?php
|
||
/**
|
||
* Handle plugin shortcodes
|
||
*/
|
||
class TWP_Shortcodes {
|
||
|
||
/**
|
||
* Initialize shortcodes
|
||
*/
|
||
public static function init() {
|
||
add_action('init', array(__CLASS__, 'register_shortcodes'));
|
||
add_action('wp_enqueue_scripts', array(__CLASS__, 'enqueue_frontend_assets'));
|
||
}
|
||
|
||
/**
|
||
* Register all shortcodes
|
||
*/
|
||
public static function register_shortcodes() {
|
||
add_shortcode('twp_browser_phone', array(__CLASS__, 'browser_phone_shortcode'));
|
||
}
|
||
|
||
/**
|
||
* Enqueue frontend assets when shortcode is present
|
||
*/
|
||
public static function enqueue_frontend_assets() {
|
||
global $post;
|
||
|
||
if (is_a($post, 'WP_Post') && has_shortcode($post->post_content, 'twp_browser_phone')) {
|
||
// Enqueue Twilio Voice SDK from unpkg CDN (same as backend)
|
||
wp_enqueue_script(
|
||
'twilio-voice-sdk',
|
||
'https://unpkg.com/@twilio/voice-sdk@2.11.0/dist/twilio.min.js',
|
||
array(),
|
||
'2.11.0',
|
||
false // Load in head to ensure it's available
|
||
);
|
||
|
||
// Add backup SDK loading
|
||
wp_add_inline_script('twilio-voice-sdk', "
|
||
window.twpLoadTwilioSDK = function() {
|
||
if (typeof Twilio === 'undefined') {
|
||
console.warn('Primary Twilio SDK failed, attempting fallback load');
|
||
var script = document.createElement('script');
|
||
script.src = 'https://unpkg.com/@twilio/voice-sdk@2.11.0/dist/twilio.min.js';
|
||
script.onload = function() { console.log('Fallback Twilio SDK loaded'); };
|
||
script.onerror = function() { console.error('Fallback Twilio SDK failed'); };
|
||
document.head.appendChild(script);
|
||
}
|
||
};
|
||
|
||
// Try loading after a delay if not available
|
||
setTimeout(window.twpLoadTwilioSDK, 2000);
|
||
", 'after');
|
||
|
||
// Enqueue our browser phone script
|
||
wp_enqueue_script(
|
||
'twp-browser-phone-frontend',
|
||
TWP_PLUGIN_URL . 'assets/js/browser-phone-frontend.js',
|
||
array('jquery', 'twilio-voice-sdk'),
|
||
TWP_VERSION,
|
||
true
|
||
);
|
||
|
||
// Enqueue mobile-friendly styles
|
||
wp_enqueue_style(
|
||
'twp-browser-phone-frontend',
|
||
TWP_PLUGIN_URL . 'assets/css/browser-phone-frontend.css',
|
||
array(),
|
||
TWP_VERSION
|
||
);
|
||
|
||
// Localize script with AJAX data
|
||
wp_localize_script('twp-browser-phone-frontend', 'twp_frontend_ajax', array(
|
||
'ajax_url' => admin_url('admin-ajax.php'),
|
||
'admin_url' => admin_url(),
|
||
'nonce' => wp_create_nonce('twp_frontend_nonce'),
|
||
'user_id' => get_current_user_id(),
|
||
'is_logged_in' => is_user_logged_in()
|
||
));
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Browser phone shortcode handler
|
||
*/
|
||
public static function browser_phone_shortcode($atts) {
|
||
// Check if user is logged in
|
||
if (!is_user_logged_in()) {
|
||
return '<div class="twp-error">You must be logged in to access the browser phone.</div>';
|
||
}
|
||
|
||
// Check if user has permission
|
||
if (!current_user_can('twp_access_browser_phone') && !current_user_can('manage_options')) {
|
||
return '<div class="twp-error">You don\'t have permission to access the browser phone.</div>';
|
||
}
|
||
|
||
// Parse shortcode attributes
|
||
$atts = shortcode_atts(array(
|
||
'title' => 'Browser Phone',
|
||
'show_title' => 'true',
|
||
'compact' => 'false'
|
||
), $atts, 'twp_browser_phone');
|
||
|
||
$show_title = ($atts['show_title'] === 'true');
|
||
$compact_mode = ($atts['compact'] === 'true');
|
||
|
||
ob_start();
|
||
?>
|
||
<div id="twp-frontend-browser-phone" class="twp-browser-phone-container <?php echo $compact_mode ? 'compact' : ''; ?>">
|
||
<?php if ($show_title): ?>
|
||
<h3 class="twp-browser-phone-title"><?php echo esc_html($atts['title']); ?></h3>
|
||
<?php endif; ?>
|
||
|
||
<!-- Connection Status -->
|
||
<div class="twp-connection-status">
|
||
<span class="status-indicator offline" id="twp-status-indicator"></span>
|
||
<span class="status-text" id="twp-status-text">Connecting...</span>
|
||
</div>
|
||
|
||
<!-- Phone Number Selection -->
|
||
<div class="twp-phone-selection">
|
||
<label for="twp-caller-id">Caller ID:</label>
|
||
<select id="twp-caller-id" class="twp-select">
|
||
<option value="">Loading...</option>
|
||
</select>
|
||
</div>
|
||
|
||
<!-- Dial Pad -->
|
||
<div class="twp-dialpad">
|
||
<div class="twp-number-display">
|
||
<input type="tel" id="twp-dial-number" placeholder="Enter number to dial" />
|
||
<button id="twp-clear-number" class="twp-btn-clear" title="Clear">×</button>
|
||
</div>
|
||
|
||
<div class="twp-dial-grid">
|
||
<button class="twp-dial-btn" data-digit="1">1<span></span></button>
|
||
<button class="twp-dial-btn" data-digit="2">2<span>ABC</span></button>
|
||
<button class="twp-dial-btn" data-digit="3">3<span>DEF</span></button>
|
||
<button class="twp-dial-btn" data-digit="4">4<span>GHI</span></button>
|
||
<button class="twp-dial-btn" data-digit="5">5<span>JKL</span></button>
|
||
<button class="twp-dial-btn" data-digit="6">6<span>MNO</span></button>
|
||
<button class="twp-dial-btn" data-digit="7">7<span>PQRS</span></button>
|
||
<button class="twp-dial-btn" data-digit="8">8<span>TUV</span></button>
|
||
<button class="twp-dial-btn" data-digit="9">9<span>WXYZ</span></button>
|
||
<button class="twp-dial-btn" data-digit="*">*</button>
|
||
<button class="twp-dial-btn" data-digit="0">0<span>+</span></button>
|
||
<button class="twp-dial-btn" data-digit="#">#</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Call Controls -->
|
||
<div class="twp-call-controls">
|
||
<button id="twp-call-btn" class="twp-btn twp-btn-primary">
|
||
<span class="call-icon">📞</span>
|
||
<span class="call-text">Call</span>
|
||
</button>
|
||
<button id="twp-hangup-btn" class="twp-btn twp-btn-danger" style="display: none;">
|
||
<span class="hangup-icon">📞</span>
|
||
<span class="hangup-text">Hang Up</span>
|
||
</button>
|
||
</div>
|
||
|
||
<!-- Call Info -->
|
||
<div class="twp-call-info" id="twp-call-info" style="display: none;">
|
||
<div class="call-timer">
|
||
<span class="timer-label">Call Duration:</span>
|
||
<span class="timer-value" id="twp-call-timer">00:00</span>
|
||
</div>
|
||
<div class="call-status">
|
||
<span id="twp-call-status">Connecting...</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Queue Management Section -->
|
||
<div class="twp-queue-section" id="twp-queue-section">
|
||
<h4>Your Queues</h4>
|
||
<div class="twp-queue-list" id="twp-queue-list">
|
||
<div class="queue-loading">Loading queues...</div>
|
||
</div>
|
||
|
||
<!-- Queue Actions (Always Visible) -->
|
||
<div class="queue-global-actions" id="twp-queue-global-actions" style="display: none;">
|
||
<div class="global-queue-actions">
|
||
<button id="twp-refresh-queues" class="twp-btn twp-btn-secondary">
|
||
Refresh
|
||
</button>
|
||
<button id="twp-alert-toggle" class="twp-btn twp-btn-secondary alert-off">
|
||
🔕 Alerts OFF
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Queue Controls (Only When Call Selected) -->
|
||
<div class="twp-queue-controls" id="twp-queue-controls" style="display: none;">
|
||
<div class="selected-queue-info">
|
||
<h5 id="selected-queue-name">No queue selected</h5>
|
||
<div class="queue-stats">
|
||
<span class="waiting-count">Waiting: <span id="twp-waiting-count">0</span></span>
|
||
<span class="queue-size">Max: <span id="twp-queue-max-size">-</span></span>
|
||
</div>
|
||
</div>
|
||
<div class="queue-actions">
|
||
<button id="twp-accept-queue-call" class="twp-btn twp-btn-success">
|
||
Accept Next Call
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Voicemail Section -->
|
||
<div class="twp-voicemail-section" id="twp-voicemail-section">
|
||
<div class="voicemail-header" id="twp-voicemail-header">
|
||
<h4>Recent Voicemails</h4>
|
||
<div class="voicemail-summary">
|
||
<span class="summary-stats">
|
||
Total: <span id="twp-total-voicemails">0</span> |
|
||
Today: <span id="twp-today-voicemails">0</span>
|
||
</span>
|
||
<button class="voicemail-toggle" id="twp-voicemail-toggle">
|
||
<span class="toggle-icon">▼</span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="voicemail-content" id="twp-voicemail-content" style="display: none;">
|
||
<div class="twp-voicemail-list" id="twp-voicemail-list">
|
||
<div class="voicemail-loading">Loading voicemails...</div>
|
||
</div>
|
||
|
||
<div class="voicemail-actions">
|
||
<button id="twp-refresh-voicemails" class="twp-btn twp-btn-secondary">
|
||
Refresh
|
||
</button>
|
||
<button id="twp-view-all-voicemails" class="twp-btn twp-btn-secondary">
|
||
View All
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Audio elements for incoming calls handled by browser -->
|
||
<audio id="twp-ringtone" style="display: none;"></audio>
|
||
|
||
<!-- Error/Success Messages -->
|
||
<div class="twp-messages" id="twp-messages"></div>
|
||
</div>
|
||
<?php
|
||
return ob_get_clean();
|
||
}
|
||
}
|