updated vodia identity

This commit is contained in:
Luca Haid
2025-08-07 13:05:07 +00:00
parent fed87a236c
commit 4313cf70c0
4 changed files with 139 additions and 11 deletions

View File

@@ -414,12 +414,18 @@ class AddressModel {
}
}
if (array_key_exists("pfm", $filter)) {
$pfm = FronkDB::singleton()->escape($filter["pfm"]);
if ($pfm) {
$where .= " AND (phone like '%$pfm%' OR fax like '%$pfm%' OR mobile like '%$pfm%' )";
$pfm = preg_replace('/^(43|0)/', '', preg_replace('/[^0-9]/', '', $filter['pfm'] ?? ''));
if (!empty($pfm)) {
$pfmEscaped = FronkDB::singleton()->escape($pfm);
$searchColumns = ['phone', 'fax', 'mobile'];
$conditions = array_map(function ($column) use ($pfmEscaped) {
$normalizedColumn = "REGEXP_REPLACE(REGEXP_REPLACE(`$column`, '[^0-9]', ''), '^(43|0)', '')";
return "$normalizedColumn LIKE '%$pfmEscaped%'";
}, $searchColumns);
$where .= " AND (" . implode(' OR ', $conditions) . ")";
}
}
if (array_key_exists("billing_type", $filter)) {
$billing_type = FronkDB::singleton()->escape($filter["billing_type"]);

View File

@@ -441,6 +441,9 @@ class UserController extends mfBaseController
case "setVodiaIdentity":
$return = $this->setVodiaIdentityApi();
break;
case "getVodiaCall":
$return = $this->getVodiaCallApi();
break;
default:
$return = false;
}
@@ -528,6 +531,44 @@ class UserController extends mfBaseController
}
private function getVodiaCallApi() {
if(!ENABLE_VODIA_IDENTITY_SWITCHER) {
return ["enabled" => false];
}
$domain = $this->me->getFlag("vodia_identity_domain")->value();
$username = $this->me->getFlag("vodia_identity_username")->value();
if(!$domain || !$username) {
return ["enabled" => false];
}
$vodia = new Vodia_Api(VODIA_API_URL, VODIA_API_ADMIN_USER, VODIA_API_ADMIN_PASS);
$calls = $vodia->getActiveCalls($domain, $username);
if(!$calls) {
return ["enabled" => true, "calls" => []];
}
$from = null;
foreach($calls as $call) {
if(isset($call['from'])) {
$from = $call['from'];
if(preg_match('/<sip:([^@]+)@/', $from, $m)) {
$from = $m[1];
} elseif(preg_match('/<([^>]+)>/', $from, $m)) {
$from = $m[1];
} else {
$from = str_replace('"', '', $from);
}
break; // only return the first call's from number
}
}
return ["enabled" => true, "number" => $from];
}
private function startSuperexpertApi() {
$me = new User();
$me->loadMe();

View File

@@ -145,4 +145,32 @@ class Vodia_Api {
return $user_settings;
}
public function getActiveCalls($domain, $user) {
if(!$this->session_id) {
$this->_authenticate($domain);
}
$url = $this->baseurl.VODIA_API_EP_GET_USER_CALLS;
$url = str_replace("{DOMAIN}", $domain, $url);
$url = str_replace("{EXT}", $user, $url);
$ctx_options = [
"http" => [
"ignore_errors" => true,
"method" => "GET",
"header" => [
"Cookie: session=".$this->session_id,
"Accept: application/json",
"Content-Type: application/json",
],
]
];
$ctx = stream_context_create($ctx_options);
$output = file_get_contents($url, false, $ctx);
return json_decode($output, true);
}
}

View File

@@ -1,6 +1,14 @@
document.body.insertAdjacentHTML('beforeend', `
<template id="vodia-identity-template">
<li id="vodia-identity-container" class="dropdown notification-list ml-2 my-auto" style="display: none;">
<a href="#" class="nav-link nav-user d-flex align-items-center" data-ref="callLookupButton" title="Aktuellen Anrufer nachschlagen" style="padding: 0!important;min-width: unset!important">
<span class="fa-stack" data-ref="callLookupIconStack">
<i class="fa-solid fa-phone fa-stack-1x" style="left: -5px; top: 6px;"></i>
<i class="fa-solid fa-magnifying-glass fa-stack-1x" style="transform: scale(0.7);"></i>
</span>
<i class="fas fa-spinner fa-spin d-none" data-ref="callLookupSpinner" style="margin-right: 10px"></i>
</a>
<a href="#" class="nav-link nav-user dropdown-toggle d-flex align-items-center" aria-haspopup="true" aria-expanded="false" data-ref="toggleButton">
<i class="phone-icon fas fa-phone" data-ref="phoneIcon"></i>
<div class="pro-user-name ml-2">
@@ -40,7 +48,11 @@ class VodiaIdentitySwitcher {
setting: "Ändere...",
dropdownTitle: "Ausgehende Identität wählen:",
ownExtension: "Eigene Nummer",
customIdentity: "Andere Nummer"
customIdentity: "Andere Nummer",
// ## START: ADDED TEXT ##
noActiveCall: "Kein aktiver Anruf gefunden.",
lookupError: "Fehler bei der Anrufabfrage."
// ## END: ADDED TEXT ##
};
pollingTimer = null;
@@ -69,7 +81,6 @@ class VodiaIdentitySwitcher {
return JSON.parse(cachedString);
} catch (e) {
console.error("Vodia Cache Error: Could not parse cached data.", e);
// Clean up corrupted data
localStorage.removeItem(this.CACHE_KEY);
return null;
}
@@ -90,6 +101,9 @@ class VodiaIdentitySwitcher {
this.elements = {
container,
callLookupButton: container.querySelector('[data-ref="callLookupButton"]'),
callLookupIconStack: container.querySelector('[data-ref="callLookupIconStack"]'),
callLookupSpinner: container.querySelector('[data-ref="callLookupSpinner"]'),
toggleButton: container.querySelector('[data-ref="toggleButton"]'),
phoneIcon: container.querySelector('[data-ref="phoneIcon"]'),
currentName: container.querySelector('[data-ref="currentName"]'),
@@ -115,11 +129,51 @@ class VodiaIdentitySwitcher {
this.elements.toggleButton.setAttribute('aria-expanded', 'false');
}
});
this.elements.callLookupButton.addEventListener('click', e => {
e.preventDefault();
this._handleCallLookup();
});
}
_setLookupButtonLoadingState(isLoading) {
const { callLookupButton, callLookupIconStack, callLookupSpinner } = this.elements;
if (isLoading) {
callLookupButton.setAttribute('disabled', 'true');
callLookupIconStack.classList.add('d-none'); // Hide the icon stack
callLookupSpinner.classList.remove('d-none'); // Show the spinner
} else {
callLookupButton.removeAttribute('disabled');
callLookupIconStack.classList.remove('d-none'); // Show the icon stack
callLookupSpinner.classList.add('d-none'); // Hide the spinner
}
}
async _handleCallLookup() {
this._setLookupButtonLoadingState(true);
try {
const response = await fetch(`${this.API_BASE_URL}User/Api/do=getVodiaCall`);
if (!response.ok) throw new Error('Network response was not ok');
const data = await response.json();
if (data.status === 'OK' && data.result.number && data.result.number.length >= 5) {
const url = `${this.API_BASE_URL}Address/Index?filter[pfm]=${data.result.number}`;
window.open(url, '_blank'); // Open address book in a new tab
} else {
if (window.notify) window.notify('info', this.TEXT.noActiveCall);
}
} catch (error) {
console.error("Vodia Call Lookup Error:", error.message);
if (window.notify) window.notify('error', this.TEXT.lookupError);
} finally {
this._setLookupButtonLoadingState(false);
}
}
// ## END: ADDED METHOD TO HANDLE CALL LOOKUP ##
_setLoadingState(isLoading, message = '') {
const { phoneIcon, currentName, currentNumber } = this.elements;
phoneIcon.className = 'phone-icon fas fa-phone'; // Reset
phoneIcon.className = 'phone-icon fas fa-phone';
if (isLoading) {
phoneIcon.classList.add('fa-spin', 'text-warning');
@@ -152,7 +206,7 @@ class VodiaIdentitySwitcher {
}
if (vodiaState?.enabled) {
this.elements.container.style.display = 'list-item';
this.elements.container.style.display = 'flex'; // Use flex to align items
this._updateUI(vodiaState);
this._setLoadingState(false);
} else {
@@ -221,7 +275,6 @@ class VodiaIdentitySwitcher {
const { 'default': defaultDisplay, default_number, current, identities } = vodiaState;
const currentId = current.replaceAll(' ', "");
// Add "Own Extension"
this.elements.identityList.appendChild(this._createListItem({
name: this.TEXT.ownExtension,
number: default_number.replaceAll(' ', ""),
@@ -230,7 +283,6 @@ class VodiaIdentitySwitcher {
isActive: currentId === default_number.replaceAll(' ', "")
}));
// Add other identities
if (!identities) return;
for (const name in identities) {
const ident = identities[name];
@@ -262,6 +314,7 @@ class VodiaIdentitySwitcher {
return item;
}
}
document.addEventListener('DOMContentLoaded', () => {
const topbar = document.querySelector("#topbar");
if (topbar) new VodiaIdentitySwitcher(topbar);