v12
This commit is contained in:
parent
40f605d106
commit
5d6fd46690
@ -17,6 +17,28 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
// Handle JSON input
|
||||||
|
$json = json_decode(file_get_contents('php://input'), true);
|
||||||
|
if ($json) {
|
||||||
|
$action = $json['action'] ?? '';
|
||||||
|
if ($action === 'reorder') {
|
||||||
|
$server_id = $json['server_id'] ?? 0;
|
||||||
|
$orders = $json['orders'] ?? []; // Array of {id, position, category_id}
|
||||||
|
$user_id = $_SESSION['user_id'];
|
||||||
|
|
||||||
|
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) {
|
||||||
|
$stmt->execute([$o['position'], $o['category_id'] ?: null, $o['id'], $server_id]);
|
||||||
|
}
|
||||||
|
echo json_encode(['success' => true]);
|
||||||
|
} else {
|
||||||
|
echo json_encode(['success' => false, 'error' => 'Permission denied']);
|
||||||
|
}
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$action = $_POST['action'] ?? 'create';
|
$action = $_POST['action'] ?? 'create';
|
||||||
$server_id = $_POST['server_id'] ?? 0;
|
$server_id = $_POST['server_id'] ?? 0;
|
||||||
$user_id = $_SESSION['user_id'];
|
$user_id = $_SESSION['user_id'];
|
||||||
@ -30,6 +52,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
$message_limit = !empty($_POST['message_limit']) ? (int)$_POST['message_limit'] : null;
|
$message_limit = !empty($_POST['message_limit']) ? (int)$_POST['message_limit'] : null;
|
||||||
$theme_color = $_POST['theme_color'] ?? null;
|
$theme_color = $_POST['theme_color'] ?? null;
|
||||||
if ($theme_color === '') $theme_color = null;
|
if ($theme_color === '') $theme_color = null;
|
||||||
|
$icon = $_POST['icon'] ?? null;
|
||||||
|
if ($icon === '') $icon = null;
|
||||||
|
$category_id = !empty($_POST['category_id']) ? (int)$_POST['category_id'] : null;
|
||||||
|
|
||||||
// Check if user has permission to manage channels
|
// Check if user has permission to manage channels
|
||||||
$stmt = db()->prepare("SELECT server_id FROM channels WHERE id = ?");
|
$stmt = db()->prepare("SELECT server_id FROM channels WHERE id = ?");
|
||||||
@ -38,8 +63,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
|
|
||||||
if ($chan && Permissions::hasPermission($user_id, $chan['server_id'], Permissions::MANAGE_CHANNELS)) {
|
if ($chan && Permissions::hasPermission($user_id, $chan['server_id'], Permissions::MANAGE_CHANNELS)) {
|
||||||
$name = strtolower(preg_replace('/[^a-zA-Z0-9\-]/', '-', $name));
|
$name = strtolower(preg_replace('/[^a-zA-Z0-9\-]/', '-', $name));
|
||||||
$stmt = db()->prepare("UPDATE channels SET name = ?, type = ?, status = ?, allow_file_sharing = ?, theme_color = ?, message_limit = ? WHERE id = ?");
|
$stmt = db()->prepare("UPDATE channels SET name = ?, type = ?, status = ?, allow_file_sharing = ?, theme_color = ?, message_limit = ?, icon = ?, category_id = ? WHERE id = ?");
|
||||||
$stmt->execute([$name, $type, $status, $allow_file_sharing, $theme_color, $message_limit, $channel_id]);
|
$stmt->execute([$name, $type, $status, $allow_file_sharing, $theme_color, $message_limit, $icon, $category_id, $channel_id]);
|
||||||
}
|
}
|
||||||
header('Location: index.php?server_id=' . $server_id . '&channel_id=' . $channel_id);
|
header('Location: index.php?server_id=' . $server_id . '&channel_id=' . $channel_id);
|
||||||
exit;
|
exit;
|
||||||
@ -72,9 +97,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
$message_limit = !empty($_POST['message_limit']) ? (int)$_POST['message_limit'] : null;
|
$message_limit = !empty($_POST['message_limit']) ? (int)$_POST['message_limit'] : null;
|
||||||
$theme_color = $_POST['theme_color'] ?? null;
|
$theme_color = $_POST['theme_color'] ?? null;
|
||||||
if ($theme_color === '') $theme_color = null;
|
if ($theme_color === '') $theme_color = 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, theme_color, message_limit) VALUES (?, ?, ?, ?, ?, ?)");
|
$stmt = db()->prepare("INSERT INTO channels (server_id, name, type, allow_file_sharing, theme_color, message_limit, icon, category_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
|
||||||
$stmt->execute([$server_id, $name, $type, $allow_file_sharing, $theme_color, $message_limit]);
|
$stmt->execute([$server_id, $name, $type, $allow_file_sharing, $theme_color, $message_limit, $icon, $category_id]);
|
||||||
$channel_id = db()->lastInsertId();
|
$channel_id = db()->lastInsertId();
|
||||||
|
|
||||||
header('Location: index.php?server_id=' . $server_id . '&channel_id=' . $channel_id);
|
header('Location: index.php?server_id=' . $server_id . '&channel_id=' . $channel_id);
|
||||||
|
|||||||
@ -178,17 +178,6 @@ body {
|
|||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.channel-item::before {
|
|
||||||
content: "#";
|
|
||||||
font-size: 1.2em;
|
|
||||||
font-weight: 300;
|
|
||||||
}
|
|
||||||
|
|
||||||
.voice-item::before {
|
|
||||||
content: "🔊";
|
|
||||||
font-size: 0.9em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.server-icon.add-btn {
|
.server-icon.add-btn {
|
||||||
color: #23a559;
|
color: #23a559;
|
||||||
}
|
}
|
||||||
@ -203,13 +192,48 @@ body {
|
|||||||
font-size: 0.75em;
|
font-size: 0.75em;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 4px;
|
||||||
padding-left: 8px;
|
padding: 16px 8px 4px 8px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.channel-category .channel-settings-btn,
|
||||||
|
.channel-category .add-channel-btn {
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
font-size: 1.2em;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.channel-category:hover .channel-settings-btn,
|
||||||
|
.channel-category:hover .add-channel-btn {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.channel-item-container .channel-settings-btn {
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.channel-item-container:hover .channel-settings-btn {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-group .channel-item-container {
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-group {
|
||||||
|
min-height: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sortable-ghost {
|
||||||
|
background-color: var(--hover) !important;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
.add-channel-btn {
|
.add-channel-btn {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
@ -400,7 +424,27 @@ body {
|
|||||||
width: 240px;
|
width: 240px;
|
||||||
background-color: var(--bg-members);
|
background-color: var(--bg-members);
|
||||||
padding: 24px 8px;
|
padding: 24px 8px;
|
||||||
display: none; /* Hidden on mobile/small screens */
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.members-sidebar.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 992px) {
|
||||||
|
.members-sidebar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.members-sidebar.show {
|
||||||
|
display: flex;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 48px;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 100;
|
||||||
|
box-shadow: -2px 0 10px rgba(0,0,0,0.5);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Reactions */
|
/* Reactions */
|
||||||
|
|||||||
@ -53,18 +53,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
new Notification(`Mention in #${window.currentChannelName}`, {
|
new Notification(`Mention in #${window.currentChannelName}`, {
|
||||||
body: `${data.username}: ${data.content}`,
|
body: `${data.username}: ${data.content}`,
|
||||||
icon: data.avatar_url || ''
|
icon: data.avatar_url || ''
|
||||||
if (e.target.classList.contains('move-rule-btn')) {
|
});
|
||||||
const id = e.target.dataset.id;
|
|
||||||
const dir = e.target.dataset.dir;
|
|
||||||
const resp = await fetch('api_v1_rules.php', {
|
|
||||||
method: 'PATCH',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({ id, dir })
|
|
||||||
});
|
|
||||||
if ((await resp.json()).success) location.reload();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -594,10 +583,21 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
document.addEventListener('click', (e) => {
|
document.addEventListener('click', async (e) => {
|
||||||
if (!e.target.closest('.search-container')) {
|
if (!e.target.closest('.search-container')) {
|
||||||
searchResults.style.display = 'none';
|
searchResults.style.display = 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (e.target.classList.contains('move-rule-btn')) {
|
||||||
|
const id = e.target.dataset.id;
|
||||||
|
const dir = e.target.dataset.dir;
|
||||||
|
const resp = await fetch('api_v1_rules.php', {
|
||||||
|
method: 'PATCH',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ id, dir })
|
||||||
|
});
|
||||||
|
if ((await resp.json()).success) location.reload();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Roles Management
|
// Roles Management
|
||||||
@ -615,6 +615,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
modal.querySelector('#edit-channel-limit').value = btn.dataset.limit || '';
|
modal.querySelector('#edit-channel-limit').value = btn.dataset.limit || '';
|
||||||
modal.querySelector('#edit-channel-status').value = btn.dataset.status || '';
|
modal.querySelector('#edit-channel-status').value = btn.dataset.status || '';
|
||||||
modal.querySelector('#edit-channel-theme').value = btn.dataset.theme || '#5865f2';
|
modal.querySelector('#edit-channel-theme').value = btn.dataset.theme || '#5865f2';
|
||||||
|
modal.querySelector('#edit-channel-icon').value = btn.dataset.icon || '';
|
||||||
modal.querySelector('#delete-channel-id').value = channelId;
|
modal.querySelector('#delete-channel-id').value = channelId;
|
||||||
|
|
||||||
// Show/Hide RSS tab
|
// Show/Hide RSS tab
|
||||||
@ -1348,6 +1349,19 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
} catch (e) { console.error(e); }
|
} catch (e) { console.error(e); }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Toggle members sidebar
|
||||||
|
const toggleMembersBtn = document.getElementById('toggle-members-btn');
|
||||||
|
const membersSidebar = document.querySelector('.members-sidebar');
|
||||||
|
if (toggleMembersBtn && membersSidebar) {
|
||||||
|
toggleMembersBtn.addEventListener('click', () => {
|
||||||
|
if (window.innerWidth > 992) {
|
||||||
|
membersSidebar.classList.toggle('hidden');
|
||||||
|
} else {
|
||||||
|
membersSidebar.classList.toggle('show');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// User Settings - Save
|
// User Settings - Save
|
||||||
const saveSettingsBtn = document.getElementById('save-settings-btn');
|
const saveSettingsBtn = document.getElementById('save-settings-btn');
|
||||||
saveSettingsBtn?.addEventListener('click', async () => {
|
saveSettingsBtn?.addEventListener('click', async () => {
|
||||||
|
|||||||
BIN
assets/pasted-20260215-151928-c94822be.png
Normal file
BIN
assets/pasted-20260215-151928-c94822be.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 189 KiB |
BIN
assets/pasted-20260215-153522-763a8478.png
Normal file
BIN
assets/pasted-20260215-153522-763a8478.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 240 KiB |
@ -9,6 +9,11 @@ class Permissions {
|
|||||||
const ADMINISTRATOR = 32;
|
const ADMINISTRATOR = 32;
|
||||||
|
|
||||||
public static function hasPermission($user_id, $server_id, $permission) {
|
public static function hasPermission($user_id, $server_id, $permission) {
|
||||||
|
$stmt = db()->prepare("SELECT is_admin FROM users WHERE id = ?");
|
||||||
|
$stmt->execute([$user_id]);
|
||||||
|
$user = $stmt->fetch();
|
||||||
|
if ($user && $user['is_admin']) return true;
|
||||||
|
|
||||||
$stmt = db()->prepare("SELECT owner_id FROM servers WHERE id = ?");
|
$stmt = db()->prepare("SELECT owner_id FROM servers WHERE id = ?");
|
||||||
$stmt->execute([$server_id]);
|
$stmt->execute([$server_id]);
|
||||||
$server = $stmt->fetch();
|
$server = $stmt->fetch();
|
||||||
|
|||||||
395
index.php
395
index.php
@ -63,7 +63,7 @@ if ($is_dm_view) {
|
|||||||
$active_server_id = $_GET['server_id'] ?? ($servers[0]['id'] ?? 1);
|
$active_server_id = $_GET['server_id'] ?? ($servers[0]['id'] ?? 1);
|
||||||
|
|
||||||
// Fetch channels
|
// Fetch channels
|
||||||
$stmt = db()->prepare("SELECT * FROM channels WHERE server_id = ?");
|
$stmt = db()->prepare("SELECT * FROM channels WHERE server_id = ? ORDER BY position ASC, id ASC");
|
||||||
$stmt->execute([$active_server_id]);
|
$stmt->execute([$active_server_id]);
|
||||||
$channels = $stmt->fetchAll();
|
$channels = $stmt->fetchAll();
|
||||||
$active_channel_id = $_GET['channel_id'] ?? ($channels[0]['id'] ?? 1);
|
$active_channel_id = $_GET['channel_id'] ?? ($channels[0]['id'] ?? 1);
|
||||||
@ -192,8 +192,10 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
|||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||||
<link rel="stylesheet" href="assets/css/discord.css?v=<?php echo time(); ?>">
|
<link rel="stylesheet" href="assets/css/discord.css?v=<?php echo time(); ?>">
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.0/Sortable.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
window.currentUserId = <?php echo $current_user_id; ?>;
|
window.currentUserId = <?php echo $current_user_id; ?>;
|
||||||
window.activeServerId = "<?php echo $active_server_id; ?>";
|
window.activeServerId = "<?php echo $active_server_id; ?>";
|
||||||
@ -266,7 +268,7 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
|||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
</div>
|
</div>
|
||||||
<div class="channels-list">
|
<div class="channels-list" id="sidebar-channels-list">
|
||||||
<?php if ($is_dm_view): ?>
|
<?php if ($is_dm_view): ?>
|
||||||
<?php foreach($dm_channels as $dm): ?>
|
<?php foreach($dm_channels as $dm): ?>
|
||||||
<a href="?server_id=dms&channel_id=<?php echo $dm['id']; ?>"
|
<a href="?server_id=dms&channel_id=<?php echo $dm['id']; ?>"
|
||||||
@ -278,25 +280,37 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
|||||||
</a>
|
</a>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<div class="channel-category">
|
<?php
|
||||||
<span>Text Channels</span>
|
// Separate categories and channels
|
||||||
<?php if ($can_manage_channels): ?>
|
$categories = array_filter($channels, function($c) { return $c['type'] === 'category'; });
|
||||||
<span class="add-channel-btn" title="Create Channel" data-bs-toggle="modal" data-bs-target="#addChannelModal" data-type="chat">+</span>
|
$top_level_channels = array_filter($channels, function($c) {
|
||||||
<?php endif; ?>
|
return $c['type'] !== 'category' && (empty($c['category_id']) || !in_array($c['category_id'], array_column($GLOBALS['channels'] ?? [], 'id')));
|
||||||
</div>
|
});
|
||||||
<?php foreach($channels as $c): if($c['type'] === 'voice') continue; ?>
|
|
||||||
<div class="channel-item-container d-flex align-items-center">
|
// Helper to render a channel item
|
||||||
|
function renderChannelItem($c, $active_channel_id, $active_server_id, $can_manage_channels) {
|
||||||
|
?>
|
||||||
|
<div class="channel-item-container d-flex align-items-center" data-id="<?php echo $c['id']; ?>">
|
||||||
<a href="?server_id=<?php echo $active_server_id; ?>&channel_id=<?php echo $c['id']; ?>"
|
<a href="?server_id=<?php echo $active_server_id; ?>&channel_id=<?php echo $c['id']; ?>"
|
||||||
class="channel-item flex-grow-1 <?php echo $c['id'] == $active_channel_id ? 'active' : ''; ?>">
|
class="channel-item flex-grow-1 <?php echo ($c['id'] == $active_channel_id) ? 'active' : ''; ?> <?php echo ($c['type'] === 'voice') ? 'voice-item' : ''; ?>" <?php echo ($c['type'] === 'voice') ? 'data-channel-id="'.$c['id'].'"' : ''; ?>>
|
||||||
<span class="me-1">
|
<span class="d-flex align-items-center">
|
||||||
<?php
|
<span class="me-1" style="width: 20px; display: inline-block; text-align: center;">
|
||||||
if ($c['type'] === 'announcement') echo '📢';
|
<?php
|
||||||
elseif ($c['type'] === 'rules') echo '📜';
|
if ($c['type'] === 'announcement') echo '<i class="fa-solid fa-bullhorn"></i>';
|
||||||
elseif ($c['type'] === 'forum') echo '🏛️';
|
elseif ($c['type'] === 'rules') echo '<i class="fa-solid fa-gavel"></i>';
|
||||||
else echo '#';
|
elseif ($c['type'] === 'forum') echo '<i class="fa-solid fa-comments"></i>';
|
||||||
?>
|
elseif ($c['type'] === 'voice') echo '<i class="fa-solid fa-volume-up"></i>';
|
||||||
|
else echo '<i class="fa-solid fa-hashtag"></i>';
|
||||||
|
?>
|
||||||
|
</span>
|
||||||
|
<?php if (!empty($c['icon'])): ?>
|
||||||
|
<span class="me-1 custom-channel-icon"><i class="fa-solid <?php echo htmlspecialchars($c['icon']); ?>"></i></span>
|
||||||
|
<?php endif; ?>
|
||||||
|
<span class="channel-name-text"><?php echo htmlspecialchars($c['name']); ?></span>
|
||||||
</span>
|
</span>
|
||||||
<?php echo htmlspecialchars($c['name']); ?>
|
<?php if ($c['type'] === 'voice' && !empty($c['status'])): ?>
|
||||||
|
<div class="channel-status small text-muted ms-4" style="font-size: 0.75em; margin-top: -2px;"><?php echo htmlspecialchars($c['status']); ?></div>
|
||||||
|
<?php endif; ?>
|
||||||
</a>
|
</a>
|
||||||
<?php if ($can_manage_channels): ?>
|
<?php if ($can_manage_channels): ?>
|
||||||
<span class="channel-settings-btn ms-1" style="cursor: pointer; color: var(--text-muted);"
|
<span class="channel-settings-btn ms-1" style="cursor: pointer; color: var(--text-muted);"
|
||||||
@ -307,42 +321,59 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
|||||||
data-files="<?php echo $c['allow_file_sharing']; ?>"
|
data-files="<?php echo $c['allow_file_sharing']; ?>"
|
||||||
data-limit="<?php echo $c['message_limit']; ?>"
|
data-limit="<?php echo $c['message_limit']; ?>"
|
||||||
data-status="<?php echo htmlspecialchars($c['status'] ?? ''); ?>"
|
data-status="<?php echo htmlspecialchars($c['status'] ?? ''); ?>"
|
||||||
|
data-icon="<?php echo htmlspecialchars($c['icon'] ?? ''); ?>"
|
||||||
|
data-category="<?php echo $c['category_id'] ?? ''; ?>"
|
||||||
data-theme="<?php echo $c['theme_color']; ?>">
|
data-theme="<?php echo $c['theme_color']; ?>">
|
||||||
<svg width="14" height="14" 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>
|
<svg width="14" height="14" 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>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
<?php endforeach; ?>
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
<div class="channel-category" style="margin-top: 16px;">
|
// Render top level channels
|
||||||
<span>Voice Channels</span>
|
if (!empty($top_level_channels)) {
|
||||||
<?php if ($can_manage_channels): ?>
|
foreach($top_level_channels as $c) {
|
||||||
<span class="add-channel-btn" title="Create Channel" data-bs-toggle="modal" data-bs-target="#addChannelModal" data-type="voice">+</span>
|
renderChannelItem($c, $active_channel_id, $active_server_id, $can_manage_channels);
|
||||||
<?php endif; ?>
|
}
|
||||||
</div>
|
}
|
||||||
<?php foreach($channels as $c): if($c['type'] !== 'voice') continue; ?>
|
|
||||||
<div class="channel-item-container d-flex align-items-center">
|
// Render categories and their channels
|
||||||
<div class="channel-item voice-item flex-grow-1" data-channel-id="<?php echo $c['id']; ?>">
|
foreach($categories as $cat) {
|
||||||
<span>🔊 <?php echo htmlspecialchars($c['name']); ?></span>
|
?>
|
||||||
<?php if (!empty($c['status'])): ?>
|
<div class="category-wrapper" data-id="<?php echo $cat['id']; ?>">
|
||||||
<div class="channel-status small text-muted ms-4" style="font-size: 0.75em; margin-top: -2px;"><?php echo htmlspecialchars($c['status']); ?></div>
|
<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; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
<?php if ($can_manage_channels): ?>
|
<div class="category-group" data-category-id="<?php echo $cat['id']; ?>">
|
||||||
<span class="channel-settings-btn ms-1" style="cursor: pointer; color: var(--text-muted);"
|
<?php
|
||||||
data-bs-toggle="modal" data-bs-target="#editChannelModal"
|
foreach($channels as $c) {
|
||||||
data-id="<?php echo $c['id']; ?>"
|
if ($c['type'] !== 'category' && $c['category_id'] == $cat['id']) {
|
||||||
data-name="<?php echo htmlspecialchars($c['name']); ?>"
|
renderChannelItem($c, $active_channel_id, $active_server_id, $can_manage_channels);
|
||||||
data-type="<?php echo $c['type']; ?>"
|
}
|
||||||
data-files="<?php echo $c['allow_file_sharing']; ?>"
|
}
|
||||||
data-limit="<?php echo $c['message_limit']; ?>"
|
?>
|
||||||
data-status="<?php echo htmlspecialchars($c['status'] ?? ''); ?>"
|
</div>
|
||||||
data-theme="<?php echo $c['theme_color']; ?>">
|
|
||||||
<svg width="14" height="14" 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>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
</div>
|
||||||
<?php endforeach; ?>
|
<?php
|
||||||
|
}
|
||||||
|
?>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
<div class="user-panel">
|
<div class="user-panel">
|
||||||
@ -370,10 +401,28 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
|||||||
<!-- Chat Area -->
|
<!-- Chat Area -->
|
||||||
<div class="chat-container">
|
<div class="chat-container">
|
||||||
<div class="chat-header">
|
<div class="chat-header">
|
||||||
<span style="color: var(--text-muted); margin-right: 8px;"><?php echo $is_dm_view ? '@' : '#'; ?></span>
|
<span style="color: var(--text-muted); margin-right: 8px; width: 20px; display: inline-block; text-align: center;">
|
||||||
|
<?php
|
||||||
|
if ($is_dm_view) {
|
||||||
|
echo '@';
|
||||||
|
} else {
|
||||||
|
if ($active_channel['type'] === 'announcement') echo '<i class="fa-solid fa-bullhorn"></i>';
|
||||||
|
elseif ($active_channel['type'] === 'rules') echo '<i class="fa-solid fa-gavel"></i>';
|
||||||
|
elseif ($active_channel['type'] === 'forum') echo '<i class="fa-solid fa-comments"></i>';
|
||||||
|
elseif ($active_channel['type'] === 'voice') echo '<i class="fa-solid fa-volume-up"></i>';
|
||||||
|
else echo '<i class="fa-solid fa-hashtag"></i>';
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</span>
|
||||||
|
<?php if (!$is_dm_view && !empty($active_channel['icon'])): ?>
|
||||||
|
<span class="me-2 custom-channel-icon"><i class="fa-solid <?php echo htmlspecialchars($active_channel['icon']); ?>"></i></span>
|
||||||
|
<?php endif; ?>
|
||||||
<span class="flex-grow-1"><?php echo htmlspecialchars($current_channel_name); ?></span>
|
<span class="flex-grow-1"><?php echo htmlspecialchars($current_channel_name); ?></span>
|
||||||
|
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
|
<button id="toggle-members-btn" class="btn btn-link text-muted p-1 me-2" title="Toggle Members List">
|
||||||
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path><circle cx="9" cy="7" r="4"></circle><path d="M23 21v-2a4 4 0 0 0-3-3.87"></path><path d="M16 3.13a4 4 0 0 1 0 7.75"></path></svg>
|
||||||
|
</button>
|
||||||
<button id="pinned-messages-btn" class="btn btn-link text-muted p-1 me-2" title="Pinned Messages">
|
<button id="pinned-messages-btn" class="btn btn-link text-muted p-1 me-2" title="Pinned Messages">
|
||||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"></path><circle cx="12" cy="10" r="3"></circle></svg>
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"></path><circle cx="12" cy="10" r="3"></circle></svg>
|
||||||
</button>
|
</button>
|
||||||
@ -674,6 +723,7 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
|||||||
<input type="text" id="chat-input" class="chat-input" placeholder="Message #<?php echo htmlspecialchars($current_channel_name); ?>" autocomplete="off">
|
<input type="text" id="chat-input" class="chat-input" placeholder="Message #<?php echo htmlspecialchars($current_channel_name); ?>" autocomplete="off">
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -753,7 +803,6 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
@ -947,25 +996,48 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
|||||||
</div>
|
</div>
|
||||||
<form action="api_v1_channels.php" method="POST">
|
<form action="api_v1_channels.php" method="POST">
|
||||||
<input type="hidden" name="server_id" value="<?php echo $active_server_id; ?>">
|
<input type="hidden" name="server_id" value="<?php echo $active_server_id; ?>">
|
||||||
<input type="hidden" name="type" id="channel-type-input" value="chat">
|
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label text-uppercase fw-bold" style="font-size: 0.7em; color: var(--text-muted);">Channel Type</label>
|
<label class="form-label text-uppercase fw-bold" style="font-size: 0.7em; color: var(--text-muted);">Channel Type</label>
|
||||||
<select name="type" class="form-select bg-dark text-white border-secondary mb-3" id="channel-type-select">
|
<select name="type" class="form-select bg-dark text-white border-secondary mb-3" id="add-channel-type">
|
||||||
<option value="chat">Traditional Chat</option>
|
<option value="chat">Traditional Chat</option>
|
||||||
<option value="announcement">Announcements</option>
|
<option value="announcement">Announcements</option>
|
||||||
<option value="rules">Rules</option>
|
<option value="rules">Rules</option>
|
||||||
<option value="forum">Forum</option>
|
<option value="forum">Forum</option>
|
||||||
<option value="voice">Voice Channel</option>
|
<option value="voice">Voice Channel</option>
|
||||||
|
<option value="category">Category (Separator)</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label text-uppercase fw-bold" style="font-size: 0.7em; color: var(--text-muted);">Channel Name</label>
|
<label class="form-label text-uppercase fw-bold" style="font-size: 0.7em; color: var(--text-muted);">Channel Name</label>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<span class="input-group-text bg-dark border-0 text-muted" id="channel-hash-prefix">#</span>
|
<span class="input-group-text bg-dark border-0 text-muted" id="add-channel-prefix">#</span>
|
||||||
<input type="text" name="name" class="form-control" placeholder="new-channel" required>
|
<input type="text" name="name" class="form-control" placeholder="new-channel" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label text-uppercase fw-bold" style="font-size: 0.7em; color: var(--text-muted);">Channel Icon</label>
|
||||||
|
<select name="icon" class="form-select bg-dark text-white border-secondary mb-3">
|
||||||
|
<option value="">Aucune icône personnalisée</option>
|
||||||
|
<option value="fa-hashtag"># Hashtag</option>
|
||||||
|
<option value="fa-volume-up">🔊 Voice</option>
|
||||||
|
<option value="fa-bullhorn">📢 Announcements</option>
|
||||||
|
<option value="fa-gavel">🔨 Rules</option>
|
||||||
|
<option value="fa-comments">💬 Forum</option>
|
||||||
|
<option value="fa-lock">🔒 Private</option>
|
||||||
|
<option value="fa-star">⭐ Star</option>
|
||||||
|
<option value="fa-heart">❤️ Heart</option>
|
||||||
|
<option value="fa-gamepad">🎮 Gaming</option>
|
||||||
|
<option value="fa-music">🎵 Music</option>
|
||||||
|
<option value="fa-video">📹 Video</option>
|
||||||
|
<option value="fa-info-circle">ℹ️ Info</option>
|
||||||
|
<option value="fa-question-circle">❓ Help</option>
|
||||||
|
<option value="fa-book">📖 Library</option>
|
||||||
|
<option value="fa-gift">🎁 Giveaways</option>
|
||||||
|
<option value="fa-code">💻 Coding</option>
|
||||||
|
<option value="fa-terminal">⌨️ Bot</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
<div class="form-check form-switch mb-3">
|
<div class="form-check form-switch mb-3">
|
||||||
<input class="form-check-input" type="checkbox" name="allow_file_sharing" id="add-channel-files" value="1" checked>
|
<input class="form-check-input" type="checkbox" name="allow_file_sharing" id="add-channel-files" value="1" checked>
|
||||||
<label class="form-check-label text-white" for="add-channel-files">Allow File Sharing</label>
|
<label class="form-check-label text-white" for="add-channel-files">Allow File Sharing</label>
|
||||||
@ -975,10 +1047,6 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
|||||||
<input type="number" name="message_limit" class="form-control" placeholder="e.g. 50 (Leave empty for no limit)">
|
<input type="number" name="message_limit" class="form-control" placeholder="e.g. 50 (Leave empty for no limit)">
|
||||||
<div class="form-text text-muted" style="font-size: 0.8em;">Automatically keeps only the last X messages in this channel.</div>
|
<div class="form-text text-muted" style="font-size: 0.8em;">Automatically keeps only the last X messages in this channel.</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
|
||||||
<label class="form-label text-uppercase fw-bold" style="font-size: 0.7em; color: var(--text-muted);">Theme Color</label>
|
|
||||||
<input type="color" name="theme_color" class="form-control form-control-color w-100" value="#5865f2" title="Choose channel theme color">
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-link text-white text-decoration-none" data-bs-dismiss="modal">Cancel</button>
|
<button type="button" class="btn btn-link text-white text-decoration-none" data-bs-dismiss="modal">Cancel</button>
|
||||||
@ -1024,12 +1092,54 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
|||||||
<option value="rules">Rules</option>
|
<option value="rules">Rules</option>
|
||||||
<option value="forum">Forum</option>
|
<option value="forum">Forum</option>
|
||||||
<option value="voice">Voice Channel</option>
|
<option value="voice">Voice Channel</option>
|
||||||
|
<option value="category">Category (Separator)</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label text-uppercase fw-bold" style="font-size: 0.7em; color: var(--text-muted);">Parent Category</label>
|
||||||
|
<select name="category_id" id="edit-channel-category-id" class="form-select bg-dark text-white border-secondary">
|
||||||
|
<option value="">No Category (Top level)</option>
|
||||||
|
<?php
|
||||||
|
foreach($channels as $cat) {
|
||||||
|
if ($cat['type'] === 'category') {
|
||||||
|
echo '<option value="'.$cat['id'].'">'.htmlspecialchars($cat['name']).'</option>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label text-uppercase fw-bold" style="font-size: 0.7em; color: var(--text-muted);">Channel Name</label>
|
<label class="form-label text-uppercase fw-bold" style="font-size: 0.7em; color: var(--text-muted);">Channel Name</label>
|
||||||
<input type="text" name="name" id="edit-channel-name" class="form-control" required>
|
<div class="input-group">
|
||||||
|
<span class="input-group-text bg-dark border-0 text-muted" id="edit-channel-prefix">#</span>
|
||||||
|
<input type="text" name="name" id="edit-channel-name" class="form-control" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label text-uppercase fw-bold" style="font-size: 0.7em; color: var(--text-muted);">Channel Icon</label>
|
||||||
|
<select name="icon" id="edit-channel-icon" class="form-select bg-dark text-white border-secondary mb-3">
|
||||||
|
<option value="">Aucune icône personnalisée</option>
|
||||||
|
<option value="fa-hashtag"># Hashtag</option>
|
||||||
|
<option value="fa-volume-up">🔊 Voice</option>
|
||||||
|
<option value="fa-bullhorn">📢 Announcements</option>
|
||||||
|
<option value="fa-gavel">🔨 Rules</option>
|
||||||
|
<option value="fa-comments">💬 Forum</option>
|
||||||
|
<option value="fa-lock">🔒 Private</option>
|
||||||
|
<option value="fa-star">⭐ Star</option>
|
||||||
|
<option value="fa-heart">❤️ Heart</option>
|
||||||
|
<option value="fa-gamepad">🎮 Gaming</option>
|
||||||
|
<option value="fa-music">🎵 Music</option>
|
||||||
|
<option value="fa-video">📹 Video</option>
|
||||||
|
<option value="fa-info-circle">ℹ️ Info</option>
|
||||||
|
<option value="fa-question-circle">❓ Help</option>
|
||||||
|
<option value="fa-book">📖 Library</option>
|
||||||
|
<option value="fa-gift">🎁 Giveaways</option>
|
||||||
|
<option value="fa-code">💻 Coding</option>
|
||||||
|
<option value="fa-terminal">⌨️ Bot</option>
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3" id="edit-channel-status-container" style="display: none;">
|
<div class="mb-3" id="edit-channel-status-container" style="display: none;">
|
||||||
@ -1219,14 +1329,181 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
|||||||
<script src="assets/js/voice.js?v=<?php echo time(); ?>"></script>
|
<script src="assets/js/voice.js?v=<?php echo time(); ?>"></script>
|
||||||
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
|
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
|
||||||
<script>
|
<script>
|
||||||
// Handle channel type in modal
|
// Handle channel type and icon in modals
|
||||||
|
const addChannelType = document.getElementById('add-channel-type');
|
||||||
|
const addChannelPrefix = document.getElementById('add-channel-prefix');
|
||||||
|
const addChannelIcon = document.querySelector('#addChannelModal select[name="icon"]');
|
||||||
|
|
||||||
|
const editChannelType = document.getElementById('edit-channel-type');
|
||||||
|
const editChannelPrefix = document.getElementById('edit-channel-prefix');
|
||||||
|
const editChannelIcon = document.getElementById('edit-channel-icon');
|
||||||
|
|
||||||
|
function getPrefixForType(type) {
|
||||||
|
if (type === 'voice') return '<i class="fa-solid fa-volume-up"></i>';
|
||||||
|
if (type === 'announcement') return '<i class="fa-solid fa-bullhorn"></i>';
|
||||||
|
if (type === 'rules') return '<i class="fa-solid fa-gavel"></i>';
|
||||||
|
if (type === 'forum') return '<i class="fa-solid fa-comments"></i>';
|
||||||
|
return '#';
|
||||||
|
}
|
||||||
|
|
||||||
|
function updatePrefix(typeSelect, iconSelect, prefixSpan) {
|
||||||
|
if (!prefixSpan || !typeSelect) return;
|
||||||
|
let prefix = getPrefixForType(typeSelect.value);
|
||||||
|
if (iconSelect && iconSelect.value) {
|
||||||
|
prefix += ` <i class="fa-solid ${iconSelect.value}"></i>`;
|
||||||
|
}
|
||||||
|
prefixSpan.innerHTML = prefix;
|
||||||
|
}
|
||||||
|
|
||||||
document.querySelectorAll('.add-channel-btn').forEach(btn => {
|
document.querySelectorAll('.add-channel-btn').forEach(btn => {
|
||||||
btn.addEventListener('click', function() {
|
btn.addEventListener('click', function() {
|
||||||
const type = this.getAttribute('data-type');
|
const type = this.getAttribute('data-type');
|
||||||
document.getElementById('channel-type-input').value = type;
|
const categoryId = this.getAttribute('data-category-id');
|
||||||
document.getElementById('channel-hash-prefix').textContent = type === 'text' ? '#' : '🔊';
|
if (addChannelType) {
|
||||||
|
addChannelType.value = type;
|
||||||
|
updatePrefix(addChannelType, addChannelIcon, addChannelPrefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add category_id hidden field or select it if we add it to addChannelModal
|
||||||
|
// Let's add a hidden field to addChannelModal for category_id
|
||||||
|
let catInput = document.getElementById('add-channel-category-id');
|
||||||
|
if (!catInput) {
|
||||||
|
catInput = document.createElement('input');
|
||||||
|
catInput.type = 'hidden';
|
||||||
|
catInput.name = 'category_id';
|
||||||
|
catInput.id = 'add-channel-category-id';
|
||||||
|
document.querySelector('#addChannelModal form').appendChild(catInput);
|
||||||
|
}
|
||||||
|
catInput.value = categoryId || '';
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (addChannelType) {
|
||||||
|
addChannelType.addEventListener('change', () => updatePrefix(addChannelType, addChannelIcon, addChannelPrefix));
|
||||||
|
}
|
||||||
|
if (addChannelIcon) {
|
||||||
|
addChannelIcon.addEventListener('change', () => updatePrefix(addChannelType, addChannelIcon, addChannelPrefix));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (editChannelType) {
|
||||||
|
editChannelType.addEventListener('change', () => updatePrefix(editChannelType, editChannelIcon, editChannelPrefix));
|
||||||
|
}
|
||||||
|
if (editChannelIcon) {
|
||||||
|
editChannelIcon.addEventListener('change', () => updatePrefix(editChannelType, editChannelIcon, editChannelPrefix));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initial update when opening edit modal
|
||||||
|
document.addEventListener('click', function(e) {
|
||||||
|
const btn = e.target.closest('.channel-settings-btn');
|
||||||
|
if (btn) {
|
||||||
|
// Fill basic fields to ensure they are present even if main.js fails
|
||||||
|
const modal = document.getElementById('editChannelModal');
|
||||||
|
if (modal) {
|
||||||
|
const idInput = document.getElementById('edit-channel-id');
|
||||||
|
const nameInput = document.getElementById('edit-channel-name');
|
||||||
|
const typeSelect = document.getElementById('edit-channel-type');
|
||||||
|
const iconSelect = document.getElementById('edit-channel-icon');
|
||||||
|
const categorySelect = document.getElementById('edit-channel-category-id');
|
||||||
|
|
||||||
|
if (idInput) idInput.value = btn.dataset.id || '';
|
||||||
|
if (nameInput) nameInput.value = btn.dataset.name || '';
|
||||||
|
if (typeSelect) typeSelect.value = btn.dataset.type || 'chat';
|
||||||
|
if (iconSelect) iconSelect.value = btn.dataset.icon || '';
|
||||||
|
if (categorySelect) categorySelect.value = btn.dataset.category || '';
|
||||||
|
|
||||||
|
// Also fill delete ID
|
||||||
|
const deleteIdInput = document.getElementById('delete-channel-id');
|
||||||
|
if (deleteIdInput) deleteIdInput.value = btn.dataset.id || '';
|
||||||
|
}
|
||||||
|
setTimeout(() => updatePrefix(editChannelType, editChannelIcon, editChannelPrefix), 100);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// SortableJS Implementation for Channels
|
||||||
|
<?php if ($can_manage_channels): ?>
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
// Sortable for groups (channels inside categories)
|
||||||
|
const groups = document.querySelectorAll('.category-group');
|
||||||
|
groups.forEach(group => {
|
||||||
|
new Sortable(group, {
|
||||||
|
group: 'channels',
|
||||||
|
animation: 150,
|
||||||
|
ghostClass: 'sortable-ghost',
|
||||||
|
onEnd: function() {
|
||||||
|
saveChannelOrders();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Sortable for categories themselves and top-level channels
|
||||||
|
const sidebar = document.getElementById('sidebar-channels-list');
|
||||||
|
new Sortable(sidebar, {
|
||||||
|
animation: 150,
|
||||||
|
draggable: '.category-wrapper, .channel-item-container:not(.category-group .channel-item-container)',
|
||||||
|
ghostClass: 'sortable-ghost',
|
||||||
|
onEnd: function() {
|
||||||
|
saveChannelOrders();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
async function saveChannelOrders() {
|
||||||
|
const orders = [];
|
||||||
|
let position = 0;
|
||||||
|
|
||||||
|
const sidebar = document.getElementById('sidebar-channels-list');
|
||||||
|
|
||||||
|
// Iterate over top-level items
|
||||||
|
const topLevelItems = sidebar.children;
|
||||||
|
|
||||||
|
Array.from(topLevelItems).forEach(item => {
|
||||||
|
if (item.classList.contains('category-wrapper')) {
|
||||||
|
// It's a category
|
||||||
|
const catId = item.dataset.id;
|
||||||
|
orders.push({
|
||||||
|
id: catId,
|
||||||
|
position: position++,
|
||||||
|
category_id: null
|
||||||
|
});
|
||||||
|
|
||||||
|
// 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
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else if (item.classList.contains('channel-item-container')) {
|
||||||
|
// It's a top level channel
|
||||||
|
orders.push({
|
||||||
|
id: item.dataset.id,
|
||||||
|
position: position++,
|
||||||
|
category_id: null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const resp = await fetch('api_v1_channels.php', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
action: 'reorder',
|
||||||
|
server_id: <?php echo $active_server_id; ?>,
|
||||||
|
orders: orders
|
||||||
|
})
|
||||||
|
});
|
||||||
|
const data = await resp.json();
|
||||||
|
if (!data.success) {
|
||||||
|
console.error('Failed to save channel order:', data.error);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Error saving channel order:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
<?php endif; ?>
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user