Fix: include public/avatars in backups and restoration, add graceful fallback for missing contact photos
This commit is contained in:
1 parent
2bd1ae0777
commit
45011d8c59
4 files changed
+52
-9
No files matched your search
+10
-6
@@ -5212,7 +5212,7 @@ function renderPhonebookTable() {
|
|||||||
const initials = getInitials(c.display_name || '?');
|
const initials = getInitials(c.display_name || '?');
|
||||||
const colorClass = getAvatarBgColor(c.display_name || '');
|
const colorClass = getAvatarBgColor(c.display_name || '');
|
||||||
const avatarHtml = c.photo_url
|
const avatarHtml = c.photo_url
|
||||||
? `<img src="${escapeHtml(c.photo_url)}" alt="${escapeHtml(c.display_name)}" class="w-8 h-8 rounded-full object-cover border border-anthracite-700 shadow">`
|
? `<img src="${escapeHtml(c.photo_url)}" alt="${escapeHtml(c.display_name)}" class="w-8 h-8 rounded-full object-cover border border-anthracite-700 shadow" onerror="this.style.display='none'; if(this.nextElementSibling) this.nextElementSibling.style.display='flex';"><div class="w-8 h-8 rounded-full ${colorClass} text-white items-center justify-center font-bold text-xs shadow-inner" style="display:none;">${escapeHtml(initials)}</div>`
|
||||||
: `<div class="w-8 h-8 rounded-full ${colorClass} text-white flex items-center justify-center font-bold text-xs shadow-inner">${escapeHtml(initials)}</div>`;
|
: `<div class="w-8 h-8 rounded-full ${colorClass} text-white flex items-center justify-center font-bold text-xs shadow-inner">${escapeHtml(initials)}</div>`;
|
||||||
|
|
||||||
const typeBadge = c.is_internal
|
const typeBadge = c.is_internal
|
||||||
@@ -5409,13 +5409,17 @@ function updateAvatarPreview(photoUrl, name) {
|
|||||||
const clearBtn = document.getElementById('phonebook-btn-clear-photo');
|
const clearBtn = document.getElementById('phonebook-btn-clear-photo');
|
||||||
if (!preview) return;
|
if (!preview) return;
|
||||||
|
|
||||||
if (photoUrl) {
|
|
||||||
preview.className = 'w-14 h-14 rounded-full overflow-hidden border border-teal-500/40 shrink-0 shadow';
|
|
||||||
preview.innerHTML = `<img src="${escapeHtml(photoUrl)}" class="w-full h-full object-cover">`;
|
|
||||||
clearBtn?.classList.remove('hidden');
|
|
||||||
} else {
|
|
||||||
const initials = getInitials(name || '?');
|
const initials = getInitials(name || '?');
|
||||||
const colorClass = getAvatarBgColor(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 relative flex items-center justify-center';
|
||||||
|
preview.innerHTML = `
|
||||||
|
<img src="${escapeHtml(photoUrl)}" class="w-full h-full object-cover" onerror="this.style.display='none'; if(this.nextElementSibling) this.nextElementSibling.style.display='flex';">
|
||||||
|
<div class="w-full h-full ${colorClass} text-white items-center justify-center font-bold text-lg shadow-inner" style="display:none;">${escapeHtml(initials)}</div>
|
||||||
|
`;
|
||||||
|
clearBtn?.classList.remove('hidden');
|
||||||
|
} else {
|
||||||
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.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;
|
preview.textContent = initials;
|
||||||
clearBtn?.classList.add('hidden');
|
clearBtn?.classList.add('hidden');
|
||||||
|
|||||||
+2
-1
@@ -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 asterisk www-data 2>/dev/null || true
|
||||||
usermod -aG www-data asterisk 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
|
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 \
|
touch /etc/asterisk/pjsip.conf \
|
||||||
/etc/asterisk/pjsip.custom.conf \
|
/etc/asterisk/pjsip.custom.conf \
|
||||||
|
|||||||
+23
-1
@@ -257,7 +257,16 @@ class BackupManager
|
|||||||
}
|
}
|
||||||
$manifestFiles['asterisk_custom_conf'] = ['count' => count(glob($destConfs . '/*'))];
|
$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 = [
|
$manifest = [
|
||||||
'nexus_version' => self::VERSION,
|
'nexus_version' => self::VERSION,
|
||||||
'created_at' => date('c'),
|
'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
|
// 10. Régénération intégrale des configurations Asterisk avec la nouvelle base restaurée
|
||||||
Database::resetConnection();
|
Database::resetConnection();
|
||||||
$dbFresh = Database::getConnection(true);
|
$dbFresh = Database::getConnection(true);
|
||||||
|
|||||||
@@ -133,7 +133,20 @@ class PhonebookManager
|
|||||||
$sql .= " ORDER BY display_name COLLATE NOCASE ASC";
|
$sql .= " ORDER BY display_name COLLATE NOCASE ASC";
|
||||||
$stmt = $db->prepare($sql);
|
$stmt = $db->prepare($sql);
|
||||||
$stmt->execute($params);
|
$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,6 +319,8 @@ class PhonebookManager
|
|||||||
$entry->addChild('Email', htmlspecialchars($email, ENT_XML1, 'UTF-8'));
|
$entry->addChild('Email', htmlspecialchars($email, ENT_XML1, 'UTF-8'));
|
||||||
}
|
}
|
||||||
if ($photo !== '') {
|
if ($photo !== '') {
|
||||||
|
$photoFile = basename($photo);
|
||||||
|
if (file_exists(self::AVATARS_DIR . '/' . $photoFile)) {
|
||||||
$fullPhotoUrl = (strpos($photo, '://') === false)
|
$fullPhotoUrl = (strpos($photo, '://') === false)
|
||||||
? rtrim($baseUrl, '/') . '/' . ltrim($photo, '/')
|
? rtrim($baseUrl, '/') . '/' . ltrim($photo, '/')
|
||||||
: $photo;
|
: $photo;
|
||||||
@@ -313,6 +328,7 @@ class PhonebookManager
|
|||||||
$entry->addChild('Picture', htmlspecialchars($fullPhotoUrl, ENT_XML1, 'UTF-8'));
|
$entry->addChild('Picture', htmlspecialchars($fullPhotoUrl, ENT_XML1, 'UTF-8'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$dom = dom_import_simplexml($xml)->ownerDocument;
|
$dom = dom_import_simplexml($xml)->ownerDocument;
|
||||||
$dom->formatOutput = true;
|
$dom->formatOutput = true;
|
||||||
|
|||||||
Reference in new issue
Block a user