diff --git a/api/pexels.php b/api/pexels.php index ce51ef0..990b07a 100644 --- a/api/pexels.php +++ b/api/pexels.php @@ -6,7 +6,8 @@ $action = $_GET['action'] ?? 'search'; if ($action === 'search') { $q = $_GET['query'] ?? 'avatar'; - $url = 'https://api.pexels.com/v1/search?query=' . urlencode($q) . '&per_page=12&page=1'; + $page = isset($_GET['page']) ? (int)$_GET['page'] : 1; + $url = 'https://api.pexels.com/v1/search?query=' . urlencode($q) . '&per_page=26&page=' . $page; $data = pexels_get($url); if (!$data) { echo json_encode(['error' => 'Failed to fetch images']); diff --git a/api/upload_avatar.php b/api/upload_avatar.php new file mode 100644 index 0000000..28f8886 --- /dev/null +++ b/api/upload_avatar.php @@ -0,0 +1,58 @@ + false, 'error' => 'Non autorisé']); + exit; +} + +if (!isset($_FILES['avatar']) || $_FILES['avatar']['error'] !== UPLOAD_ERR_OK) { + echo json_encode(['success' => false, 'error' => 'Aucun fichier reçu ou erreur de téléchargement']); + exit; +} + +$file = $_FILES['avatar']; +$allowedTypes = ['image/jpeg', 'image/png', 'image/webp', 'image/gif']; +$maxSize = 2 * 1024 * 1024; // 2MB + +if (!in_array($file['type'], $allowedTypes)) { + echo json_encode(['success' => false, 'error' => 'Format de fichier non supporté (JPG, PNG, WebP, GIF uniquement)']); + exit; +} + +if ($file['size'] > $maxSize) { + echo json_encode(['success' => false, 'error' => 'Le fichier est trop volumineux (max 2Mo)']); + exit; +} + +$extension = pathinfo($file['name'], PATHINFO_EXTENSION); +if (empty($extension)) { + $extensions = [ + 'image/jpeg' => 'jpg', + 'image/png' => 'png', + 'image/webp' => 'webp', + 'image/gif' => 'gif' + ]; + $extension = $extensions[$file['type']] ?? 'jpg'; +} + +$filename = 'avatar_' . $user['id'] . '_' . time() . '.' . $extension; +$targetPath = __DIR__ . '/../assets/images/avatars/' . $filename; +$relativeUrl = 'assets/images/avatars/' . $filename; + +if (move_uploaded_file($file['tmp_name'], $targetPath)) { + // Optionally delete old local avatar if it exists + if (!empty($user['avatar_url']) && strpos($user['avatar_url'], 'assets/images/avatars/') === 0) { + $oldFile = __DIR__ . '/../' . $user['avatar_url']; + if (file_exists($oldFile)) { + unlink($oldFile); + } + } + + echo json_encode(['success' => true, 'url' => $relativeUrl]); +} else { + echo json_encode(['success' => false, 'error' => 'Erreur lors de l\'enregistrement du fichier']); +} diff --git a/api/upload_server_icon.php b/api/upload_server_icon.php new file mode 100644 index 0000000..c330add --- /dev/null +++ b/api/upload_server_icon.php @@ -0,0 +1,79 @@ + false, 'error' => 'Non autorisé']); + exit; +} + +$server_id = $_POST['server_id'] ?? 0; +if (!$server_id) { + echo json_encode(['success' => false, 'error' => 'ID du serveur manquant']); + exit; +} + +if (!Permissions::hasPermission($user['id'], $server_id, Permissions::MANAGE_SERVER)) { + echo json_encode(['success' => false, 'error' => 'Vous n\'avez pas la permission de gérer ce serveur']); + exit; +} + +if (!isset($_FILES['icon']) || $_FILES['icon']['error'] !== UPLOAD_ERR_OK) { + echo json_encode(['success' => false, 'error' => 'Aucun fichier reçu ou erreur de téléchargement']); + exit; +} + +$file = $_FILES['icon']; +$allowedTypes = ['image/jpeg', 'image/png', 'image/webp', 'image/gif']; +$maxSize = 2 * 1024 * 1024; // 2MB + +if (!in_array($file['type'], $allowedTypes)) { + echo json_encode(['success' => false, 'error' => 'Format de fichier non supporté (JPG, PNG, WebP, GIF uniquement)']); + exit; +} + +if ($file['size'] > $maxSize) { + echo json_encode(['success' => false, 'error' => 'Le fichier est trop volumineux (max 2Mo)']); + exit; +} + +$extension = pathinfo($file['name'], PATHINFO_EXTENSION); +if (empty($extension)) { + $extensions = [ + 'image/jpeg' => 'jpg', + 'image/png' => 'png', + 'image/webp' => 'webp', + 'image/gif' => 'gif' + ]; + $extension = $extensions[$file['type']] ?? 'jpg'; +} + +$filename = 'server_' . $server_id . '_' . time() . '.' . $extension; +$dir = __DIR__ . '/../assets/images/servers/'; +if (!is_dir($dir)) { + mkdir($dir, 0775, true); +} + +$targetPath = $dir . $filename; +$relativeUrl = 'assets/images/servers/' . $filename; + +if (move_uploaded_file($file['tmp_name'], $targetPath)) { + // Optionally fetch old icon to delete it if it's local + $stmt = db()->prepare("SELECT icon_url FROM servers WHERE id = ?"); + $stmt->execute([$server_id]); + $server = $stmt->fetch(); + + if ($server && !empty($server['icon_url']) && strpos($server['icon_url'], 'assets/images/servers/') === 0) { + $oldFile = __DIR__ . '/../' . $server['icon_url']; + if (file_exists($oldFile)) { + unlink($oldFile); + } + } + + echo json_encode(['success' => true, 'url' => $relativeUrl]); +} else { + echo json_encode(['success' => false, 'error' => 'Erreur lors de l\'enregistrement du fichier']); +} diff --git a/assets/images/avatars/avatar_2_1771557067.png b/assets/images/avatars/avatar_2_1771557067.png new file mode 100644 index 0000000..63e17fb Binary files /dev/null and b/assets/images/avatars/avatar_2_1771557067.png differ diff --git a/assets/images/servers/server_1_1771557116.png b/assets/images/servers/server_1_1771557116.png new file mode 100644 index 0000000..63e17fb Binary files /dev/null and b/assets/images/servers/server_1_1771557116.png differ diff --git a/assets/js/main.js b/assets/js/main.js index 085dfe6..5b7ea1a 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -2544,33 +2544,125 @@ document.addEventListener('DOMContentLoaded', () => { // User Settings - Save logic removed and moved to index.php for reliability const avatarSearchBtn = document.getElementById('search-avatar-btn'); + const avatarRefreshBtn = document.getElementById('refresh-avatar-btn'); const avatarSearchQuery = document.getElementById('avatar-search-query'); const avatarResults = document.getElementById('avatar-results'); const avatarPreview = document.getElementById('settings-avatar-preview'); const avatarUrlInput = document.getElementById('settings-avatar-url'); + const avatarUploadInput = document.getElementById('avatar-upload-input'); - avatarSearchBtn?.addEventListener('click', async () => { - const q = avatarSearchQuery.value.trim(); + avatarUploadInput?.addEventListener('change', async (e) => { + const file = e.target.files[0]; + if (!file) return; + + const formData = new FormData(); + formData.append('avatar', file); + + try { + avatarPreview.innerHTML = '
'; + const resp = await fetch('api/upload_avatar.php', { + method: 'POST', + body: formData + }); + const data = await resp.json(); + avatarPreview.innerHTML = ''; + + if (data.success) { + avatarUrlInput.value = data.url; + avatarPreview.style.backgroundImage = `url('${data.url}')`; + } else { + alert(data.error || 'Erreur lors de l\'upload'); + } + } catch (err) { + console.error(err); + avatarPreview.innerHTML = ''; + alert('Erreur réseau lors de l\'upload'); + } + }); + + const serverIconUploadInput = document.getElementById('server-icon-upload-input'); + // serverIconPreview and serverIconUrlInput are already declared above + + serverIconUploadInput?.addEventListener('change', async (e) => { + const file = e.target.files[0]; + if (!file) return; + + const formData = new FormData(); + formData.append('icon', file); + formData.append('server_id', window.activeServerId); + + try { + serverIconPreview.innerHTML = ''; + const resp = await fetch('api/upload_server_icon.php', { + method: 'POST', + body: formData + }); + const data = await resp.json(); + serverIconPreview.innerHTML = ''; + + if (data.success) { + serverIconUrlInput.value = data.url; + serverIconPreview.style.backgroundImage = `url('${data.url}')`; + } else { + alert(data.error || 'Erreur lors de l\'upload'); + } + } catch (err) { + console.error(err); + serverIconPreview.innerHTML = ''; + alert('Erreur réseau lors de l\'upload'); + } + }); + + let currentAvatarPage = 1; + + async function fetchAvatars(q, page = 1) { if (!q) return; avatarResults.innerHTML = '