Fix wrong leg transferred (#7812)

* Fix icon color

* Fix dialpad outbound call with _undef_ variable

* remove submodule
This commit is contained in:
frytimo
2026-03-25 17:35:53 -03:00
committed by GitHub
parent e594e95e44
commit d72fb962f9
3 changed files with 76 additions and 9 deletions

View File

@@ -436,7 +436,7 @@ body.op-dragging, body.op-dragging * {
/* user status: logged out — grey */
.op-ext-logged-out { border-color: #9da5ae; background-color: #e2e3e5; }
.op-ext-logged-out .op-ext-icon { background-color: #d6d8db; }
.op-ext-logged-out .op-ext-icon .op-ext-status-icon { color: #6c757d; }
.op-ext-logged-out .op-ext-icon .op-ext-status-icon { color: #1e7e34; }
.op-ext-logged-out .op-ext-info { background-color: #f0f1f2; }
.op-ext-logged-out .op-ext-number { color: #888; }
.op-ext-logged-out .op-ext-name { color: #999; }

View File

@@ -872,7 +872,8 @@ class operator_panel_service extends base_websocket_system_service implements we
if (!preg_match('/^[0-9*#+]+$/', $destination)) {
return ['success' => false, 'message' => 'Invalid destination'];
}
event_socket::api("uuid_transfer $uuid $destination XML $context");
$bleg = !empty($payload['bleg']) ? '-bleg ' : '';
event_socket::api("uuid_transfer $uuid {$bleg}$destination XML $context");
return ['success' => true, 'message' => 'Call transferred'];
case 'eavesdrop':
@@ -1007,11 +1008,29 @@ class operator_panel_service extends base_websocket_system_service implements we
return ['success' => false, 'message' => 'Cannot call self'];
}
// 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";
// Look up the source extension's user_context for correct dialplan routing
$originate_context = $context;
try {
$database = database::new(['config' => parent::$config]);
$rows = $database->select(
"SELECT e.user_context FROM v_extensions AS e "
. "LEFT JOIN v_domains AS d ON e.domain_uuid = d.domain_uuid "
. "WHERE d.domain_name = :domain_name AND e.extension = :extension AND e.enabled = 'true' LIMIT 1",
[':domain_name' => $domain_name, ':extension' => $source],
'all'
);
if (!empty($rows[0]['user_context'])) {
$originate_context = $rows[0]['user_context'];
}
} catch (\Exception $e) {
$this->debug('Could not look up user_context for originate: ' . $e->getMessage());
}
// The destination gets routed through the extension's dialplan context
$originate_cmd = "originate {sip_auto_answer=true,origination_caller_id_number=$source}user/$source@$domain_name $dest XML $originate_context";
// Log the originate command attempt
$this->debug("Originate: from=$source to=$dest domain=$domain_name context=$context cmd=$originate_cmd");
$this->debug("Originate: from=$source to=$dest domain=$domain_name context=$originate_context cmd=$originate_cmd");
$fs_response = event_socket::api($originate_cmd);

View File

@@ -1330,7 +1330,21 @@ function action_record(uuid) {
* @param {string} status
*/
function send_user_status(status) {
send_action('user_status', { status, user_uuid }).catch(console.error);
send_action('user_status', { status, user_uuid })
.then(() => {
// Update local extensions_map so the UI reflects the new status immediately
if (Array.isArray(user_own_extensions)) {
user_own_extensions.forEach(ext_num => {
const ext = extensions_map.get(ext_num);
if (ext) {
ext.user_status = status;
ext.do_not_disturb = (status === 'Do Not Disturb') ? 'true' : 'false';
}
});
}
render_extensions_tab();
})
.catch(console.error);
}
function action_agent_status(agent_name, status) {
@@ -1381,10 +1395,30 @@ function load_extensions_snapshot() {
if (ext.extension) extensions_map.set(ext.extension, ext);
});
render_extensions_tab();
sync_status_buttons();
})
.catch(console.error);
}
/**
* Highlight the status button that matches the logged-in user's current
* status (read from extensions_map for the user's own extension).
*/
function sync_status_buttons() {
if (!Array.isArray(user_own_extensions) || user_own_extensions.length === 0) return;
const ext = extensions_map.get(user_own_extensions[0]);
if (!ext) return;
const current = (ext.user_status || '').trim();
if (!current) return;
document.querySelectorAll('.op-status-btn').forEach(b => {
if (b.getAttribute('data-status') === current) {
b.classList.add('active');
} else {
b.classList.remove('active');
}
});
}
/**
* Derive call state for a given extension number by scanning calls_map.
* @param {string} ext_number
@@ -1474,6 +1508,8 @@ function render_ext_block(ext, is_mine) {
css_state = 'op-ext-on-break';
} else if (user_status_raw === 'Available' || user_status_raw === 'Available (On Demand)') {
css_state = 'op-ext-available';
} else if (user_status_raw === 'Logged Out') {
css_state = 'op-ext-logged-out';
} else {
// Registered with no explicit/active status — blue
css_state = 'op-ext-registered';
@@ -1485,7 +1521,7 @@ function render_ext_block(ext, is_mine) {
'op-ext-on-break': '#8a6508',
'op-ext-dnd': '#a71d2a',
'op-ext-registered': '#2b6cb0',
'op-ext-logged-out': '#6c757d',
'op-ext-logged-out': '#1e7e34',
'op-ext-unregistered': '#6c757d',
'op-ext-ringing': '#0e6882',
'op-ext-active': '#2a7a2b',
@@ -1574,9 +1610,17 @@ function render_ext_block(ext, is_mine) {
status_icon = 'status_do_not_disturb';
status_hover = text['label-status_do_not_disturb'] || 'Do Not Disturb';
break;
case 'Logged Out':
// Use green icon when still registered to indicate the phone is online
if (reg) {
status_icon = 'status_available';
} else {
status_icon = 'status_logged_out';
}
status_hover = text['label-status_logged_out_or_unknown'] || text['label-status_logged_out'] || 'Logged Out';
break;
default:
if (reg) {
// In this panel, registered-without-explicit-status uses the same icon as Available.
status_icon = 'status_available';
status_hover = text['label-status_available'] || 'Available';
} else {
@@ -2228,7 +2272,11 @@ function on_ext_drop(ext_number, event) {
dragged_extension = null;
if (!uuid || !ext_number) return;
if (source_ext && source_ext === ext_number) return;
send_action('transfer', { uuid, destination: ext_number, context: domain_name })
// When dragged from an extension block the UUID is the extension's own
// leg; use -bleg so FreeSWITCH transfers the *other* leg (the caller).
const payload = { uuid, destination: ext_number, context: domain_name };
if (source_ext) payload.bleg = true;
send_action('transfer', payload)
.catch(console.error);
} else if (dragged_eavesdrop_uuid) {
// Eavesdrop an existing call using dropped extension as destination