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 */
|
/* user status: logged out — grey */
|
||||||
.op-ext-logged-out { border-color: #9da5ae; background-color: #e2e3e5; }
|
.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 { 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-info { background-color: #f0f1f2; }
|
||||||
.op-ext-logged-out .op-ext-number { color: #888; }
|
.op-ext-logged-out .op-ext-number { color: #888; }
|
||||||
.op-ext-logged-out .op-ext-name { color: #999; }
|
.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)) {
|
if (!preg_match('/^[0-9*#+]+$/', $destination)) {
|
||||||
return ['success' => false, 'message' => 'Invalid 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'];
|
return ['success' => true, 'message' => 'Call transferred'];
|
||||||
|
|
||||||
case 'eavesdrop':
|
case 'eavesdrop':
|
||||||
@@ -1007,11 +1008,29 @@ class operator_panel_service extends base_websocket_system_service implements we
|
|||||||
return ['success' => false, 'message' => 'Cannot call self'];
|
return ['success' => false, 'message' => 'Cannot call self'];
|
||||||
}
|
}
|
||||||
|
|
||||||
// The destination gets routed through the domain's dialplan which handles bridging
|
// Look up the source extension's user_context for correct dialplan routing
|
||||||
$originate_cmd = "originate {sip_auto_answer=true,origination_caller_id_number=$source,sip_h_Call-Info=_undef_}user/$source@$domain_name $dest XML $context";
|
$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
|
// 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);
|
$fs_response = event_socket::api($originate_cmd);
|
||||||
|
|
||||||
|
|||||||
@@ -1330,7 +1330,21 @@ function action_record(uuid) {
|
|||||||
* @param {string} status
|
* @param {string} status
|
||||||
*/
|
*/
|
||||||
function send_user_status(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) {
|
function action_agent_status(agent_name, status) {
|
||||||
@@ -1381,10 +1395,30 @@ function load_extensions_snapshot() {
|
|||||||
if (ext.extension) extensions_map.set(ext.extension, ext);
|
if (ext.extension) extensions_map.set(ext.extension, ext);
|
||||||
});
|
});
|
||||||
render_extensions_tab();
|
render_extensions_tab();
|
||||||
|
sync_status_buttons();
|
||||||
})
|
})
|
||||||
.catch(console.error);
|
.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.
|
* Derive call state for a given extension number by scanning calls_map.
|
||||||
* @param {string} ext_number
|
* @param {string} ext_number
|
||||||
@@ -1474,6 +1508,8 @@ function render_ext_block(ext, is_mine) {
|
|||||||
css_state = 'op-ext-on-break';
|
css_state = 'op-ext-on-break';
|
||||||
} else if (user_status_raw === 'Available' || user_status_raw === 'Available (On Demand)') {
|
} else if (user_status_raw === 'Available' || user_status_raw === 'Available (On Demand)') {
|
||||||
css_state = 'op-ext-available';
|
css_state = 'op-ext-available';
|
||||||
|
} else if (user_status_raw === 'Logged Out') {
|
||||||
|
css_state = 'op-ext-logged-out';
|
||||||
} else {
|
} else {
|
||||||
// Registered with no explicit/active status — blue
|
// Registered with no explicit/active status — blue
|
||||||
css_state = 'op-ext-registered';
|
css_state = 'op-ext-registered';
|
||||||
@@ -1485,7 +1521,7 @@ function render_ext_block(ext, is_mine) {
|
|||||||
'op-ext-on-break': '#8a6508',
|
'op-ext-on-break': '#8a6508',
|
||||||
'op-ext-dnd': '#a71d2a',
|
'op-ext-dnd': '#a71d2a',
|
||||||
'op-ext-registered': '#2b6cb0',
|
'op-ext-registered': '#2b6cb0',
|
||||||
'op-ext-logged-out': '#6c757d',
|
'op-ext-logged-out': '#1e7e34',
|
||||||
'op-ext-unregistered': '#6c757d',
|
'op-ext-unregistered': '#6c757d',
|
||||||
'op-ext-ringing': '#0e6882',
|
'op-ext-ringing': '#0e6882',
|
||||||
'op-ext-active': '#2a7a2b',
|
'op-ext-active': '#2a7a2b',
|
||||||
@@ -1574,9 +1610,17 @@ function render_ext_block(ext, is_mine) {
|
|||||||
status_icon = 'status_do_not_disturb';
|
status_icon = 'status_do_not_disturb';
|
||||||
status_hover = text['label-status_do_not_disturb'] || 'Do Not Disturb';
|
status_hover = text['label-status_do_not_disturb'] || 'Do Not Disturb';
|
||||||
break;
|
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:
|
default:
|
||||||
if (reg) {
|
if (reg) {
|
||||||
// In this panel, registered-without-explicit-status uses the same icon as Available.
|
|
||||||
status_icon = 'status_available';
|
status_icon = 'status_available';
|
||||||
status_hover = text['label-status_available'] || 'Available';
|
status_hover = text['label-status_available'] || 'Available';
|
||||||
} else {
|
} else {
|
||||||
@@ -2228,7 +2272,11 @@ function on_ext_drop(ext_number, event) {
|
|||||||
dragged_extension = null;
|
dragged_extension = null;
|
||||||
if (!uuid || !ext_number) return;
|
if (!uuid || !ext_number) return;
|
||||||
if (source_ext && source_ext === 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);
|
.catch(console.error);
|
||||||
} else if (dragged_eavesdrop_uuid) {
|
} else if (dragged_eavesdrop_uuid) {
|
||||||
// Eavesdrop an existing call using dropped extension as destination
|
// Eavesdrop an existing call using dropped extension as destination
|
||||||
|
|||||||
Reference in New Issue
Block a user