From 652014e5248be7a83832152913bc4784353f3289 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Sun, 15 Feb 2026 19:01:03 +0000 Subject: [PATCH] Autosave: 20260215-190103 --- api_v1_channels.php | 12 +++----- api_v1_messages.php | 16 +++++++---- api_v1_roles.php | 12 ++++---- api_v1_servers.php | 10 +++++-- assets/js/main.js | 36 +++++++++++++++++------ index.php | 69 ++++++++++++++++++++++++++++++++++----------- 6 files changed, 109 insertions(+), 46 deletions(-) diff --git a/api_v1_channels.php b/api_v1_channels.php index cadc6d3..1cb0822 100644 --- a/api_v1_channels.php +++ b/api_v1_channels.php @@ -50,8 +50,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { $status = $_POST['status'] ?? null; $allow_file_sharing = isset($_POST['allow_file_sharing']) ? 1 : 0; $message_limit = !empty($_POST['message_limit']) ? (int)$_POST['message_limit'] : null; - $theme_color = $_POST['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; @@ -63,8 +61,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { if ($chan && Permissions::hasPermission($user_id, $chan['server_id'], Permissions::MANAGE_CHANNELS)) { $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 = ?, icon = ?, category_id = ? WHERE id = ?"); - $stmt->execute([$name, $type, $status, $allow_file_sharing, $theme_color, $message_limit, $icon, $category_id, $channel_id]); + $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]); } header('Location: index.php?server_id=' . $server_id . '&channel_id=' . $channel_id); exit; @@ -95,14 +93,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { $name = strtolower(preg_replace('/[^a-zA-Z0-9\-]/', '-', $name)); $allow_file_sharing = isset($_POST['allow_file_sharing']) ? 1 : 0; $message_limit = !empty($_POST['message_limit']) ? (int)$_POST['message_limit'] : null; - $theme_color = $_POST['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, icon, category_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"); - $stmt->execute([$server_id, $name, $type, $allow_file_sharing, $theme_color, $message_limit, $icon, $category_id]); + $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]); $channel_id = db()->lastInsertId(); header('Location: index.php?server_id=' . $server_id . '&channel_id=' . $channel_id); diff --git a/api_v1_messages.php b/api_v1_messages.php index 8081a21..2c3794b 100644 --- a/api_v1_messages.php +++ b/api_v1_messages.php @@ -45,13 +45,14 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') { $stmt = db()->prepare(" SELECT m.*, u.username, u.avatar_url, - (SELECT r.color FROM roles r JOIN user_roles ur ON r.id = ur.role_id WHERE ur.user_id = u.id AND r.server_id = ? ORDER BY r.position DESC LIMIT 1) as role_color + (SELECT r.color FROM roles r JOIN user_roles ur ON r.id = ur.role_id WHERE ur.user_id = u.id AND r.server_id = ? ORDER BY r.position DESC LIMIT 1) as role_color, + (SELECT r.icon_url FROM roles r JOIN user_roles ur ON r.id = ur.role_id WHERE ur.user_id = u.id AND r.server_id = ? ORDER BY r.position DESC LIMIT 1) as role_icon FROM messages m JOIN users u ON m.user_id = u.id WHERE m.channel_id = ? AND m.is_pinned = 1 ORDER BY m.created_at DESC "); - $stmt->execute([$server_id ?: 0, $channel_id]); + $stmt->execute([$server_id ?: 0, $server_id ?: 0, $channel_id]); $msgs = $stmt->fetchAll(); foreach ($msgs as &$m) { @@ -76,13 +77,14 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') { $stmt = db()->prepare(" SELECT m.*, u.username, u.avatar_url, - (SELECT r.color FROM roles r JOIN user_roles ur ON r.id = ur.role_id WHERE ur.user_id = u.id AND r.server_id = ? ORDER BY r.position DESC LIMIT 1) as role_color + (SELECT r.color FROM roles r JOIN user_roles ur ON r.id = ur.role_id WHERE ur.user_id = u.id AND r.server_id = ? ORDER BY r.position DESC LIMIT 1) as role_color, + (SELECT r.icon_url FROM roles r JOIN user_roles ur ON r.id = ur.role_id WHERE ur.user_id = u.id AND r.server_id = ? ORDER BY r.position DESC LIMIT 1) as role_icon FROM messages m JOIN users u ON m.user_id = u.id WHERE m.channel_id = ? AND m.id > ? ORDER BY m.id ASC "); - $stmt->execute([$server_id ?: 0, $channel_id, $after_id]); + $stmt->execute([$server_id ?: 0, $server_id ?: 0, $channel_id, $after_id]); $msgs = $stmt->fetchAll(); foreach ($msgs as &$m) { @@ -259,12 +261,13 @@ try { // Fetch message with username and role color for the response $stmt = db()->prepare(" SELECT m.*, u.username, u.avatar_url, - (SELECT r.color FROM roles r JOIN user_roles ur ON r.id = ur.role_id WHERE ur.user_id = u.id AND r.server_id = ? ORDER BY r.position DESC LIMIT 1) as role_color + (SELECT r.color FROM roles r JOIN user_roles ur ON r.id = ur.role_id WHERE ur.user_id = u.id AND r.server_id = ? ORDER BY r.position DESC LIMIT 1) as role_color, + (SELECT r.icon_url FROM roles r JOIN user_roles ur ON r.id = ur.role_id WHERE ur.user_id = u.id AND r.server_id = ? ORDER BY r.position DESC LIMIT 1) as role_icon FROM messages m JOIN users u ON m.user_id = u.id WHERE m.id = ? "); - $stmt->execute([$server_id ?: 0, $last_id]); + $stmt->execute([$server_id ?: 0, $server_id ?: 0, $last_id]); $msg = $stmt->fetch(); echo json_encode([ @@ -275,6 +278,7 @@ try { 'username' => $msg['username'], 'avatar_url' => $msg['avatar_url'], 'role_color' => $msg['role_color'], + 'role_icon' => $msg['role_icon'], 'content' => $msg['content'], 'attachment_url' => $msg['attachment_url'], 'metadata' => $msg['metadata'] ? json_decode($msg['metadata']) : null, diff --git a/api_v1_roles.php b/api_v1_roles.php index 20862b9..c42ae85 100644 --- a/api_v1_roles.php +++ b/api_v1_roles.php @@ -27,11 +27,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') { $roles = $stmt->fetchAll(); // Fetch members and their roles - $stmt = db()->prepare(" + $stmt = db()->prepare(" SELECT u.id, u.username, u.avatar_url, GROUP_CONCAT(r.id) as role_ids, GROUP_CONCAT(r.name) as role_names, - (SELECT r2.color FROM roles r2 JOIN user_roles ur2 ON r2.id = ur2.role_id WHERE ur2.user_id = u.id AND r2.server_id = ? ORDER BY r2.position DESC LIMIT 1) as role_color + (SELECT r2.color FROM roles r2 JOIN user_roles ur2 ON r2.id = ur2.role_id WHERE ur2.user_id = u.id AND r2.server_id = ? ORDER BY r2.position DESC LIMIT 1) as role_color, + (SELECT r2.icon_url FROM roles r2 JOIN user_roles ur2 ON r2.id = ur2.role_id WHERE ur2.user_id = u.id AND r2.server_id = ? ORDER BY r2.position DESC LIMIT 1) as role_icon FROM users u JOIN server_members sm ON u.id = sm.user_id LEFT JOIN user_roles ur ON u.id = ur.user_id @@ -39,7 +40,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') { WHERE sm.server_id = ? GROUP BY u.id "); - $stmt->execute([$server_id, $server_id, $server_id]); + $stmt->execute([$server_id, $server_id, $server_id, $server_id]); $members = $stmt->fetchAll(); echo json_encode([ @@ -87,10 +88,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { $role_id = $data['id'] ?? 0; $name = $data['name'] ?? ''; $color = $data['color'] ?? ''; + $icon_url = $data['icon_url'] ?? null; $perms = $data['permissions'] ?? 0; - $stmt = db()->prepare("UPDATE roles SET name = ?, color = ?, permissions = ? WHERE id = ? AND server_id = ?"); - $stmt->execute([$name, $color, $perms, $role_id, $server_id]); + $stmt = db()->prepare("UPDATE roles SET name = ?, color = ?, icon_url = ?, permissions = ? WHERE id = ? AND server_id = ?"); + $stmt->execute([$name, $color, $icon_url, $perms, $role_id, $server_id]); echo json_encode(['success' => true]); } elseif ($action === 'delete') { $role_id = $data['id'] ?? 0; diff --git a/api_v1_servers.php b/api_v1_servers.php index ae7bff5..f324857 100644 --- a/api_v1_servers.php +++ b/api_v1_servers.php @@ -26,9 +26,15 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { $server_id = $_POST['server_id'] ?? 0; $name = $_POST['name'] ?? ''; $icon_url = $_POST['icon_url'] ?? ''; + $theme_color = $_POST['theme_color'] ?? null; + if ($theme_color === '') $theme_color = null; + + require_once 'includes/permissions.php'; + if (Permissions::hasPermission($user_id, $server_id, Permissions::MANAGE_SERVER)) { + $stmt = db()->prepare("UPDATE servers SET name = ?, icon_url = ?, theme_color = ? WHERE id = ?"); + $stmt->execute([$name, $icon_url, $theme_color, $server_id]); + } - $stmt = db()->prepare("UPDATE servers SET name = ?, icon_url = ? WHERE id = ? AND owner_id = ?"); - $stmt->execute([$name, $icon_url, $server_id, $user_id]); header('Location: index.php?server_id=' . $server_id); exit; } diff --git a/assets/js/main.js b/assets/js/main.js index abc797d..a8dfe2e 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -233,7 +233,7 @@ document.addEventListener('DOMContentLoaded', () => { const addBtn = e.target.closest('.add-reaction-btn'); if (addBtn) { const msgId = addBtn.parentElement.dataset.messageId; - showEmojiPicker(addBtn, msgId); + showEmojiPicker(addBtn, (emoji) => toggleReaction(msgId, emoji)); return; } @@ -265,7 +265,7 @@ document.addEventListener('DOMContentLoaded', () => { } catch (e) { console.error(e); } } - function showEmojiPicker(anchor, messageId) { + function showEmojiPicker(anchor, callback) { document.querySelector('.emoji-picker')?.remove(); const picker = document.createElement('div'); picker.className = 'emoji-picker'; @@ -273,7 +273,7 @@ document.addEventListener('DOMContentLoaded', () => { const span = document.createElement('span'); span.textContent = emoji; span.onclick = () => { - toggleReaction(messageId, emoji); + callback(emoji); picker.remove(); }; picker.appendChild(span); @@ -436,6 +436,7 @@ document.addEventListener('DOMContentLoaded', () => {
${escapeHTML(msg.username)} + ${msg.role_icon ? `` : ''} ${msg.time}
@@ -656,7 +657,6 @@ document.addEventListener('DOMContentLoaded', () => { modal.querySelector('#edit-channel-files').checked = btn.dataset.files == '1'; modal.querySelector('#edit-channel-limit').value = btn.dataset.limit || ''; modal.querySelector('#edit-channel-status').value = btn.dataset.status || ''; - 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; @@ -815,7 +815,7 @@ document.addEventListener('DOMContentLoaded', () => { const addRoleBtn = document.getElementById('add-role-btn'); const membersTabBtn = document.getElementById('members-tab-btn'); const membersList = document.getElementById('server-members-list'); - const activeServerId = new URLSearchParams(window.location.search).get('server_id') || 1; + const activeServerId = window.activeServerId || new URLSearchParams(window.location.search).get('server_id') || 1; let serverRoles = []; let serverPermissions = []; @@ -847,13 +847,15 @@ document.addEventListener('DOMContentLoaded', () => { roles.forEach(role => { const item = document.createElement('div'); item.className = 'list-group-item bg-transparent text-white border-secondary d-flex justify-content-between align-items-center p-2 mb-1 rounded'; + const isUrl = role.icon_url && (role.icon_url.startsWith('http') || role.icon_url.startsWith('/')); item.innerHTML = `
${role.name} + ${role.icon_url ? (isUrl ? `` : `${role.icon_url}`) : ''}
- +
`; @@ -881,11 +883,17 @@ document.addEventListener('DOMContentLoaded', () => { `; }); + const isIconUrl = member.role_icon && (member.role_icon.startsWith('http') || member.role_icon.startsWith('/')); + const roleIconHtml = member.role_icon ? (isIconUrl ? `` : `${member.role_icon}`) : ''; + item.innerHTML = `
-
${member.username}
+
+ ${escapeHTML(member.username)} + ${roleIconHtml} +
${rolesHtml || 'No roles available'}
@@ -903,6 +911,7 @@ document.addEventListener('DOMContentLoaded', () => { document.getElementById('edit-role-id').value = role.id; document.getElementById('edit-role-name').value = role.name; document.getElementById('edit-role-color').value = role.color; + document.getElementById('edit-role-icon').value = role.icon; const permsContainer = document.getElementById('role-permissions-checkboxes'); permsContainer.innerHTML = ''; @@ -927,6 +936,7 @@ document.addEventListener('DOMContentLoaded', () => { const id = document.getElementById('edit-role-id').value; const name = document.getElementById('edit-role-name').value; const color = document.getElementById('edit-role-color').value; + const icon_url = document.getElementById('edit-role-icon').value; let permissions = 0; document.querySelectorAll('.perm-check:checked').forEach(cb => { @@ -937,7 +947,7 @@ document.addEventListener('DOMContentLoaded', () => { const resp = await fetch('api_v1_roles.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ action: 'update', server_id: activeServerId, id, name, color, permissions }) + body: JSON.stringify({ action: 'update', server_id: activeServerId, id, name, color, icon_url, permissions }) }); const data = await resp.json(); if (data.success) { @@ -947,6 +957,12 @@ document.addEventListener('DOMContentLoaded', () => { } catch (e) { console.error(e); } }); + document.getElementById('role-emoji-picker-btn')?.addEventListener('click', (e) => { + showEmojiPicker(e.currentTarget, (emoji) => { + document.getElementById('edit-role-icon').value = emoji; + }); + }); + membersList?.addEventListener('change', async (e) => { if (e.target.classList.contains('role-assign-check')) { const userId = e.target.dataset.userId; @@ -1537,11 +1553,15 @@ document.addEventListener('DOMContentLoaded', () => { const authorStyle = msg.role_color ? `color: ${msg.role_color};` : ''; + const isRoleIconUrl = msg.role_icon && (msg.role_icon.startsWith('http') || msg.role_icon.startsWith('/')); + const roleIcon = msg.role_icon ? (isRoleIconUrl ? `` : `${msg.role_icon}`) : ''; + div.innerHTML = `
${escapeHTML(msg.username)} + ${roleIcon} ${msg.time} ${pinnedBadge}
diff --git a/index.php b/index.php index a16ffe4..1246c37 100644 --- a/index.php +++ b/index.php @@ -1,5 +1,15 @@ '; + } else { + return '' . htmlspecialchars($icon) . ''; + } +} requireLogin(); $user = getCurrentUser(); @@ -81,8 +91,10 @@ if ($is_dm_view) { $is_owner = false; $can_manage_channels = false; $can_manage_server = false; + $active_server = null; foreach($servers as $s) { if($s['id'] == $active_server_id) { + $active_server = $s; $is_owner = ($s['owner_id'] == $current_user_id); $can_manage_channels = Permissions::hasPermission($current_user_id, $active_server_id, Permissions::MANAGE_CHANNELS) || $is_owner; $can_manage_server = Permissions::hasPermission($current_user_id, $active_server_id, Permissions::MANAGE_SERVER) || Permissions::hasPermission($current_user_id, $active_server_id, Permissions::ADMINISTRATOR) || $is_owner; @@ -90,7 +102,7 @@ if ($is_dm_view) { } } - $channel_theme = $active_channel['theme_color'] ?? null; + $channel_theme = $active_server['theme_color'] ?? null; $channel_type = $active_channel['type'] ?? 'chat'; $active_thread_id = $_GET['thread_id'] ?? null; @@ -104,13 +116,14 @@ if ($is_dm_view) { if ($active_thread) { $stmt = db()->prepare(" SELECT m.*, u.username, u.avatar_url, - (SELECT r.color FROM roles r JOIN user_roles ur ON r.id = ur.role_id WHERE ur.user_id = u.id AND r.server_id = ? ORDER BY r.position DESC LIMIT 1) as role_color + (SELECT r.color FROM roles r JOIN user_roles ur ON r.id = ur.role_id WHERE ur.user_id = u.id AND r.server_id = ? ORDER BY r.position DESC LIMIT 1) as role_color, + (SELECT r.icon_url FROM roles r JOIN user_roles ur ON r.id = ur.role_id WHERE ur.user_id = u.id AND r.server_id = ? ORDER BY r.position DESC LIMIT 1) as role_icon FROM messages m JOIN users u ON m.user_id = u.id WHERE m.thread_id = ? ORDER BY m.created_at ASC "); - $stmt->execute([$active_server_id, $active_thread_id]); + $stmt->execute([$active_server_id, $active_server_id, $active_thread_id]); $messages = $stmt->fetchAll(); } } @@ -133,27 +146,29 @@ if ($is_dm_view) { (SELECT COUNT(*) FROM messages m WHERE m.thread_id = t.id) as message_count, (SELECT MAX(created_at) FROM messages m WHERE m.thread_id = t.id) as last_activity, (SELECT r.color FROM roles r JOIN user_roles ur ON r.id = ur.role_id WHERE ur.user_id = u.id AND r.server_id = ? ORDER BY r.position DESC LIMIT 1) as role_color, + (SELECT r.icon_url FROM roles r JOIN user_roles ur ON r.id = ur.role_id WHERE ur.user_id = u.id AND r.server_id = ? ORDER BY r.position DESC LIMIT 1) as role_icon, (SELECT GROUP_CONCAT(CONCAT(ft.name, ':', ft.color) SEPARATOR '|') FROM thread_tags tt JOIN forum_tags ft ON tt.tag_id = ft.id WHERE tt.thread_id = t.id) as tags FROM forum_threads t JOIN users u ON t.user_id = u.id WHERE t.channel_id = ? " . $status_where . " ORDER BY last_activity DESC, t.created_at DESC "); - $stmt->execute([$active_server_id, $active_channel_id]); + $stmt->execute([$active_server_id, $active_server_id, $active_channel_id]); $threads = $stmt->fetchAll(); } else { // Fetch messages $display_limit = !empty($active_channel['message_limit']) ? (int)$active_channel['message_limit'] : 50; $stmt = db()->prepare(" SELECT m.*, u.username, u.avatar_url, - (SELECT r.color FROM roles r JOIN user_roles ur ON r.id = ur.role_id WHERE ur.user_id = u.id AND r.server_id = ? ORDER BY r.position DESC LIMIT 1) as role_color + (SELECT r.color FROM roles r JOIN user_roles ur ON r.id = ur.role_id WHERE ur.user_id = u.id AND r.server_id = ? ORDER BY r.position DESC LIMIT 1) as role_color, + (SELECT r.icon_url FROM roles r JOIN user_roles ur ON r.id = ur.role_id WHERE ur.user_id = u.id AND r.server_id = ? ORDER BY r.position DESC LIMIT 1) as role_icon FROM messages m JOIN users u ON m.user_id = u.id WHERE m.channel_id = ? ORDER BY m.created_at ASC LIMIT " . $display_limit . " "); - $stmt->execute([$active_server_id, $active_channel_id]); + $stmt->execute([$active_server_id, $active_server_id, $active_channel_id]); $messages = $stmt->fetchAll(); } @@ -163,12 +178,13 @@ if ($is_dm_view) { // Fetch members $stmt = db()->prepare(" SELECT u.id, u.username, u.avatar_url, u.status, - (SELECT r.color FROM roles r JOIN user_roles ur ON r.id = ur.role_id WHERE ur.user_id = u.id AND r.server_id = ? ORDER BY r.position DESC LIMIT 1) as role_color + (SELECT r.color FROM roles r JOIN user_roles ur ON r.id = ur.role_id WHERE ur.user_id = u.id AND r.server_id = ? ORDER BY r.position DESC LIMIT 1) as role_color, + (SELECT r.icon_url FROM roles r JOIN user_roles ur ON r.id = ur.role_id WHERE ur.user_id = u.id AND r.server_id = ? ORDER BY r.position DESC LIMIT 1) as role_icon FROM users u JOIN server_members sm ON u.id = sm.user_id WHERE sm.server_id = ? "); - $stmt->execute([$active_server_id, $active_server_id]); + $stmt->execute([$active_server_id, $active_server_id, $active_server_id]); $members = $stmt->fetchAll(); } @@ -236,7 +252,13 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? ''; "> + style=""> @@ -331,8 +353,7 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? ''; data-limit="" data-status="" data-icon="" - data-category="" - data-theme=""> + data-category=""> @@ -474,6 +495,7 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
"> + SOLUTION @@ -577,7 +599,11 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
-
Started by messages
+
+ Started by "> + + • messages +
@@ -605,6 +631,7 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
"> + @@ -752,6 +779,7 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
"> +
@@ -871,6 +899,11 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
+
+ + +
+
@@ -1172,11 +1205,6 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
Keep only the most recent messages.
-
- - -
- @@ -1256,6 +1284,13 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
+
+ +
+ + +
+