Autosave: 20260215-190103

This commit is contained in:
Flatlogic Bot 2026-02-15 19:01:03 +00:00
parent dfd640b430
commit 652014e524
6 changed files with 109 additions and 46 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -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', () => {
<div style="flex: 1;">
<div class="message-author" style="font-size: 0.85em; ${authorStyle}">
${escapeHTML(msg.username)}
${msg.role_icon ? `<img src="${msg.role_icon}" class="role-icon ms-1" style="width: 16px; height: 16px; vertical-align: middle; object-fit: contain;">` : ''}
<span class="message-time">${msg.time}</span>
</div>
<div class="message-text" style="font-size: 0.9em;">
@ -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 = `
<div class="d-flex align-items-center">
<div style="width: 14px; height: 14px; border-radius: 50%; background-color: ${role.color}; margin-right: 12px; box-shadow: 0 0 5px ${role.color}88;"></div>
<span class="fw-medium">${role.name}</span>
${role.icon_url ? (isUrl ? `<img src="${role.icon_url}" class="ms-1" style="width: 16px; height: 16px; object-fit: contain;">` : `<span class="ms-1" style="font-size: 14px;">${role.icon_url}</span>`) : ''}
</div>
<div>
<button class="btn btn-sm btn-outline-light edit-role-btn-v2" data-id="${role.id}" data-name="${role.name}" data-color="${role.color}" data-perms="${role.permissions}">Edit</button>
<button class="btn btn-sm btn-outline-light edit-role-btn-v2" data-id="${role.id}" data-name="${role.name}" data-color="${role.color}" data-perms="${role.permissions}" data-icon="${role.icon_url || ''}">Edit</button>
<button class="btn btn-sm btn-outline-danger delete-role-btn" data-id="${role.id}">×</button>
</div>
`;
@ -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 ? `<img src="${member.role_icon}" class="role-icon ms-1" style="width: 14px; height: 14px; vertical-align: middle; object-fit: contain;">` : `<span class="ms-1" style="font-size: 14px; vertical-align: middle;">${member.role_icon}</span>`) : '';
item.innerHTML = `
<div class="d-flex align-items-center flex-grow-1">
<div class="message-avatar me-2" style="width: 32px; height: 32px; ${member.avatar_url ? `background-image: url('${member.avatar_url}');` : ''}"></div>
<div class="flex-grow-1">
<div class="fw-bold small" style="color: ${member.role_color || 'inherit'}">${member.username}</div>
<div class="fw-bold small" style="color: ${member.role_color || 'inherit'}">
${escapeHTML(member.username)}
${roleIconHtml}
</div>
<div class="member-roles-assign-list">
${rolesHtml || '<span class="text-muted small">No roles available</span>'}
</div>
@ -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 ? `<img src="${msg.role_icon}" class="role-icon ms-1" style="width: 20px; height: 20px; vertical-align: middle; object-fit: contain;">` : `<span class="ms-1" style="font-size: 20px; vertical-align: middle;">${msg.role_icon}</span>`) : '';
div.innerHTML = `
<div class="message-avatar" style="${avatarStyle}"></div>
<div class="message-content">
<div class="message-header">
<span class="message-author" style="${authorStyle}">${escapeHTML(msg.username)}</span>
${roleIcon}
<span class="message-time">${msg.time}</span>
${pinnedBadge}
</div>

View File

@ -1,5 +1,15 @@
<?php
require_once 'auth/session.php';
function renderRoleIcon($icon, $size = '14px') {
if (empty($icon)) return '';
$isUrl = (strpos($icon, 'http') === 0 || strpos($icon, '/') === 0);
if ($isUrl) {
return '<img src="' . htmlspecialchars($icon) . '" class="role-icon ms-1" style="width: '.$size.'; height: '.$size.'; vertical-align: middle; object-fit: contain;">';
} else {
return '<span class="ms-1" style="font-size: '.$size.'; vertical-align: middle;">' . htmlspecialchars($icon) . '</span>';
}
}
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'] ?? '';
<a href="?server_id=<?php echo $s['id']; ?>"
class="server-icon <?php echo $s['id'] == $active_server_id ? 'active' : ''; ?>"
title="<?php echo htmlspecialchars($s['name']); ?>"
style="<?php echo !empty($s['icon_url']) ? "background-image: url('{$s['icon_url']}'); background-size: cover;" : ""; ?>">
style="<?php
if (!empty($s['icon_url'])) {
echo "background-image: url('{$s['icon_url']}'); background-size: cover;";
} else if (!empty($s['theme_color'])) {
echo "background-color: {$s['theme_color']};";
}
?>">
<?php echo empty($s['icon_url']) ? mb_substr($s['name'], 0, 1) : ''; ?>
</a>
<?php endforeach; ?>
@ -331,8 +353,7 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
data-limit="<?php echo $c['message_limit']; ?>"
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-category="<?php echo $c['category_id'] ?? ''; ?>">
<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; ?>
@ -474,6 +495,7 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
<div class="message-content">
<div class="message-author" style="<?php echo !empty($m['role_color']) ? "color: {$m['role_color']};" : ""; ?>">
<?php echo htmlspecialchars($m['username']); ?>
<?php echo renderRoleIcon($m['role_icon'], '20px'); ?>
<span class="message-time"><?php echo date('H:i', strtotime($m['created_at'])); ?></span>
<?php if ($is_solution): ?>
<span class="badge bg-success ms-2">SOLUTION</span>
@ -577,7 +599,11 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
<span class="badge rounded-pill ms-1" style="background-color: <?php echo htmlspecialchars($t_color); ?>; font-size: 0.6em;"><?php echo htmlspecialchars($t_name); ?></span>
<?php endforeach; endif; ?>
</div>
<div class="thread-meta small text-muted">Started by <?php echo htmlspecialchars($thread['username']); ?> • <?php echo $thread['message_count']; ?> messages</div>
<div class="thread-meta small text-muted">
Started by <span style="<?php echo !empty($thread['role_color']) ? "color: {$thread['role_color']};" : ""; ?>"><?php echo htmlspecialchars($thread['username']); ?></span>
<?php echo renderRoleIcon($thread['role_icon'], '14px'); ?>
<?php echo $thread['message_count']; ?> messages
</div>
</div>
<div class="thread-activity text-end small text-muted">
<?php if($thread['last_activity']): ?>
@ -605,6 +631,7 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
<div class="message-header">
<span class="message-author" style="<?php echo !empty($m['role_color']) ? "color: {$m['role_color']};" : ""; ?>">
<?php echo htmlspecialchars($m['username']); ?>
<?php echo renderRoleIcon($m['role_icon'], '20px'); ?>
</span>
<span class="message-time"><?php echo date('H:i', strtotime($m['created_at'])); ?></span>
<?php if ($m['is_pinned']): ?>
@ -752,6 +779,7 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
</div>
<span style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap; <?php echo !empty($m['role_color']) ? "color: {$m['role_color']};" : ""; ?>">
<?php echo htmlspecialchars($m['username']); ?>
<?php echo renderRoleIcon($m['role_icon'], '16px'); ?>
</span>
</div>
<?php endforeach; ?>
@ -871,6 +899,11 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
<input type="text" name="name" class="form-control" value="<?php echo htmlspecialchars($active_server_name ?? ''); ?>" required>
</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="<?php echo $active_server['theme_color'] ?? '#5865f2'; ?>" title="Choose server theme color">
</div>
<div id="server-icon-search-results" class="d-flex flex-wrap gap-2 mb-3 overflow-auto" style="max-height: 150px;"></div>
<div class="mb-3">
@ -1172,11 +1205,6 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
<div class="form-text text-muted" style="font-size: 0.8em;">Keep only the most recent messages.</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" id="edit-channel-theme" class="form-control form-control-color w-100" value="#5865f2" title="Choose channel theme color">
</div>
<button type="submit" class="btn btn-primary w-100 mb-2">Save Changes</button>
</form>
<button type="button" id="clear-channel-history-btn" class="btn btn-warning w-100 mb-2">Vider l'historique</button>
@ -1256,6 +1284,13 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
<label class="form-label text-uppercase fw-bold" style="font-size: 0.7em; color: var(--text-muted);">Role Color</label>
<input type="color" id="edit-role-color" class="form-control form-control-color w-100">
</div>
<div class="mb-3">
<label class="form-label text-uppercase fw-bold" style="font-size: 0.7em; color: var(--text-muted);">Role Icon (Emoji or URL)</label>
<div class="input-group">
<input type="text" id="edit-role-icon" class="form-control" placeholder="https://example.com/icon.png">
<button class="btn btn-outline-secondary" type="button" id="role-emoji-picker-btn">😊</button>
</div>
</div>
<div class="mb-3">
<label class="form-label text-uppercase fw-bold" style="font-size: 0.7em; color: var(--text-muted);">Permissions</label>
<div id="role-permissions-checkboxes" class="p-2 bg-dark rounded">