feat(trunk): add customizable Asterisk Trunk Dial Options field with quick presets (tTR, TR, T)

This commit is contained in:
sorcier committed 2026-11-04 00:18:37 +01:00
1 parent bf2e2098f0
commit b3c98834c8
5 files changed
+43 -3

No files matched your search

+6 -2
View File
@@ -1133,6 +1133,7 @@ try {
$custom_aor_params = trim((string)($payload['custom_aor_params'] ?? '')); $custom_aor_params = trim((string)($payload['custom_aor_params'] ?? ''));
$moh_sound_id = max(0, (int)($payload['moh_sound_id'] ?? 0)); $moh_sound_id = max(0, (int)($payload['moh_sound_id'] ?? 0));
$cid_name_prefix = trim((string)($payload['cid_name_prefix'] ?? '')); $cid_name_prefix = trim((string)($payload['cid_name_prefix'] ?? ''));
$dial_options = preg_replace('/[^a-zA-Z0-9(),]/', '', trim((string)($payload['dial_options'] ?? 'tTR'))) ?: 'tTR';
$enabled = isset($payload['enabled']) ? (int)!empty($payload['enabled']) : 1; $enabled = isset($payload['enabled']) ? (int)!empty($payload['enabled']) : 1;
$didRoutesRaw = is_array($payload['did_routes'] ?? null) ? $payload['did_routes'] : []; $didRoutesRaw = is_array($payload['did_routes'] ?? null) ? $payload['did_routes'] : [];
@@ -1203,6 +1204,7 @@ try {
':custom_aor_params' => $custom_aor_params, ':custom_aor_params' => $custom_aor_params,
':moh_sound_id' => $moh_sound_id, ':moh_sound_id' => $moh_sound_id,
':cid_name_prefix' => $cid_name_prefix, ':cid_name_prefix' => $cid_name_prefix,
':dial_options' => $dial_options,
':enabled' => $enabled, ':enabled' => $enabled,
]; ];
@@ -1230,6 +1232,7 @@ try {
outbound_prefix = :outbound_prefix, custom_endpoint_params = :custom_endpoint_params, outbound_prefix = :outbound_prefix, custom_endpoint_params = :custom_endpoint_params,
custom_reg_params = :custom_reg_params, custom_aor_params = :custom_aor_params, custom_reg_params = :custom_reg_params, custom_aor_params = :custom_aor_params,
moh_sound_id = :moh_sound_id, cid_name_prefix = :cid_name_prefix, moh_sound_id = :moh_sound_id, cid_name_prefix = :cid_name_prefix,
dial_options = :dial_options,
enabled = :enabled enabled = :enabled
WHERE id = :id WHERE id = :id
'); ');
@@ -1245,7 +1248,7 @@ try {
qualify_frequency, max_channels, timers, language, rtp_keepalive, t38_udptl, qualify_frequency, max_channels, timers, language, rtp_keepalive, t38_udptl,
fax_detect, inband_progress, match_ips, context, inbound_dest, did_routes_json, fax_detect, inband_progress, match_ips, context, inbound_dest, did_routes_json,
outbound_prefix, custom_endpoint_params, custom_reg_params, custom_aor_params, moh_sound_id, outbound_prefix, custom_endpoint_params, custom_reg_params, custom_aor_params, moh_sound_id,
cid_name_prefix, enabled cid_name_prefix, dial_options, enabled
) VALUES ( ) VALUES (
:name, :host, :port, :outbound_proxy, :transport, :auth_mode, :username, :auth_username, :secret, :name, :host, :port, :outbound_proxy, :transport, :auth_mode, :username, :auth_username, :secret,
:registration_mode, :register, :server_uri, :client_uri, :contact_user, :expiration, :retry_interval, :registration_mode, :register, :server_uri, :client_uri, :contact_user, :expiration, :retry_interval,
@@ -1255,7 +1258,7 @@ try {
:qualify_frequency, :max_channels, :timers, :language, :rtp_keepalive, :t38_udptl, :qualify_frequency, :max_channels, :timers, :language, :rtp_keepalive, :t38_udptl,
:fax_detect, :inband_progress, :match_ips, :context, :inbound_dest, :did_routes_json, :fax_detect, :inband_progress, :match_ips, :context, :inbound_dest, :did_routes_json,
:outbound_prefix, :custom_endpoint_params, :custom_reg_params, :custom_aor_params, :moh_sound_id, :outbound_prefix, :custom_endpoint_params, :custom_reg_params, :custom_aor_params, :moh_sound_id,
:cid_name_prefix, :enabled :cid_name_prefix, :dial_options, :enabled
) )
ON CONFLICT(name) DO UPDATE SET ON CONFLICT(name) DO UPDATE SET
host = excluded.host, port = excluded.port, outbound_proxy = excluded.outbound_proxy, host = excluded.host, port = excluded.port, outbound_proxy = excluded.outbound_proxy,
@@ -1279,6 +1282,7 @@ try {
custom_reg_params = excluded.custom_reg_params, custom_aor_params = excluded.custom_aor_params, custom_reg_params = excluded.custom_reg_params, custom_aor_params = excluded.custom_aor_params,
moh_sound_id = excluded.moh_sound_id, moh_sound_id = excluded.moh_sound_id,
cid_name_prefix = excluded.cid_name_prefix, cid_name_prefix = excluded.cid_name_prefix,
dial_options = excluded.dial_options,
enabled = excluded.enabled enabled = excluded.enabled
'); ');
$stmt->execute($params); $stmt->execute($params);
+11
View File
@@ -2032,6 +2032,13 @@ function syncTrunkHostDomain(hostVal) {
form.elements['from_domain'].value = ''; form.elements['from_domain'].value = '';
} }
function setTrunkDialOptionsPreset(val) {
const form = document.getElementById('form-trunk');
if (form && form.elements['dial_options']) {
form.elements['dial_options'].value = val;
}
}
function populateInboundDestDropdown(selectedValue = '101') { function populateInboundDestDropdown(selectedValue = '101') {
const select = document.getElementById('trunk-inbound-select'); const select = document.getElementById('trunk-inbound-select');
const form = document.getElementById('form-trunk'); const form = document.getElementById('form-trunk');
@@ -2096,6 +2103,7 @@ function parseFreePbxConfig() {
if (val.includes('toheader')) form.elements['context'].value = 'from-pstn-toheader'; if (val.includes('toheader')) form.elements['context'].value = 'from-pstn-toheader';
else form.elements['context'].value = 'from-external'; else form.elements['context'].value = 'from-external';
} }
else if (key === 'dialopts' || key === 'dial_options' || key === 'asterisk_trunk_dial_options') form.elements['dial_options'].value = val;
else if (key === 'outboundproxy') form.elements['outbound_proxy'].value = val; else if (key === 'outboundproxy') form.elements['outbound_proxy'].value = val;
}); });
@@ -2324,6 +2332,9 @@ function openTrunkModal(trunk = null) {
if (form.elements['cid_name_prefix']) { if (form.elements['cid_name_prefix']) {
form.elements['cid_name_prefix'].value = trunk ? (trunk.cid_name_prefix || '') : ''; form.elements['cid_name_prefix'].value = trunk ? (trunk.cid_name_prefix || '') : '';
} }
if (form.elements['dial_options']) {
form.elements['dial_options'].value = trunk ? (trunk.dial_options || 'tTR') : 'tTR';
}
populateInboundDestDropdown(trunk ? (trunk.inbound_dest || '101') : '101'); populateInboundDestDropdown(trunk ? (trunk.inbound_dest || '101') : '101');
// Musique d'attente (MoH) spécifique à ce Trunk // Musique d'attente (MoH) spécifique à ce Trunk
+22
View File
@@ -3517,6 +3517,28 @@ $currentUser = Auth::getUsername();
</label> </label>
</div> </div>
</div> </div>
<!-- Options d'appel Asterisk (Trunk Dial Options style FreePBX) -->
<div class="p-3.5 rounded-xl bg-anthracite-950 border border-anthracite-700/80 flex flex-col sm:flex-row sm:items-center justify-between gap-3">
<div class="max-w-xl">
<label class="block text-xs font-bold text-white mb-0.5 flex items-center gap-1.5">
<span>📞 Options d'appel Asterisk (Asterisk Trunk Dial Options)</span>
<span class="px-1.5 py-0.2 rounded bg-indigo-500/20 text-indigo-300 text-[10px] font-mono font-semibold">FreePBX Dial Options</span>
</label>
<p class="text-[11px] text-slate-300">
Paramètres transmis à l'application Asterisk <code class="text-indigo-300">Dial()</code> pour ce Trunk. <code class="text-emerald-300">R</code> génère la tonalité de sonnerie locale (indispensable pour les passerelles analogiques/FXO type HT813/Livebox). <code class="text-indigo-300">T/t</code> autorise le transfert DTMF.
</p>
</div>
<div class="flex items-center gap-2">
<input type="text" name="dial_options" value="tTR" placeholder="tTR"
class="w-24 px-3 py-1.5 rounded-lg bg-anthracite-900 border border-anthracite-700 text-xs text-white font-mono font-bold text-center focus:border-indigo-500">
<div class="flex items-center gap-1">
<button type="button" onclick="setTrunkDialOptionsPreset('tTR')" class="px-2 py-1 rounded bg-anthracite-800 hover:bg-anthracite-700 text-[11px] text-slate-300 hover:text-white border border-anthracite-600 transition" title="Par défaut recommandé (Transfert DTMF + Tonalité locale)">tTR</button>
<button type="button" onclick="setTrunkDialOptionsPreset('TR')" class="px-2 py-1 rounded bg-emerald-500/20 hover:bg-emerald-500/30 text-[11px] text-emerald-300 border border-emerald-500/40 transition" title="Style FreePBX standard pour passerelles FXO">TR</button>
<button type="button" onclick="setTrunkDialOptionsPreset('T')" class="px-2 py-1 rounded bg-indigo-500/20 hover:bg-indigo-500/30 text-[11px] text-indigo-300 border border-indigo-500/40 transition" title="Standard SIP sans forçage sonnerie">T</button>
</div>
</div>
</div>
</div> </div>
<!-- SOUS-ONGLET 2 : ROUTES ENTRANTES & FILTRAGE PAR ID APPELANT (INBOUND ROUTES) --> <!-- SOUS-ONGLET 2 : ROUTES ENTRANTES & FILTRAGE PAR ID APPELANT (INBOUND ROUTES) -->
+2 -1
View File
@@ -1174,8 +1174,9 @@ class ConfigGenerator
$tEpName = ($tObj && ($tObj['registration_mode'] ?? '') === 'receive' && !empty($tObj['username'])) $tEpName = ($tObj && ($tObj['registration_mode'] ?? '') === 'receive' && !empty($tObj['username']))
? $tObj['username'] ? $tObj['username']
: "trunk-{$tSlug}"; : "trunk-{$tSlug}";
$dialOpts = ($tObj && !empty($tObj['dial_options'])) ? trim((string)$tObj['dial_options']) : 'tTR';
$sub[] = " same => n,NoOp(Route [{$rName}] - Tentative Trunk #{$validTrunkCount} [{$tEpName}] vers \${OUTNUM})"; $sub[] = " same => n,NoOp(Route [{$rName}] - Tentative Trunk #{$validTrunkCount} [{$tEpName}] vers \${OUTNUM})";
$sub[] = " same => n,Dial(PJSIP/\${OUTNUM}@{$tEpName},60,tTR)"; $sub[] = " same => n,Dial(PJSIP/\${OUTNUM}@{$tEpName},60,{$dialOpts})";
$sub[] = " same => n,GotoIf(\$[\"\${DIALSTATUS}\" = \"ANSWER\" | \"\${DIALSTATUS}\" = \"BUSY\" | \"\${DIALSTATUS}\" = \"NOANSWER\" | \"\${DIALSTATUS}\" = \"CANCEL\"]?call_done)"; $sub[] = " same => n,GotoIf(\$[\"\${DIALSTATUS}\" = \"ANSWER\" | \"\${DIALSTATUS}\" = \"BUSY\" | \"\${DIALSTATUS}\" = \"NOANSWER\" | \"\${DIALSTATUS}\" = \"CANCEL\"]?call_done)";
$sub[] = " same => n,NoOp(Trunk [{$tEpName}] indisponible [\${DIALSTATUS}] -> Passage au Trunk / Route suivant)"; $sub[] = " same => n,NoOp(Trunk [{$tEpName}] indisponible [\${DIALSTATUS}] -> Passage au Trunk / Route suivant)";
} }
+2
View File
@@ -164,6 +164,7 @@ class Database
outbound_prefix VARCHAR(32) NOT NULL DEFAULT '', outbound_prefix VARCHAR(32) NOT NULL DEFAULT '',
custom_endpoint_params TEXT NOT NULL DEFAULT '', custom_endpoint_params TEXT NOT NULL DEFAULT '',
cid_name_prefix VARCHAR(64) NOT NULL DEFAULT '', cid_name_prefix VARCHAR(64) NOT NULL DEFAULT '',
dial_options VARCHAR(32) NOT NULL DEFAULT 'tTR',
enabled INTEGER NOT NULL DEFAULT 1 CHECK (enabled IN (0, 1)), enabled INTEGER NOT NULL DEFAULT 1 CHECK (enabled IN (0, 1)),
created_at DATETIME DEFAULT CURRENT_TIMESTAMP created_at DATETIME DEFAULT CURRENT_TIMESTAMP
); );
@@ -578,6 +579,7 @@ class Database
'custom_aor_params' => "TEXT NOT NULL DEFAULT ''", 'custom_aor_params' => "TEXT NOT NULL DEFAULT ''",
'moh_sound_id' => "INTEGER NOT NULL DEFAULT 0", 'moh_sound_id' => "INTEGER NOT NULL DEFAULT 0",
'cid_name_prefix' => "VARCHAR(64) NOT NULL DEFAULT ''", 'cid_name_prefix' => "VARCHAR(64) NOT NULL DEFAULT ''",
'dial_options' => "VARCHAR(32) NOT NULL DEFAULT 'tTR'",
]; ];
foreach ($extraColumns as $colName => $colDef) { foreach ($extraColumns as $colName => $colDef) {