diff --git a/api_v1_channels.php b/api_v1_channels.php index 4affb2a..43d7054 100644 --- a/api_v1_channels.php +++ b/api_v1_channels.php @@ -26,6 +26,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { $orders = $json['orders'] ?? []; // Array of {id, position, category_id} $user_id = $_SESSION['user_id']; + // Debug log + file_put_contents('debug_reorder.log', date('Y-m-d H:i:s') . " - Server: $server_id - Orders: " . json_encode($orders) . "\n", FILE_APPEND); + if (Permissions::hasPermission($user_id, $server_id, Permissions::MANAGE_CHANNELS)) { $stmt = db()->prepare("UPDATE channels SET position = ?, category_id = ? WHERE id = ? AND server_id = ?"); foreach ($orders as $o) { @@ -61,7 +64,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { if ($chan && Permissions::hasPermission($user_id, $chan['server_id'], Permissions::MANAGE_CHANNELS)) { if ($type === 'separator' && !$name) $name = 'separator'; - $name = strtolower(preg_replace('/[^a-zA-Z0-9\-]/', '-', $name)); + // Allow spaces, accents and mixed case + $name = trim($name); + // Explicitly exclude position from update to prevent jumping to bottom $stmt = db()->prepare("UPDATE channels SET name = ?, type = ?, status = ?, allow_file_sharing = ?, message_limit = ?, icon = ?, category_id = ? WHERE id = ?"); $stmt->execute([$name, $type, $status, $allow_file_sharing, $message_limit, $icon, $category_id, $channel_id]); } @@ -91,16 +96,22 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { if (Permissions::hasPermission($user_id, $server_id, Permissions::MANAGE_CHANNELS) && ($name || $type === 'separator')) { try { if ($type === 'separator' && !$name) $name = 'separator'; - // Basic sanitization for channel name - $name = strtolower(preg_replace('/[^a-zA-Z0-9\-]/', '-', $name)); + // Allow spaces, accents and mixed case + $name = trim($name); $allow_file_sharing = isset($_POST['allow_file_sharing']) ? 1 : 0; $message_limit = !empty($_POST['message_limit']) ? (int)$_POST['message_limit'] : null; $icon = $_POST['icon'] ?? null; if ($icon === '') $icon = null; $category_id = !empty($_POST['category_id']) ? (int)$_POST['category_id'] : null; - $stmt = db()->prepare("INSERT INTO channels (server_id, name, type, allow_file_sharing, message_limit, icon, category_id) VALUES (?, ?, ?, ?, ?, ?, ?)"); - $stmt->execute([$server_id, $name, $type, $allow_file_sharing, $message_limit, $icon, $category_id]); + // Get next position + $stmtPos = db()->prepare("SELECT MAX(position) as max_pos FROM channels WHERE server_id = ?"); + $stmtPos->execute([$server_id]); + $maxPos = $stmtPos->fetch(); + $nextPos = ($maxPos['max_pos'] ?? -1) + 1; + + $stmt = db()->prepare("INSERT INTO channels (server_id, name, type, allow_file_sharing, message_limit, icon, category_id, position) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"); + $stmt->execute([$server_id, $name, $type, $allow_file_sharing, $message_limit, $icon, $category_id, $nextPos]); $channel_id = db()->lastInsertId(); header('Location: index.php?server_id=' . $server_id . '&channel_id=' . $channel_id); diff --git a/assets/css/discord.css b/assets/css/discord.css index 00cafb0..ab4a68b 100644 --- a/assets/css/discord.css +++ b/assets/css/discord.css @@ -134,7 +134,9 @@ body { /* Channels Sidebar */ .channels-sidebar { - width: 240px; + width: auto; + min-width: 240px; + max-width: 400px; background-color: var(--bg-channels); display: flex; flex-direction: column; @@ -166,6 +168,7 @@ body { gap: 8px; margin-bottom: 2px; text-decoration: none; + white-space: nowrap; } .channel-item:hover { @@ -189,7 +192,7 @@ body { .channel-category { color: var(--text-muted); - font-size: 0.75em; + font-size: 0.85em; text-transform: uppercase; font-weight: bold; margin-bottom: 4px; @@ -197,6 +200,7 @@ body { display: flex; justify-content: space-between; align-items: center; + white-space: nowrap; } .channel-category .channel-settings-btn, diff --git a/debug_reorder.log b/debug_reorder.log new file mode 100644 index 0000000..7038d2e --- /dev/null +++ b/debug_reorder.log @@ -0,0 +1,4 @@ +2026-02-15 23:32:05 - Server: 1 - Orders: [{"id":"1","position":0,"category_id":null},{"id":"6","position":1,"category_id":null},{"id":"10","position":2,"category_id":null},{"id":"2","position":3,"category_id":null},{"id":"9","position":4,"category_id":null},{"id":"3","position":5,"category_id":null}] +2026-02-15 23:35:48 - Server: 1 - Orders: [{"id":"10","position":0,"category_id":null},{"id":"1","position":1,"category_id":null},{"id":"6","position":2,"category_id":null},{"id":"2","position":3,"category_id":null},{"id":"9","position":4,"category_id":null},{"id":"3","position":5,"category_id":null}] +2026-02-15 23:36:25 - Server: 1 - Orders: [{"id":"10","position":0,"category_id":null},{"id":"1","position":1,"category_id":"10"},{"id":"6","position":2,"category_id":"10"},{"id":"2","position":3,"category_id":null},{"id":"9","position":4,"category_id":null},{"id":"3","position":5,"category_id":null}] +2026-02-15 23:36:28 - Server: 1 - Orders: [{"id":"10","position":0,"category_id":null},{"id":"1","position":1,"category_id":"10"},{"id":"6","position":2,"category_id":"10"},{"id":"2","position":3,"category_id":"10"},{"id":"9","position":4,"category_id":null},{"id":"3","position":5,"category_id":null}] diff --git a/index.php b/index.php index 683c6a8..b1fba61 100644 --- a/index.php +++ b/index.php @@ -1,4 +1,6 @@ " data-category=""> @@ -401,47 +397,55 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? ''; -
-
- - - - - - + - -
-
- +
+
+ + + + + + + + + + + +
+
+ + ?> +
-
- @@ -458,7 +462,7 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
- +
@@ -1543,24 +1547,34 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? ''; function updatePrefix(typeSelect, iconSelect, prefixSpan) { if (!prefixSpan || !typeSelect) return; - // Handle name input visibility for separator + // Handle name input visibility for separator and category const modal = typeSelect.closest('.modal'); const nameInputContainer = modal.querySelector('input[name="name"]')?.closest('.mb-3'); const iconSelectContainer = modal.querySelector('select[name="icon"]')?.closest('.mb-3'); const fileSharingContainer = modal.querySelector('input[name="allow_file_sharing"]')?.closest('.mb-3') || modal.querySelector('input[name="allow_file_sharing"]')?.closest('.form-check'); const limitContainer = modal.querySelector('input[name="message_limit"]')?.closest('.mb-3'); + const categoryContainer = modal.querySelector('select[name="category_id"]')?.closest('.mb-3'); if (typeSelect.value === 'separator') { if (nameInputContainer) nameInputContainer.style.display = 'none'; if (iconSelectContainer) iconSelectContainer.style.display = 'none'; if (fileSharingContainer) fileSharingContainer.style.display = 'none'; if (limitContainer) limitContainer.style.display = 'none'; + if (categoryContainer) categoryContainer.style.display = 'none'; if (modal.querySelector('input[name="name"]')) modal.querySelector('input[name="name"]').required = false; + } else if (typeSelect.value === 'category') { + if (nameInputContainer) nameInputContainer.style.display = 'block'; + if (iconSelectContainer) iconSelectContainer.style.display = 'block'; + if (fileSharingContainer) fileSharingContainer.style.display = 'none'; + if (limitContainer) limitContainer.style.display = 'none'; + if (categoryContainer) categoryContainer.style.display = 'none'; + if (modal.querySelector('input[name="name"]')) modal.querySelector('input[name="name"]').required = true; } else { if (nameInputContainer) nameInputContainer.style.display = 'block'; if (iconSelectContainer) iconSelectContainer.style.display = 'block'; if (fileSharingContainer) fileSharingContainer.style.display = 'block'; if (limitContainer) limitContainer.style.display = 'block'; + if (categoryContainer) categoryContainer.style.display = 'block'; if (modal.querySelector('input[name="name"]')) modal.querySelector('input[name="name"]').required = true; } @@ -1649,6 +1663,7 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? ''; groups.forEach(group => { new Sortable(group, { group: 'channels', + draggable: '.channel-item-container', animation: 150, ghostClass: 'sortable-ghost', onEnd: function() { @@ -1660,6 +1675,7 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? ''; // Sortable for categories themselves and top-level channels const sidebar = document.getElementById('sidebar-channels-list'); new Sortable(sidebar, { + group: 'channels', animation: 150, draggable: '.category-wrapper, .channel-item-container:not(.category-group .channel-item-container)', ghostClass: 'sortable-ghost', @@ -1679,11 +1695,13 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? ''; const topLevelItems = sidebar.children; Array.from(topLevelItems).forEach(item => { + const itemId = item.dataset.id; + if (!itemId) return; + if (item.classList.contains('category-wrapper')) { // It's a category - const catId = item.dataset.id; orders.push({ - id: catId, + id: itemId, position: position++, category_id: null }); @@ -1691,16 +1709,18 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? ''; // Now add all channels inside this category const subChannels = item.querySelectorAll('.category-group .channel-item-container'); subChannels.forEach(sub => { - orders.push({ - id: sub.dataset.id, - position: position++, - category_id: catId - }); + if (sub.dataset.id) { + orders.push({ + id: sub.dataset.id, + position: position++, + category_id: itemId + }); + } }); } else if (item.classList.contains('channel-item-container')) { - // It's a top level channel + // It's a top level channel or separator orders.push({ - id: item.dataset.id, + id: itemId, position: position++, category_id: null }); @@ -1713,7 +1733,7 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? ''; headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'reorder', - server_id: , + server_id: "", orders: orders }) }); diff --git a/requests.log b/requests.log new file mode 100644 index 0000000..044ac28 --- /dev/null +++ b/requests.log @@ -0,0 +1,12 @@ +2026-02-15 23:22:04 - GET /?fl_project=38443 - POST: [] +2026-02-15 23:27:59 - GET / - POST: [] +2026-02-15 23:28:03 - HEAD / - POST: [] +2026-02-15 23:28:16 - GET /?fl_project=38443 - POST: [] +2026-02-15 23:31:59 - GET /index.php?server_id=1&channel_id=10 - POST: [] +2026-02-15 23:32:07 - GET /index.php?server_id=1&channel_id=10 - POST: [] +2026-02-15 23:32:12 - GET /index.php?server_id=1&channel_id=10 - POST: [] +2026-02-15 23:36:00 - GET /index.php?server_id=1&channel_id=10 - POST: [] +2026-02-15 23:36:17 - GET /index.php?server_id=1&channel_id=1 - POST: [] +2026-02-15 23:36:35 - GET /index.php?server_id=1&channel_id=2 - POST: [] +2026-02-15 23:38:20 - GET /index.php?server_id=1&channel_id=6 - POST: [] +2026-02-15 23:38:22 - GET /index.php?server_id=1&channel_id=1 - POST: [] diff --git a/test_reorder.php b/test_reorder.php new file mode 100644 index 0000000..828c8d6 --- /dev/null +++ b/test_reorder.php @@ -0,0 +1,17 @@ + 1, 'position' => 1, 'category_id' => null], + ['id' => 10, 'position' => 0, 'category_id' => null], + ['id' => 6, 'position' => 2, 'category_id' => null], + ['id' => 2, 'position' => 3, 'category_id' => null], + ['id' => 9, 'position' => 4, 'category_id' => null], + ['id' => 3, 'position' => 5, 'category_id' => null] +]; +$server_id = 1; +$stmt = db()->prepare("UPDATE channels SET position = ?, category_id = ? WHERE id = ? AND server_id = ?"); +foreach ($orders as $o) { + $stmt->execute([$o['position'], $o['category_id'], $o['id'], $server_id]); + echo "Updated ID {$o['id']} to position {$o['position']}\n"; +} +?>