categories

This commit is contained in:
Flatlogic Bot 2026-02-15 23:38:38 +00:00
parent f604713529
commit b5ae307f55
6 changed files with 132 additions and 64 deletions

View File

@ -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);

View File

@ -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,

4
debug_reorder.log Normal file
View File

@ -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}]

134
index.php
View File

@ -1,4 +1,6 @@
<?php
// Request log
file_put_contents('requests.log', date('Y-m-d H:i:s') . " - " . $_SERVER['REQUEST_METHOD'] . " " . $_SERVER['REQUEST_URI'] . " - POST: " . json_encode($_POST) . "\n", FILE_APPEND);
require_once 'auth/session.php';
function renderRoleIcon($icon, $size = '12px') {
@ -330,12 +332,6 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
<?php endforeach; ?>
<?php else: ?>
<?php
// Separate categories and channels
$categories = array_filter($channels, function($c) { return $c['type'] === 'category'; });
$top_level_channels = array_filter($channels, function($c) use ($channels) {
return $c['type'] !== 'category' && (empty($c['category_id']) || !in_array($c['category_id'], array_column($channels, 'id')));
});
// Helper to render a channel item
function renderChannelItem($c, $active_channel_id, $active_server_id, $can_manage_channels) {
if ($c['type'] === 'separator') {
@ -351,7 +347,7 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
data-files="0"
data-limit="0"
data-status=""
data-icon=""
data-icon="<?php echo htmlspecialchars($c['icon'] ?? ''); ?>"
data-category="<?php echo $c['category_id'] ?? ''; ?>">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33 1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82 1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg>
</span>
@ -401,47 +397,55 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
<?php
}
// Render top level channels
if (!empty($top_level_channels)) {
foreach($top_level_channels as $c) {
renderChannelItem($c, $active_channel_id, $active_server_id, $can_manage_channels);
}
}
$category_ids = array_column(array_filter($channels, function($c) { return $c['type'] === 'category'; }), 'id');
// Render categories and their channels
foreach($categories as $cat) {
?>
<div class="category-wrapper" data-id="<?php echo $cat['id']; ?>">
<div class="channel-category d-flex align-items-center mt-3" data-id="<?php echo $cat['id']; ?>">
<span class="category-name flex-grow-1 text-uppercase fw-bold" style="font-size: 0.7em; cursor: pointer; color: var(--text-muted);"><?php echo htmlspecialchars($cat['name']); ?></span>
<?php if ($can_manage_channels): ?>
<span class="channel-settings-btn ms-1" style="cursor: pointer; color: var(--text-muted);"
data-bs-toggle="modal" data-bs-target="#editChannelModal"
data-id="<?php echo $cat['id']; ?>"
data-name="<?php echo htmlspecialchars($cat['name']); ?>"
data-type="category"
data-files="0"
data-limit="0"
data-status=""
data-icon=""
data-category=""
data-theme="">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33 1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82 1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg>
</span>
<span class="add-channel-btn ms-1" title="Create Channel" data-bs-toggle="modal" data-bs-target="#addChannelModal" data-type="chat" data-category-id="<?php echo $cat['id']; ?>">+</span>
<?php endif; ?>
</div>
<div class="category-group" data-category-id="<?php echo $cat['id']; ?>">
<?php
foreach($channels as $c) {
if ($c['type'] !== 'category' && $c['category_id'] == $cat['id']) {
renderChannelItem($c, $active_channel_id, $active_server_id, $can_manage_channels);
foreach($channels as $item) {
// Skip channels that have a parent category (they will be rendered inside their category)
if ($item['type'] !== 'category' && !empty($item['category_id']) && in_array($item['category_id'], $category_ids)) {
continue;
}
if ($item['type'] === 'category') {
// Render category and its children
?>
<div class="category-wrapper" data-id="<?php echo $item['id']; ?>">
<div class="channel-category d-flex align-items-center mt-3" data-id="<?php echo $item['id']; ?>">
<?php if (!empty($item['icon'])): ?>
<span class="me-1" style="font-size: 14px;"><?php echo renderRoleIcon($item['icon'], '14px'); ?></span>
<?php endif; ?>
<span class="category-name flex-grow-1 text-uppercase fw-bold" style="font-size: 0.85em; cursor: pointer; color: var(--text-muted);"><?php echo htmlspecialchars($item['name']); ?></span>
<?php if ($can_manage_channels): ?>
<span class="channel-settings-btn ms-1" style="cursor: pointer; color: var(--text-muted);"
data-bs-toggle="modal" data-bs-target="#editChannelModal"
data-id="<?php echo $item['id']; ?>"
data-name="<?php echo htmlspecialchars($item['name']); ?>"
data-type="category"
data-files="0"
data-limit="0"
data-status=""
data-icon="<?php echo htmlspecialchars($item['icon'] ?? ''); ?>"
data-category=""
data-theme="">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33 1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82 1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg>
</span>
<span class="add-channel-btn ms-1" title="Create Channel" data-bs-toggle="modal" data-bs-target="#addChannelModal" data-type="chat" data-category-id="<?php echo $item['id']; ?>">+</span>
<?php endif; ?>
</div>
<div class="category-group" data-category-id="<?php echo $item['id']; ?>">
<?php
foreach($channels as $c) {
if ($c['type'] !== 'category' && $c['category_id'] == $item['id']) {
renderChannelItem($c, $active_channel_id, $active_server_id, $can_manage_channels);
}
}
}
?>
?>
</div>
</div>
</div>
<?php
<?php
} else {
// Render top level channel
renderChannelItem($item, $active_channel_id, $active_server_id, $can_manage_channels);
}
}
?>
<?php endif; ?>
@ -458,7 +462,7 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
</div>
<div class="user-actions">
<a href="#" title="Settings" style="color: var(--text-muted); margin-right: 8px;" data-bs-toggle="modal" data-bs-target="#userSettingsModal">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33 1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82 1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg>
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33 1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33 1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg>
</a>
<a href="auth/logout.php" title="Logout" style="color: var(--text-muted);"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"></path><polyline points="16 17 21 12 16 7"></polyline><line x1="21" y1="12" x2="9" y2="12"></line></svg></a>
</div>
@ -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: <?php echo $active_server_id; ?>,
server_id: "<?php echo $active_server_id; ?>",
orders: orders
})
});

12
requests.log Normal file
View File

@ -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: []

17
test_reorder.php Normal file
View File

@ -0,0 +1,17 @@
<?php
require 'db/config.php';
$orders = [
['id' => 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";
}
?>