diff --git a/api_v1_channel_permissions.php b/api_v1_channel_permissions.php
index 4373c26..a6ce976 100644
--- a/api_v1_channel_permissions.php
+++ b/api_v1_channel_permissions.php
@@ -16,7 +16,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') {
$server_id = $channel['server_id'] ?? 0;
// Ensure @everyone role exists for this server
- $stmt = db()->prepare("SELECT id FROM roles WHERE server_id = ? AND (name = '@everyone' OR name = 'Everyone') LIMIT 1");
+ $stmt = db()->prepare("SELECT id FROM roles WHERE server_id = ? AND (LOWER(name) = '@everyone' OR LOWER(name) = 'everyone') LIMIT 1");
$stmt->execute([$server_id]);
$everyone = $stmt->fetch();
if (!$everyone && $server_id) {
@@ -46,18 +46,20 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') {
}
}
- if (!$has_everyone && $everyone_role_id) {
+ if (!$has_everyone && $everyone_role_id > 0) {
$stmt = db()->prepare("SELECT name, color FROM roles WHERE id = ?");
$stmt->execute([$everyone_role_id]);
$r = $stmt->fetch();
- $permissions[] = [
- 'channel_id' => (int)$channel_id,
- 'role_id' => (int)$everyone_role_id,
- 'allow_permissions' => 0,
- 'deny_permissions' => 0,
- 'role_name' => $r['name'],
- 'role_color' => $r['color']
- ];
+ if ($r) {
+ array_unshift($permissions, [
+ 'channel_id' => (int)$channel_id,
+ 'role_id' => (int)$everyone_role_id,
+ 'allow_permissions' => 0,
+ 'deny_permissions' => 0,
+ 'role_name' => $r['name'],
+ 'role_color' => $r['color']
+ ]);
+ }
}
echo json_encode(['success' => true, 'permissions' => $permissions]);
@@ -70,12 +72,22 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$allow = $data['allow'] ?? 0;
$deny = $data['deny'] ?? 0;
- // Check if user is owner of the server
- $stmt = db()->prepare("SELECT s.owner_id FROM servers s JOIN channels c ON s.id = c.server_id WHERE c.id = ?");
+ // Check permissions: Owner or MANAGE_CHANNELS or ADMINISTRATOR
+ require_once 'includes/permissions.php';
+ $stmt = db()->prepare("SELECT server_id FROM channels WHERE id = ?");
$stmt->execute([$channel_id]);
+ $ch = $stmt->fetch();
+ $server_id = $ch['server_id'] ?? 0;
+
+ $stmt = db()->prepare("SELECT owner_id FROM servers WHERE id = ?");
+ $stmt->execute([$server_id]);
$server = $stmt->fetch();
- if ($server && $server['owner_id'] == $user_id) {
+ $is_owner = ($server && $server['owner_id'] == $user_id);
+ $can_manage = Permissions::hasPermission($user_id, $server_id, Permissions::MANAGE_CHANNELS) ||
+ Permissions::hasPermission($user_id, $server_id, Permissions::ADMINISTRATOR);
+
+ if ($is_owner || $can_manage) {
$stmt = db()->prepare("
INSERT INTO channel_permissions (channel_id, role_id, allow_permissions, deny_permissions)
VALUES (?, ?, ?, ?)
@@ -93,12 +105,22 @@ if ($_SERVER['REQUEST_METHOD'] === 'DELETE') {
$channel_id = $data['channel_id'] ?? 0;
$role_id = $data['role_id'] ?? 0;
- // Check if user is owner
- $stmt = db()->prepare("SELECT s.owner_id FROM servers s JOIN channels c ON s.id = c.server_id WHERE c.id = ?");
+ // Check permissions
+ require_once 'includes/permissions.php';
+ $stmt = db()->prepare("SELECT server_id FROM channels WHERE id = ?");
$stmt->execute([$channel_id]);
+ $ch = $stmt->fetch();
+ $server_id = $ch['server_id'] ?? 0;
+
+ $stmt = db()->prepare("SELECT owner_id FROM servers WHERE id = ?");
+ $stmt->execute([$server_id]);
$server = $stmt->fetch();
- if ($server && $server['owner_id'] == $user_id) {
+ $is_owner = ($server && $server['owner_id'] == $user_id);
+ $can_manage = Permissions::hasPermission($user_id, $server_id, Permissions::MANAGE_CHANNELS) ||
+ Permissions::hasPermission($user_id, $server_id, Permissions::ADMINISTRATOR);
+
+ if ($is_owner || $can_manage) {
$stmt = db()->prepare("DELETE FROM channel_permissions WHERE channel_id = ? AND role_id = ?");
$stmt->execute([$channel_id, $role_id]);
echo json_encode(['success' => true]);
diff --git a/assets/js/main.js b/assets/js/main.js
index b4b3d00..7c2f329 100644
--- a/assets/js/main.js
+++ b/assets/js/main.js
@@ -759,8 +759,8 @@ document.addEventListener('DOMContentLoaded', () => {
channelPermissionsTabBtn?.addEventListener('click', async () => {
const channelId = document.getElementById('edit-channel-id').value;
currentSelectedOverrideRole = null;
- channelPermissionsSettings.style.display = 'none';
- noRoleSelectedView.style.display = 'flex';
+ channelPermissionsSettings.classList.add('d-none');
+ noRoleSelectedView.classList.remove('d-none');
await loadChannelPermissions(channelId);
await loadRolesForPermissions(channelId);
});
@@ -776,37 +776,68 @@ document.addEventListener('DOMContentLoaded', () => {
}
async function loadRolesForPermissions(channelId) {
- addPermRoleList.innerHTML = '';
- const resp = await fetch(`api_v1_roles.php?server_id=${activeServerId}`);
- const data = await resp.json();
- if (data.success) {
- // Filter out roles already in overrides
- const existingRoleIds = channelPermissionsData.map(p => parseInt(p.role_id));
- const availableRoles = data.roles.filter(role => !existingRoleIds.includes(parseInt(role.id)));
+ if (!addPermRoleList) return;
+ addPermRoleList.innerHTML = '
Loading roles...';
+
+ try {
+ const resp = await fetch(`api_v1_roles.php?server_id=${activeServerId}`);
+ const data = await resp.json();
+
+ if (data.success) {
+ addPermRoleList.innerHTML = '';
+
+ // Filter out roles already in overrides
+ const existingRoleIds = channelPermissionsData.map(p => parseInt(p.role_id));
+ const availableRoles = data.roles.filter(role => !existingRoleIds.includes(parseInt(role.id)));
+
+ if (availableRoles.length === 0) {
+ addPermRoleList.innerHTML = 'No more roles to add';
+ if (window.canManageServer) {
+ const divider = document.createElement('li');
+ divider.innerHTML = '
';
+ addPermRoleList.appendChild(divider);
+ const createLink = document.createElement('li');
+ createLink.innerHTML = ' Create roles in Server Settings';
+ addPermRoleList.appendChild(createLink);
+ }
+ return;
+ }
- if (availableRoles.length === 0) {
- addPermRoleList.innerHTML = 'No more roles to add';
- return;
+ // Add Roles section
+ const header = document.createElement('li');
+ header.innerHTML = '';
+ addPermRoleList.appendChild(header);
+
+ availableRoles.forEach(role => {
+ const li = document.createElement('li');
+ li.innerHTML = `
+
+ ${role.name}
+ `;
+ li.onclick = async (e) => {
+ e.preventDefault();
+ const postResp = await fetch('api_v1_channel_permissions.php', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ channel_id: channelId, role_id: role.id, allow: 0, deny: 0 })
+ });
+ const postData = await postResp.json();
+ if (postData.success) {
+ await loadChannelPermissions(channelId);
+ await loadRolesForPermissions(channelId);
+ selectOverrideRole(role.id, role.name);
+ } else {
+ alert("Error adding permission: " + (postData.error || "Unknown error"));
+ }
+ };
+ addPermRoleList.appendChild(li);
+ });
+ } else {
+ addPermRoleList.innerHTML = `Error: ${data.error || 'Failed to load'}`;
}
-
- availableRoles.forEach(role => {
- const li = document.createElement('li');
- li.innerHTML = `
-
- ${role.name}
- `;
- li.onclick = async (e) => {
- e.preventDefault();
- await fetch('api_v1_channel_permissions.php', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ channel_id: channelId, role_id: role.id, allow: 0, deny: 0 })
- });
- await loadChannelPermissions(channelId);
- selectOverrideRole(role.id, role.name);
- };
- addPermRoleList.appendChild(li);
- });
+ } catch (err) {
+ addPermRoleList.innerHTML = 'Network error';
+ console.error(err);
}
}
@@ -819,11 +850,13 @@ document.addEventListener('DOMContentLoaded', () => {
// Sort: @everyone always at top, then by name
const sortedData = [...channelPermissionsData].sort((a, b) => {
- const isAEveryone = a.role_name.toLowerCase().includes('everyone');
- const isBEveryone = b.role_name.toLowerCase().includes('everyone');
+ const nameA = (a.role_name || '').toLowerCase();
+ const nameB = (b.role_name || '').toLowerCase();
+ const isAEveryone = nameA.includes('everyone');
+ const isBEveryone = nameB.includes('everyone');
if (isAEveryone && !isBEveryone) return -1;
if (!isAEveryone && isBEveryone) return 1;
- return a.role_name.localeCompare(b.role_name);
+ return nameA.localeCompare(nameB);
});
sortedData.forEach(p => {
@@ -831,10 +864,10 @@ document.addEventListener('DOMContentLoaded', () => {
item.className = `list-group-item list-group-item-action bg-transparent text-white border-0 mb-1 p-2 small d-flex align-items-center ${currentSelectedOverrideRole == p.role_id ? 'active' : ''}`;
item.style.cursor = 'pointer';
item.innerHTML = `
-
- ${p.role_name}
+
+ ${p.role_name || 'Unknown Role'}
`;
- item.onclick = () => selectOverrideRole(p.role_id, p.role_name);
+ item.onclick = () => selectOverrideRole(p.role_id, p.role_name || 'Unknown Role');
channelPermissionsRolesList.appendChild(item);
});
}
@@ -847,8 +880,8 @@ document.addEventListener('DOMContentLoaded', () => {
renderRoleOverridesList(channelId);
selectedPermRoleName.textContent = roleName;
- noRoleSelectedView.style.display = 'none';
- channelPermissionsSettings.style.display = 'block';
+ noRoleSelectedView.classList.add('d-none');
+ channelPermissionsSettings.classList.remove('d-none');
// Load existing permissions for this role
const p = channelPermissionsData.find(perm => perm.role_id == roleId) || { allow_permissions: 0, deny_permissions: 0 };
@@ -881,8 +914,8 @@ document.addEventListener('DOMContentLoaded', () => {
});
currentSelectedOverrideRole = null;
- channelPermissionsSettings.style.display = 'none';
- noRoleSelectedView.style.display = 'flex';
+ channelPermissionsSettings.classList.add('d-none');
+ noRoleSelectedView.classList.remove('d-none');
loadChannelPermissions(channelId);
});
@@ -951,6 +984,13 @@ document.addEventListener('DOMContentLoaded', () => {
modal.querySelector('#edit-channel-name').value = channelName;
modal.querySelector('#header-channel-name').textContent = channelName;
modal.querySelector('#edit-channel-type').value = channelType;
+
+ // Force switch to Overview tab
+ const overviewTabBtn = modal.querySelector('[data-bs-target="#edit-channel-general"]');
+ if (overviewTabBtn) {
+ bootstrap.Tab.getOrCreateInstance(overviewTabBtn).show();
+ }
+
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 || '';
diff --git a/assets/pasted-20260215-214239-79c3300e.png b/assets/pasted-20260215-214239-79c3300e.png
new file mode 100644
index 0000000..252efa2
Binary files /dev/null and b/assets/pasted-20260215-214239-79c3300e.png differ
diff --git a/index.php b/index.php
index 5f3b5ab..7d67ea3 100644
--- a/index.php
+++ b/index.php
@@ -1270,14 +1270,13 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
Roles / Members
+
+
+
@@ -1286,7 +1285,7 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
-
+
Role Name
@@ -1580,6 +1579,12 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
if (iconSelect) iconSelect.value = btn.dataset.icon || '';
if (categorySelect) categorySelect.value = btn.dataset.category || '';
+ // Force switch to Overview tab
+ const overviewTabBtn = modal.querySelector('[data-bs-target="#edit-channel-general"]');
+ if (overviewTabBtn && typeof bootstrap !== 'undefined') {
+ bootstrap.Tab.getOrCreateInstance(overviewTabBtn).show();
+ }
+
// Also fill delete ID
const deleteIdInput = document.getElementById('delete-channel-id');
if (deleteIdInput) deleteIdInput.value = btn.dataset.id || '';