diff --git a/public/api.php b/public/api.php index 9149093..a8be442 100644 --- a/public/api.php +++ b/public/api.php @@ -544,6 +544,14 @@ try { if ($name === '') { throw new InvalidArgumentException("Le nom du groupe d'appel est obligatoire."); } + if (str_starts_with($failover, 'vm-')) { + $vmTarget = substr($failover, 3); + $vmStmt = $db->prepare('SELECT voicemail_enabled FROM extensions WHERE extension = :ext AND enabled = 1'); + $vmStmt->execute([':ext' => $vmTarget]); + if (!(int)$vmStmt->fetchColumn()) { + throw new InvalidArgumentException("Le répondeur du poste {$vmTarget} n'existe pas ou n'est pas activé."); + } + } if ($id) { $stmt = $db->prepare(' diff --git a/public/assets/app.js b/public/assets/app.js index 7ade917..8568ae4 100644 --- a/public/assets/app.js +++ b/public/assets/app.js @@ -1234,6 +1234,9 @@ function renderGroupsTable() { const badges = members.length ? members.map(m => `${escapeHtml(m)}`).join(' ') : 'Aucun membre'; + const failoverLabel = String(grp.failover_dest || '').startsWith('vm-') + ? `📬 Répondeur ${escapeHtml(String(grp.failover_dest).slice(3))}` + : `${escapeHtml(grp.failover_dest)}`; return ` @@ -1241,7 +1244,7 @@ function renderGroupsTable() { ${escapeHtml(grp.name)} ${escapeHtml(grp.strategy)}
${badges}
- ${Number(grp.ring_time)}s → ${escapeHtml(grp.failover_dest)} + ${Number(grp.ring_time)}s → ${failoverLabel} @@ -1893,6 +1896,21 @@ function buildDestinationOptionsHtml(selectedValue = 'ext-101') { html += ``; } + const vmExts = exts.filter(e => Number(e.voicemail_enabled) === 1); + const hasMissingVm = normVal.startsWith('vm-') && !vmExts.some(e => `vm-${e.extension}` === normVal); + if (vmExts.length > 0 || hasMissingVm) { + html += ``; + if (hasMissingVm) { + const missingExt = normVal.slice(3); + html += ``; + } + vmExts.forEach(e => { + const val = `vm-${e.extension}`; + html += ``; + }); + html += ``; + } + // Piège à Spammeurs / Musique d'attente en boucle continue (MoH) const mohSounds = sounds.filter(s => s.usage_type === 'moh'); html += ``; @@ -1948,6 +1966,8 @@ function styleDestinationSelect(selectEl) { selectEl.className = 'trunk-did-dest w-full px-2.5 py-1.5 rounded-lg bg-fuchsia-500/15 border border-fuchsia-500/40 text-xs text-fuchsia-300 font-semibold'; } else if (val.startsWith('grp-')) { selectEl.className = 'trunk-did-dest w-full px-2.5 py-1.5 rounded-lg bg-cyan-500/10 border border-cyan-500/40 text-xs text-cyan-300 font-semibold'; + } else if (val.startsWith('vm-')) { + selectEl.className = 'trunk-did-dest w-full px-2.5 py-1.5 rounded-lg bg-violet-500/15 border border-violet-500/40 text-xs text-violet-300 font-semibold'; } else { selectEl.className = 'trunk-did-dest w-full px-2.5 py-1.5 rounded-lg bg-emerald-500/10 border border-emerald-500/40 text-xs text-emerald-300 font-semibold'; } diff --git a/src/ConfigGenerator.php b/src/ConfigGenerator.php index 6ef1b0d..eae062a 100644 --- a/src/ConfigGenerator.php +++ b/src/ConfigGenerator.php @@ -1686,6 +1686,30 @@ class ConfigGenerator $lines[] = " same => n,Hangup()"; $lines[] = ""; + // 5b. Contexte dédié Dépôt Direct sur Boîte Vocale (Redirection sans sonnerie) + $lines[] = "; ==========================================================================="; + $lines[] = "; DÉPÔT DIRECT SUR BOÎTE VOCALE (REDIRECTION / DÉBORDEMENT)"; + $lines[] = "; ==========================================================================="; + $lines[] = "[app-voicemail]"; + foreach ($extensions as $ext) { + $num = trim((string)$ext['extension']); + if (empty($ext['voicemail_enabled'])) { + continue; + } + $lines[] = "exten => {$num},1,NoOp(Redirection directe vers messagerie vocale du poste {$num})"; + $lines[] = " same => n,Set(CHANNEL(language)={$sysLang})"; + $lines[] = " same => n,Answer()"; + $lines[] = " same => n,Wait(0.5)"; + $lines[] = " same => n,VoiceMail({$num}@default,u)"; + $lines[] = " same => n,Hangup()"; + } + $lines[] = "exten => _X.,1,NoOp(Boite vocale inexistante ou desactivee pour \${EXTEN})"; + $lines[] = " same => n,Set(CHANNEL(language)={$sysLang})"; + $lines[] = " same => n,Answer()"; + $lines[] = " same => n,Playback(vm-incorrect-mailbox)"; + $lines[] = " same => n,Hangup()"; + $lines[] = ""; + return implode("\n", $lines) . "\n"; } @@ -2328,6 +2352,17 @@ EOF; if (preg_match('/^\d+$/', $dest)) { return "Goto(from-internal,{$dest},1)"; } + if (str_starts_with($dest, 'vm-')) { + $vmExt = preg_replace('/[^0-9A-Za-z_-]/', '', substr($dest, 3)); + if ($vmExt !== '') { + $stmt = $this->db->prepare('SELECT voicemail_enabled FROM extensions WHERE extension = :ext AND enabled = 1'); + $stmt->execute([':ext' => $vmExt]); + if ($stmt->fetchColumn()) { + return "Goto(app-voicemail,{$vmExt},1)"; + } + } + return 'Goto(app-terminate,hangup,1)'; + } if (str_starts_with($dest, 'page-')) { $pageExt = substr($dest, 5); return "Goto(from-internal,{$pageExt},1)";