diff --git a/Core/API/RoutesAPI.class.php b/Core/API/RoutesAPI.class.php index 94282fa..0a0bc2d 100644 --- a/Core/API/RoutesAPI.class.php +++ b/Core/API/RoutesAPI.class.php @@ -75,8 +75,8 @@ namespace Core\API\Routes { use Core\Objects\DatabaseEntity\Group; use Core\Objects\DatabaseEntity\Route; use Core\Objects\Router\ApiRoute; + use Core\Objects\Router\EmptyRoute; use Core\Objects\Router\Router; - use Core\Objects\Router\StaticRoute; class Fetch extends RoutesAPI { @@ -363,7 +363,7 @@ namespace Core\API\Routes { $path = $this->getParam("path"); $pattern = $this->getParam("pattern"); $exact = $this->getParam("exact"); - $route = new StaticRoute($pattern, $exact, ""); + $route = new EmptyRoute($pattern, $exact, ""); $this->result["match"] = $route->match($path); return $this->success; } diff --git a/Core/Driver/Logger/Logger.class.php b/Core/Driver/Logger/Logger.class.php index 9022ac4..fe34024 100644 --- a/Core/Driver/Logger/Logger.class.php +++ b/Core/Driver/Logger/Logger.class.php @@ -54,7 +54,7 @@ class Logger { }, $debugTrace)); } - public function log(string $message, string $severity, bool $appendStackTrace = true) { + public function log(string $message, string $severity, bool $appendStackTrace = true): void { if ($appendStackTrace) { $message .= "\n" . $this->getStackTrace(); diff --git a/Core/Objects/DatabaseEntity/Group.class.php b/Core/Objects/DatabaseEntity/Group.class.php index 25e7f22..c517342 100644 --- a/Core/Objects/DatabaseEntity/Group.class.php +++ b/Core/Objects/DatabaseEntity/Group.class.php @@ -44,4 +44,14 @@ class Group extends DatabaseEntity { new Group(Group::SUPPORT, Group::GROUPS[Group::SUPPORT], "#007bff"), ]; } + + public function delete(SQL $sql): bool { + if (parent::delete($sql)) { + $handler = User::getHandler($sql); + $table = $handler->getNMRelation("groups")->getTableName(); + return $sql->delete($table)->whereEq("group_id", $this->id)->execute(); + } else { + return false; + } + } } \ No newline at end of file diff --git a/react/admin-panel/src/views/access-control-list.js b/react/admin-panel/src/views/access-control-list.js index 9135b90..78924d3 100644 --- a/react/admin-panel/src/views/access-control-list.js +++ b/react/admin-panel/src/views/access-control-list.js @@ -147,7 +147,7 @@ export default function AccessControlList(props) { const permission = acl[index]; if (query) { - if (!permission.method.toLowerCase().includes(query.toLowerCase()) || + if (!permission.method.toLowerCase().includes(query.toLowerCase()) && !permission.description.toLowerCase().includes(query.toLowerCase())) { continue; } diff --git a/react/admin-panel/src/views/group/group-edit.js b/react/admin-panel/src/views/group/group-edit.js index 1170da3..04d59c5 100644 --- a/react/admin-panel/src/views/group/group-edit.js +++ b/react/admin-panel/src/views/group/group-edit.js @@ -1,6 +1,7 @@ import {useCallback, useContext, useEffect, useState} from "react"; import {Link, useNavigate, useParams} from "react-router-dom"; import {LocaleContext} from "shared/locale"; +import SearchField from "shared/elements/search-field"; import {Button, CircularProgress} from "@material-ui/core"; import * as React from "react"; import ColorPicker from "material-ui-color-picker"; @@ -10,6 +11,7 @@ import usePagination from "shared/hooks/pagination"; import {Delete, KeyboardArrowLeft, Save} from "@material-ui/icons"; import Dialog from "shared/elements/dialog"; import {Box, FormControl, FormGroup, FormLabel, styled, TextField} from "@mui/material"; +import {Add} from "@mui/icons-material"; const defaultGroupData = { name: "", @@ -37,6 +39,7 @@ export default function EditGroupView(props) { const [fetchGroup, setFetchGroup] = useState(!isNewGroup); const [group, setGroup] = useState(isNewGroup ? defaultGroupData : null); const [members, setMembers] = useState([]); + const [selectedUser, setSelectedUser] = useState(null); // ui const [dialogData, setDialogData] = useState({open: false}); @@ -91,11 +94,11 @@ export default function EditGroupView(props) { setSaving(true); if (isNewGroup) { api.createGroup(group.name, group.color).then(data => { - if (!data.success) { + setSaving(false); + if (!data.success) { props.showDialog(data.msg, "Error creating group"); - setSaving(false); } else { - navigate(`/admin/groups/${data.id}`) + navigate(`/admin/group/${data.id}`) } }); } else { @@ -108,6 +111,41 @@ export default function EditGroupView(props) { } }, [api, groupId, isNewGroup, group]); + const onSearchUser = useCallback((async (query) => { + let data = await api.searchUser(query); + if (!data.success) { + props.showDialog(data.msg, "Error searching users"); + return []; + } + + return data.users; + }), [api]); + + const onAddMember = useCallback(() => { + if (selectedUser) { + api.addGroupMember(groupId, selectedUser.id).then(data => { + if (!data.success) { + props.showDialog(data.msg, "Error adding member"); + } else { + let newMembers = [...members]; + newMembers.push(selectedUser); + setMembers(newMembers); + } + setSelectedUser(null); + }); + } + }, [api, groupId, selectedUser]) + + const onDeleteGroup = useCallback(() => { + api.deleteGroup(groupId).then(data => { + if (!data.success) { + props.showDialog(data.msg, "Error deleting group"); + } else { + navigate("/admin/groups"); + } + }); + }, [api, groupId]); + useEffect(() => { onFetchGroup(); }, []); @@ -170,19 +208,31 @@ export default function EditGroupView(props) { } variant={"outlined"} onClick={() => navigate("/admin/groups")}> - {L("general.cancel")} + {L("general.go_back")} } color={"primary"} - variant={"outlined"} disabled={isSaving} + variant={"outlined"} + disabled={isSaving || (!api.hasPermission(isNewGroup ? "groups/create" : "groups/update"))} onClick={onSave}> {isSaving ? L("general.saving") + "…" : L("general.save")} + { !isNewGroup && + + } {!isNewGroup && api.hasPermission("groups/getMembers") ? -