Portions created by the Initial Developer are Copyright (C) 2008-2025 the Initial Developer. All Rights Reserved. Contributor(s): Mark J Crane Tim Fry */ // Includes require_once dirname(__DIR__, 2) . "/resources/require.php"; require_once "resources/check_auth.php"; // Check permissions if (!permission_exists('operator_panel_view')) { echo "access denied"; exit; } // Multi-lingual support $language = new text; $text = $language->get(); // Create token and register with the active operator panel service $token = (new token())->create($_SERVER['PHP_SELF']); subscriber::save_token($token, ['active.operator.panel']); // Gather user permissions for the JS side $perm = [ 'operator_panel_view' => permission_exists('operator_panel_view'), 'operator_panel_manage' => permission_exists('operator_panel_manage'), 'operator_panel_hangup' => permission_exists('operator_panel_hangup'), 'operator_panel_eavesdrop' => permission_exists('operator_panel_eavesdrop'), 'operator_panel_coach' => permission_exists('operator_panel_coach'), 'operator_panel_record' => permission_exists('operator_panel_record'), 'operator_panel_originate' => permission_exists('operator_panel_originate'), ]; // WebSocket settings from default_settings $ws_settings = [ 'reconnect_delay' => (int)$settings->get('operator_panel', 'reconnect_delay', 500), 'ping_interval' => (int)$settings->get('operator_panel', 'ping_interval', 5000), 'auth_timeout' => (int)$settings->get('operator_panel', 'auth_timeout', 5000), 'pong_timeout' => (int)$settings->get('operator_panel', 'pong_timeout', 1500), 'max_reconnect_delay' => (int)$settings->get('operator_panel', 'max_reconnect_delay', 5000), 'pong_timeout_max_retries' => (int)$settings->get('operator_panel', 'pong_timeout_max_retries', 2), 'refresh_interval' => (int)$settings->get('operator_panel', 'refresh_interval', 0), ]; // Theme colors for connection status indicator $status_colors = [ 'connected' => $settings->get('theme', 'operator_panel_status_connected', '#28a745'), 'warning' => $settings->get('theme', 'operator_panel_status_warning', '#ffc107'), 'disconnected' => $settings->get('theme', 'operator_panel_status_disconnected', '#dc3545'), 'connecting' => $settings->get('theme', 'operator_panel_status_connecting', '#6c757d'), ]; $status_icons = [ 'connected' => $settings->get('theme', 'operator_panel_status_icon_connected', 'fa-solid fa-plug-circle-check'), 'warning' => $settings->get('theme', 'operator_panel_status_icon_warning', 'fa-solid fa-plug-circle-exclamation'), 'disconnected' => $settings->get('theme', 'operator_panel_status_icon_disconnected', 'fa-solid fa-plug-circle-xmark'), 'connecting' => $settings->get('theme', 'operator_panel_status_icon_connecting', 'fa-solid fa-plug fa-fade'), ]; $status_show_icon = $settings->get('theme', 'operator_panel_status_show_icon', 'true') === 'true'; // Optional user status list for the presence dropdown $user_statuses = ['Available', 'Available (On Demand)', 'On Break', 'Do Not Disturb', 'Logged Out']; // Card label position for extension group cards: top, left, right, bottom, hidden $card_label_position = strtolower((string)$settings->get('operator_panel', 'card_label_position', 'left')); if (!in_array($card_label_position, ['top', 'left', 'right', 'bottom', 'hidden'], true)) { $card_label_position = 'left'; } // Optional polling reconciliation of registration state (can be disabled). $registrations_reconcile_enabled = $settings->get('operator_panel', 'registrations_reconcile_enabled', 'false') === 'true'; // Get the logged-in user's own extension numbers (shown at top of Extensions panel) // and primary eavesdrop destination extension $user_own_extensions = []; if (!empty($_SESSION['user']['extensions'])) { // $_SESSION['user']['extensions'] is an array of extension number strings $user_own_extensions = array_values(array_filter($_SESSION['user']['extensions'])); } elseif (!empty($_SESSION['user']['extension'])) { foreach ($_SESSION['user']['extension'] as $ext_record) { if (!empty($ext_record['destination'])) { $user_own_extensions[] = $ext_record['destination']; } } } // Include the page header $document['title'] = $text['title-operator_panel'] ?? 'Operator Panel'; require_once "resources/header.php"; // Cache-busting hashes for JS assets $ws_client_hash = md5_file(__DIR__ . '/resources/javascript/websocket_client.js'); $lop_js_hash = md5_file(__DIR__ . '/resources/javascript/operator_panel.js'); ?> \n"; echo "
" . $text['title-operator_panel'] . "\n"; // Connection status indicator (icon + text) echo "\t\t"; if ($status_show_icon) { echo ""; } echo "" . htmlspecialchars($text['status-connecting'] ?? 'Connecting') . ""; echo "\n"; echo "
\n"; // My status buttons (matching the original design) if ($perm['operator_panel_view']) { $status_btn_colors = [ 'Available' => '#28a745', 'Available (On Demand)'=> '#28a745', 'On Break' => '#b8860b', 'Do Not Disturb' => '#dc3545', 'Logged Out' => '#6c757d', ]; echo "
\n"; echo "
\n"; foreach ($user_statuses as $s) { $color = $status_btn_colors[$s] ?? '#6c757d'; $label = strtoupper(htmlspecialchars($s)); echo " \n"; } echo "
\n"; echo "
\n"; } echo "
\n"; echo "\n"; ?>