mirror of
https://github.com/fusionpbx/fusionpbx.git
synced 2026-03-31 13:39:55 +00:00
Fix wrong leg transferred (#7812)
* Fix icon color * Fix dialpad outbound call with _undef_ variable * remove submodule
This commit is contained in:
@@ -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; }
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user