This commit is contained in:
Flatlogic Bot 2026-02-15 22:26:04 +00:00
parent 5494f1e4ee
commit c08cfebf52
4 changed files with 133 additions and 66 deletions

View File

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

View File

@ -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 = '<li><span class="dropdown-item text-muted">Loading roles...</span></li>';
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 = '<li><span class="dropdown-item disabled text-muted">No more roles to add</span></li>';
if (window.canManageServer) {
const divider = document.createElement('li');
divider.innerHTML = '<hr class="dropdown-divider border-secondary opacity-25">';
addPermRoleList.appendChild(divider);
const createLink = document.createElement('li');
createLink.innerHTML = '<a class="dropdown-item small text-info" href="#" data-bs-toggle="modal" data-bs-target="#serverSettingsModal" style="font-size: 0.8em;"><i class="fa-solid fa-gear me-1"></i> Create roles in Server Settings</a>';
addPermRoleList.appendChild(createLink);
}
return;
}
if (availableRoles.length === 0) {
addPermRoleList.innerHTML = '<li><span class="dropdown-item disabled">No more roles to add</span></li>';
return;
// Add Roles section
const header = document.createElement('li');
header.innerHTML = '<h6 class="dropdown-header text-uppercase" style="font-size: 0.65em; color: #949ba4;">Roles</h6>';
addPermRoleList.appendChild(header);
availableRoles.forEach(role => {
const li = document.createElement('li');
li.innerHTML = `<a class="dropdown-item d-flex align-items-center gap-2 py-2" href="#">
<div style="width: 12px; height: 12px; border-radius: 50%; background-color: ${role.color || '#99aab5'}; border: 1px solid rgba(255,255,255,0.1);"></div>
<span style="color: #dbdee1; font-size: 0.9em;">${role.name}</span>
</a>`;
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 = `<li><span class="dropdown-item text-danger">Error: ${data.error || 'Failed to load'}</span></li>`;
}
availableRoles.forEach(role => {
const li = document.createElement('li');
li.innerHTML = `<a class="dropdown-item d-flex align-items-center gap-2" href="#">
<div style="width: 10px; height: 10px; border-radius: 50%; background-color: ${role.color};"></div>
${role.name}
</a>`;
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 = '<li><span class="dropdown-item text-danger">Network error</span></li>';
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 = `
<div style="width: 8px; height: 8px; border-radius: 50%; background-color: ${p.role_color}; margin-right: 8px; flex-shrink: 0;"></div>
<span class="flex-grow-1 text-truncate">${p.role_name}</span>
<div style="width: 8px; height: 8px; border-radius: 50%; background-color: ${p.role_color || '#99aab5'}; margin-right: 8px; flex-shrink: 0;"></div>
<span class="flex-grow-1 text-truncate">${p.role_name || 'Unknown Role'}</span>
`;
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 || '';

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

View File

@ -1270,14 +1270,13 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
<div class="p-3 border-bottom border-secondary d-flex justify-content-between align-items-center">
<span class="small fw-bold text-uppercase" style="font-size: 0.7em; color: #dbdee1;">Roles / Members</span>
<div class="dropdown">
<button class="btn btn-sm btn-link text-white p-0" type="button" data-bs-toggle="dropdown" style="text-decoration: none;">
<i class="fa-solid fa-plus-circle"></i>
</button>
<ul class="dropdown-menu dropdown-menu-dark shadow" id="add-permission-role-list" style="max-height: 300px; overflow-y: auto;">
<!-- Roles loaded here -->
</ul>
</div>
<button class="btn btn-sm btn-link text-white p-0" type="button" data-bs-toggle="dropdown" title="Add Role or Member" style="text-decoration: none; opacity: 0.8;">
<i class="fa-solid fa-plus-circle" style="font-size: 1.1rem;"></i>
</button>
<ul class="dropdown-menu dropdown-menu-dark shadow border-secondary" id="add-permission-role-list" style="max-height: 300px; overflow-y: auto; min-width: 200px;">
<!-- Roles loaded here -->
</ul>
</div>
</div>
<div id="channel-permissions-roles-list" class="list-group list-group-flush overflow-auto flex-grow-1" style="max-height: 350px; overflow-x: hidden;">
<!-- List of roles with overrides -->
@ -1286,7 +1285,7 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
<!-- Main: Permission Settings -->
<div class="col-8 d-flex flex-column" style="background-color: #313338;">
<div id="channel-permissions-settings" style="display: none;" class="h-100 d-flex flex-column">
<div id="channel-permissions-settings" class="h-100 d-flex flex-column d-none">
<div class="p-3 border-bottom border-secondary d-flex justify-content-between align-items-center">
<h6 class="mb-0 fw-bold" id="selected-perm-role-name">Role Name</h6>
<button class="btn btn-sm btn-outline-danger py-0 px-2" id="remove-selected-perm-role" style="font-size: 0.75em;">Clear Overrides</button>
@ -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 || '';