Fix the context being sent as 'default' (#7811)

Minor change requests implemented that were forgotten:
- Removed the line between the extension that belong to the user and the other extensions.
- Added the permissions for the tabs so they are not visible yet. "Extensions" and "Calls" are the only ones with the permissions after running app defaults
This commit is contained in:
frytimo
2026-03-25 14:42:53 -03:00
committed by GitHub
parent 630fed9494
commit 0c4122cb94
5 changed files with 91 additions and 28 deletions
+4
View File
@@ -52,6 +52,10 @@
$apps[$x]['permissions'][$y]['groups'][] = "superadmin";
$apps[$x]['permissions'][$y]['groups'][] = "admin";
$y++;
$apps[$x]['permissions'][$y]['name'] = "operator_panel_originate";
$apps[$x]['permissions'][$y]['groups'][] = "superadmin";
$apps[$x]['permissions'][$y]['groups'][] = "admin";
$y++;
$apps[$x]['permissions'][$y]['name'] = "operator_panel_call_details";
$apps[$x]['permissions'][$y]['groups'][] = "superadmin";
$apps[$x]['permissions'][$y]['groups'][] = "admin";
+14 -1
View File
@@ -66,7 +66,20 @@
// operator_panel_record - record calls
// operator_panel_call_details - view caller/callee details
// operator_panel_on_demand - on-demand availability status
// No new permissions are declared here.
// Tab visibility permissions
$y = 0;
$apps[$x]['permissions'][$y]['name'] = "operator_panel_extensions";
$apps[$x]['permissions'][$y]['groups'][] = "superadmin";
$apps[$x]['permissions'][$y]['groups'][] = "admin";
$y++;
$apps[$x]['permissions'][$y]['name'] = "operator_panel_calls";
$apps[$x]['permissions'][$y]['groups'][] = "superadmin";
$apps[$x]['permissions'][$y]['groups'][] = "admin";
$y++;
$apps[$x]['permissions'][$y]['name'] = "operator_panel_conferences";
$y++;
$apps[$x]['permissions'][$y]['name'] = "operator_panel_agents";
//default settings
$y = 0;
+45 -12
View File
@@ -45,13 +45,16 @@
// 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'),
'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_record' => permission_exists('operator_panel_record'),
'operator_panel_originate' => permission_exists('operator_panel_originate'),
'operator_panel_extensions' => permission_exists('operator_panel_extensions'),
'operator_panel_calls' => permission_exists('operator_panel_calls'),
'operator_panel_conferences' => permission_exists('operator_panel_conferences'),
'operator_panel_agents' => permission_exists('operator_panel_agents'),
];
// WebSocket settings from default_settings
@@ -470,7 +473,6 @@ body.op-dragging, body.op-dragging * {
#my_extensions_container:not(:empty) {
margin-bottom: 14px;
padding-bottom: 10px;
border-bottom: 1px solid #d0d8e5;
}
/* call group cards */
.op-group-card {
@@ -503,6 +505,21 @@ body.op-dragging, body.op-dragging * {
#extensions_container.op-edit-mode .op-group-card.sortable-ghost {
opacity: .4;
}
/* In edit mode, force hidden headers visible so Sortable has a drag handle */
#extensions_container.op-edit-mode .op-group-card[data-position="hidden"] .op-group-card-header {
display: flex;
min-width: 18px;
padding: 4px 2px;
cursor: grab;
background: #d0d8e5;
writing-mode: vertical-rl;
text-orientation: mixed;
transform: rotate(180deg);
align-items: center;
justify-content: center;
font-size: 11px;
color: #888;
}
/* Card header - default/left side orientation with vertical text */
.op-group-card-header {
background-color: #e5e9f0;
@@ -575,6 +592,7 @@ body.op-dragging, body.op-dragging * {
<!-- Bootstrap tabs: Extensions | Calls | Conferences | Agents -->
<ul class="nav nav-tabs" id="lop_tabs" role="tablist" style="margin-bottom:16px;">
<?php if ($perm['operator_panel_extensions']): ?>
<li class="nav-item" role="presentation">
<button class="nav-link active" id="tab-extensions" data-bs-toggle="tab" data-bs-target="#panel-extensions"
type="button" role="tab" aria-controls="panel-extensions" aria-selected="true">
@@ -582,13 +600,17 @@ body.op-dragging, body.op-dragging * {
<span id="extensions_count" class="badge ms-1" style="background:#6c757d;color:#fff;">0</span>
</button>
</li>
<?php endif; ?>
<?php if ($perm['operator_panel_calls']): ?>
<li class="nav-item" role="presentation">
<button class="nav-link" id="tab-calls" data-bs-toggle="tab" data-bs-target="#panel-calls"
type="button" role="tab" aria-controls="panel-calls" aria-selected="false">
<button class="nav-link<?= !$perm['operator_panel_extensions'] ? ' active' : '' ?>" id="tab-calls" data-bs-toggle="tab" data-bs-target="#panel-calls"
type="button" role="tab" aria-controls="panel-calls" aria-selected="<?= !$perm['operator_panel_extensions'] ? 'true' : 'false' ?>">
<?= htmlspecialchars($text['tab-calls'] ?? 'Calls') ?>
<span id="calls_count" class="badge ms-1" style="background:#6c757d;color:#fff;">0</span>
</button>
</li>
<?php endif; ?>
<?php if ($perm['operator_panel_conferences']): ?>
<li class="nav-item" role="presentation">
<button class="nav-link" id="tab-conferences" data-bs-toggle="tab" data-bs-target="#panel-conferences"
type="button" role="tab" aria-controls="panel-conferences" aria-selected="false">
@@ -596,6 +618,8 @@ body.op-dragging, body.op-dragging * {
<span id="conferences_count" class="badge ms-1" style="background:#6c757d;color:#fff;">0</span>
</button>
</li>
<?php endif; ?>
<?php if ($perm['operator_panel_agents']): ?>
<li class="nav-item" role="presentation">
<button class="nav-link" id="tab-agents" data-bs-toggle="tab" data-bs-target="#panel-agents"
type="button" role="tab" aria-controls="panel-agents" aria-selected="false">
@@ -603,12 +627,14 @@ body.op-dragging, body.op-dragging * {
<span id="agents_count" class="badge ms-1" style="background:#6c757d;color:#fff;">0</span>
</button>
</li>
<?php endif; ?>
</ul>
<div class="tab-content" id="lop_tab_content">
<!-- EXTENSIONS TAB -->
<div class="tab-pane fade show active" id="panel-extensions" role="tabpanel" aria-labelledby="tab-extensions">
<?php if ($perm['operator_panel_extensions']): ?>
<div class="tab-pane fade<?= $perm['operator_panel_extensions'] ? ' show active' : '' ?>" id="panel-extensions" role="tabpanel" aria-labelledby="tab-extensions">
<!-- Group filter bar -->
<div id="extensions_filter_bar" class="op-filter-bar" style="display:none;">
<button type="button" class="op-edit-btn" id="edit_mode_btn" onclick="toggle_edit_mode()" title="<?= htmlspecialchars($text['label-edit_mode'] ?? 'Edit Mode') ?>">
@@ -622,9 +648,11 @@ body.op-dragging, body.op-dragging * {
<p class="text-muted"><?= htmlspecialchars($text['label-connecting'] ?? 'Connecting...') ?></p>
</div>
</div>
<?php endif; ?>
<!-- CALLS TAB -->
<div class="tab-pane fade" id="panel-calls" role="tabpanel" aria-labelledby="tab-calls">
<?php if ($perm['operator_panel_calls']): ?>
<div class="tab-pane fade<?= !$perm['operator_panel_extensions'] && $perm['operator_panel_calls'] ? ' show active' : '' ?>" id="panel-calls" role="tabpanel" aria-labelledby="tab-calls">
<div id="calls_filter_bar" class="op-filter-bar" style="display:none;">
<div id="group_filter_buttons_calls" class="op-group-filters"></div>
<input type="text" id="calls_text_filter" class="op-text-filter" placeholder="<?= htmlspecialchars($text['label-filter'] ?? 'Filter...') ?>" oninput="apply_calls_filters()">
@@ -633,8 +661,10 @@ body.op-dragging, body.op-dragging * {
<p class="text-muted"><?= htmlspecialchars($text['label-connecting'] ?? 'Connecting...') ?></p>
</div>
</div>
<?php endif; ?>
<!-- CONFERENCES TAB -->
<?php if ($perm['operator_panel_conferences']): ?>
<div class="tab-pane fade" id="panel-conferences" role="tabpanel" aria-labelledby="tab-conferences">
<div id="conferences_filter_bar" class="op-filter-bar" style="display:none;">
<div id="group_filter_buttons_conferences" class="op-group-filters"></div>
@@ -644,8 +674,10 @@ body.op-dragging, body.op-dragging * {
<p class="text-muted"><?= htmlspecialchars($text['label-connecting'] ?? 'Connecting...') ?></p>
</div>
</div>
<?php endif; ?>
<!-- AGENTS TAB -->
<?php if ($perm['operator_panel_agents']): ?>
<div class="tab-pane fade" id="panel-agents" role="tabpanel" aria-labelledby="tab-agents">
<div id="agents_filter_bar" class="op-filter-bar" style="display:none;">
<div id="group_filter_buttons_agents" class="op-group-filters"></div>
@@ -655,6 +687,7 @@ body.op-dragging, body.op-dragging * {
<p class="text-muted"><?= htmlspecialchars($text['label-connecting'] ?? 'Connecting...') ?></p>
</div>
</div>
<?php endif; ?>
</div>
@@ -850,10 +850,10 @@ class operator_panel_service extends base_websocket_system_service implements we
* @return array ['success' => bool, 'message' => string]
*/
private function execute_action(string $action, array $payload): array {
$uuid = $payload['uuid'] ?? '';
$uuid = $payload['uuid'] ?? '';
$destination = $payload['destination'] ?? '';
$context = $payload['context'] ?? 'default';
$domain_name = $payload['domain_name'] ?? '';
$context = $payload['context'] ?? ($domain_name !== '' ? $domain_name : 'default');
try {
switch ($action) {
@@ -1006,20 +1006,28 @@ class operator_panel_service extends base_websocket_system_service implements we
if ($source === $dest) {
return ['success' => false, 'message' => 'Cannot call self'];
}
$context = trim((string)($payload['context'] ?? $domain_name ?? 'default'));
if ($context === '') {
$context = 'default';
// The destination gets routed through the domain's dialplan which handles bridging
$originate_cmd = "originate {sip_auto_answer=true,origination_caller_id_number=$source,sip_h_Call-Info=_undef_}user/$source@$domain_name $dest XML $context";
// Log the originate command attempt
$this->debug("Originate: from=$source to=$dest domain=$domain_name context=$context cmd=$originate_cmd");
$fs_response = event_socket::api($originate_cmd);
// Log the response from FreeSWITCH
$this->debug("Originate response: " . json_encode($fs_response));
if ($fs_response === false) {
$this->error("Failed to send originate command to FreeSWITCH");
return ['success' => false, 'message' => 'Failed to send originate command to FreeSWITCH'];
}
$context = preg_replace('/[^a-zA-Z0-9_\-.]/', '', $context);
if ($context === '') {
$context = 'default';
if (is_array($fs_response) && isset($fs_response['-ERR'])) {
$this->error("Originate error: " . $fs_response['-ERR']);
return ['success' => false, 'message' => 'FreeSWITCH error: ' . $fs_response['-ERR']];
}
// Route destination through dialplan so extension features (forward_all,
// follow_me, voicemail, etc.) are evaluated like a normal extension call.
$destination_route = 'loopback/' . $dest . '/' . $context;
event_socket::api("bgapi originate {sip_auto_answer=true,origination_caller_id_number=$source,sip_h_Call-Info=_undef_}user/$source@$domain_name $destination_route");
return ['success' => true, 'message' => 'Call originated'];
return ['success' => true, 'message' => 'Call originated', 'fs_response' => $fs_response];
case 'user_status':
$status = $payload['status'] ?? '';
@@ -1895,8 +1895,11 @@ function toggle_edit_mode() {
if (typeof Sortable !== 'undefined') {
sortable_instance = Sortable.create(container, {
animation: 150,
draggable: '.op-group-card',
handle: '.op-group-card-header',
ghostClass: 'sortable-ghost',
filter: '.op-ext-block, .op-ext-action-icon',
preventOnFilter: false,
onEnd: function() {
save_card_order();
}
@@ -1972,12 +1975,11 @@ function render_extensions_tab() {
// Group remaining extensions by call_group (case-insensitive)
const groups = new Map();
const ungrouped_label = text['label-ungrouped'] || 'Ungrouped';
others.forEach(ext => {
const raw_group = (ext.call_group || '').trim();
const key = raw_group.toLowerCase() || '';
if (!groups.has(key)) {
groups.set(key, { display: raw_group ? to_title_case(raw_group) : ungrouped_label, exts: [] });
groups.set(key, { display: raw_group ? to_title_case(raw_group) : '', exts: [] });
}
groups.get(key).exts.push(ext);
});
@@ -2067,8 +2069,11 @@ function render_extensions_tab() {
if (sortable_instance) sortable_instance.destroy();
sortable_instance = Sortable.create(container, {
animation: 150,
draggable: '.op-group-card',
handle: '.op-group-card-header',
ghostClass: 'sortable-ghost',
filter: '.op-ext-block, .op-ext-action-icon',
preventOnFilter: false,
onEnd: function() { save_card_order(); }
});
}