diff --git a/public/assets/app.js b/public/assets/app.js index e941bd2..9257e6c 100644 --- a/public/assets/app.js +++ b/public/assets/app.js @@ -5212,7 +5212,7 @@ function renderPhonebookTable() { const initials = getInitials(c.display_name || '?'); const colorClass = getAvatarBgColor(c.display_name || ''); const avatarHtml = c.photo_url - ? `${escapeHtml(c.display_name)}` + ? `${escapeHtml(c.display_name)}` : `
${escapeHtml(initials)}
`; const typeBadge = c.is_internal @@ -5409,13 +5409,17 @@ function updateAvatarPreview(photoUrl, name) { const clearBtn = document.getElementById('phonebook-btn-clear-photo'); if (!preview) return; + const initials = getInitials(name || '?'); + const colorClass = getAvatarBgColor(name || ''); + if (photoUrl) { - preview.className = 'w-14 h-14 rounded-full overflow-hidden border border-teal-500/40 shrink-0 shadow'; - preview.innerHTML = ``; + preview.className = 'w-14 h-14 rounded-full overflow-hidden border border-teal-500/40 shrink-0 shadow relative flex items-center justify-center'; + preview.innerHTML = ` + + + `; clearBtn?.classList.remove('hidden'); } else { - const initials = getInitials(name || '?'); - const colorClass = getAvatarBgColor(name || ''); preview.className = `w-14 h-14 rounded-full ${colorClass} text-white flex items-center justify-center font-bold text-lg overflow-hidden shrink-0 shadow-inner`; preview.textContent = initials; clearBtn?.classList.add('hidden'); diff --git a/scripts/fix.sh b/scripts/fix.sh index 38980b9..3115584 100755 --- a/scripts/fix.sh +++ b/scripts/fix.sh @@ -57,8 +57,9 @@ echo "-> Droits applicatifs et ACL POSIX (/etc/asterisk et /var/www/nexus-pbx).. usermod -aG asterisk www-data 2>/dev/null || true usermod -aG www-data asterisk 2>/dev/null || true +mkdir -p /var/www/nexus-pbx/public/avatars /var/www/nexus-pbx/storage/sounds /var/www/nexus-pbx/storage/backups /var/www/nexus-pbx/storage/system_backups chown -R www-data:asterisk /var/www/nexus-pbx -chmod 2775 /var/www/nexus-pbx/storage /var/www/nexus-pbx/storage/backups /var/www/nexus-pbx/public/avatars 2>/dev/null || true +chmod 2775 /var/www/nexus-pbx/storage /var/www/nexus-pbx/storage/backups /var/www/nexus-pbx/storage/system_backups /var/www/nexus-pbx/storage/sounds /var/www/nexus-pbx/public/avatars 2>/dev/null || true touch /etc/asterisk/pjsip.conf \ /etc/asterisk/pjsip.custom.conf \ diff --git a/src/BackupManager.php b/src/BackupManager.php index e90b808..5b55983 100644 --- a/src/BackupManager.php +++ b/src/BackupManager.php @@ -257,7 +257,16 @@ class BackupManager } $manifestFiles['asterisk_custom_conf'] = ['count' => count(glob($destConfs . '/*'))]; - // F. Création du Manifeste + // F. Avatars et photos des contacts de l'annuaire (/var/www/nexus-pbx/public/avatars/) + $publicAvatars = '/var/www/nexus-pbx/public/avatars'; + if (is_dir($publicAvatars)) { + $destAvatars = $stageDir . '/public_avatars'; + mkdir($destAvatars, 0755, true); + self::copyDirectoryRecursive($publicAvatars, $destAvatars); + $manifestFiles['public_avatars'] = ['count' => count(glob($destAvatars . '/*'))]; + } + + // G. Création du Manifeste $manifest = [ 'nexus_version' => self::VERSION, 'created_at' => date('c'), @@ -573,6 +582,19 @@ class BackupManager } } + // 9b. Restauration des avatars et photos des contacts de l'annuaire + if (is_dir($stageDir . '/public_avatars')) { + $destAvatars = '/var/www/nexus-pbx/public/avatars'; + if (!is_dir($destAvatars)) { + @mkdir($destAvatars, 0775, true); + } + self::copyDirectoryRecursive($stageDir . '/public_avatars', $destAvatars, true); + @chown($destAvatars, 'www-data'); + @chgrp($destAvatars, 'asterisk'); + exec("chown -R www-data:asterisk " . escapeshellarg($destAvatars)); + exec("chmod -R 775 " . escapeshellarg($destAvatars)); + } + // 10. Régénération intégrale des configurations Asterisk avec la nouvelle base restaurée Database::resetConnection(); $dbFresh = Database::getConnection(true); diff --git a/src/PhonebookManager.php b/src/PhonebookManager.php index 3976482..79f029d 100644 --- a/src/PhonebookManager.php +++ b/src/PhonebookManager.php @@ -133,7 +133,20 @@ class PhonebookManager $sql .= " ORDER BY display_name COLLATE NOCASE ASC"; $stmt = $db->prepare($sql); $stmt->execute($params); - return $stmt->fetchAll(PDO::FETCH_ASSOC); + $contacts = $stmt->fetchAll(PDO::FETCH_ASSOC); + + // Vérification de la présence réelle du fichier sur le disque + foreach ($contacts as &$c) { + if (!empty($c['photo_url'])) { + $filename = basename((string)$c['photo_url']); + if (!file_exists(self::AVATARS_DIR . '/' . $filename)) { + $c['photo_url'] = ''; + } + } + } + unset($c); + + return $contacts; } /** @@ -306,11 +319,14 @@ class PhonebookManager $entry->addChild('Email', htmlspecialchars($email, ENT_XML1, 'UTF-8')); } if ($photo !== '') { - $fullPhotoUrl = (strpos($photo, '://') === false) - ? rtrim($baseUrl, '/') . '/' . ltrim($photo, '/') - : $photo; - $entry->addChild('Photo', htmlspecialchars($fullPhotoUrl, ENT_XML1, 'UTF-8')); - $entry->addChild('Picture', htmlspecialchars($fullPhotoUrl, ENT_XML1, 'UTF-8')); + $photoFile = basename($photo); + if (file_exists(self::AVATARS_DIR . '/' . $photoFile)) { + $fullPhotoUrl = (strpos($photo, '://') === false) + ? rtrim($baseUrl, '/') . '/' . ltrim($photo, '/') + : $photo; + $entry->addChild('Photo', htmlspecialchars($fullPhotoUrl, ENT_XML1, 'UTF-8')); + $entry->addChild('Picture', htmlspecialchars($fullPhotoUrl, ENT_XML1, 'UTF-8')); + } } }