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(initials)}
`
: `${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 = `
+
+ ${escapeHtml(initials)}
+ `;
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'));
+ }
}
}