diff --git a/Core/API/UserAPI.class.php b/Core/API/UserAPI.class.php index eed0bfa..9032dab 100644 --- a/Core/API/UserAPI.class.php +++ b/Core/API/UserAPI.class.php @@ -237,7 +237,7 @@ namespace Core\API\User { public function __construct(Context $context, $externalCall = false) { parent::__construct($context, $externalCall, - self::getPaginationParameters(['id', 'name', 'fullName', 'email', 'groups', 'registeredAt', 'active', 'confirmed'], + self::getPaginationParameters(['id', 'name', 'fullName', 'email', 'groups', 'lastOnline', 'registeredAt', 'active', 'confirmed'], 'id', 'asc') ); } @@ -316,20 +316,17 @@ namespace Core\API\User { } else if ($user === null) { return $this->createError("User not found"); } else { - - $queriedUser = $user->jsonSerialize(); + // allow access to unconfirmed users only when we have administrative privileges, or we are querying ourselves $currentUser = $this->context->getUser(); - - // full info only when we have administrative privileges, or we are querying ourselves $fullInfo = ($userId === $currentUser->getId() || $currentUser->hasGroup(Group::ADMIN) || $currentUser->hasGroup(Group::SUPPORT)); - if (!$fullInfo && !$queriedUser["confirmed"]) { + if (!$fullInfo && !$user->isConfirmed()) { return $this->createError("No permissions to access this user"); } - $this->result["user"] = $queriedUser; + $this->result["user"] = $user->jsonSerialize(); } return $this->success; diff --git a/Core/core.php b/Core/core.php index 46e1679..672c7bf 100644 --- a/Core/core.php +++ b/Core/core.php @@ -10,7 +10,7 @@ if (is_file($autoLoad)) { require_once $autoLoad; } -const WEBBASE_VERSION = "2.4.1"; +const WEBBASE_VERSION = "2.4.2"; spl_autoload_extensions(".php"); spl_autoload_register(function ($class) { diff --git a/react/admin-panel/src/AdminDashboard.jsx b/react/admin-panel/src/AdminDashboard.jsx index a354537..7d262e4 100644 --- a/react/admin-panel/src/AdminDashboard.jsx +++ b/react/admin-panel/src/AdminDashboard.jsx @@ -88,7 +88,7 @@ export default function AdminDashboard(props) { }/> }/> }/> - }/> + }/> }/> }/> } /> diff --git a/react/admin-panel/src/elements/profile-link.jsx b/react/admin-panel/src/elements/profile-link.jsx index 659cc6c..9718293 100644 --- a/react/admin-panel/src/elements/profile-link.jsx +++ b/react/admin-panel/src/elements/profile-link.jsx @@ -15,6 +15,6 @@ export default function ProfileLink(props) { return - {text ? text : (user.fullName || user.name)} + {typeof text === "string" ? text : (user.fullName || user.name)} } \ No newline at end of file diff --git a/react/admin-panel/src/elements/sidebar.js b/react/admin-panel/src/elements/sidebar.js index e9114ff..a620b61 100644 --- a/react/admin-panel/src/elements/sidebar.js +++ b/react/admin-panel/src/elements/sidebar.js @@ -23,8 +23,7 @@ const DrawerHeader = styled('div')(({ theme }) => ({ padding: theme.spacing(0, 1), ...theme.mixins.toolbar, "& > button": { - display: 'flex', - marginLeft: "auto", + display: "flex", }, "& > img": { width: 30, @@ -122,22 +121,25 @@ export default function Sidebar(props) { onFetchLanguages(); }, []); - const menuItems = { + const menuItems= { "dashboard": { "name": "admin.dashboard", "icon": }, "users": { "name": "admin.users", - "icon": + "icon": , + "match": /\/admin\/(users|user\/.*)/ }, "groups": { "name": "admin.groups", - "icon": + "icon": , + "match": /\/admin\/(groups|group\/.*)/ }, "routes": { "name": "admin.page_routes", - "icon": + "icon": , + "match": /\/admin\/(routes|route\/.*)/ }, "settings": { "name": "admin.settings", @@ -172,8 +174,15 @@ export default function Sidebar(props) { let li = []; for (const [id, menuItem] of Object.entries(menuItems)) { - const match= /^\/admin\/(.*)$/.exec(currentPath); - const active = match?.length >= 2 && match[1] === id; + + let active; + if (menuItem.hasOwnProperty("match")) { + active = !!menuItem.match.exec(currentPath); + } else { + const match= /^\/admin\/(.*)$/.exec(currentPath); + active = match?.length >= 2 && match[1] === id; + } + li.push( navigate(`/admin/${id}`)} />); } @@ -188,14 +197,16 @@ export default function Sidebar(props) { WebBase >} - setDrawerOpen(!drawerOpen)}> + setDrawerOpen(!drawerOpen)}> {drawerOpen ? : } {L("account.logged_in_as")}: - navigate("/admin/profile")} /> @@ -216,7 +227,6 @@ export default function Sidebar(props) { : setAnchorEl(e.currentTarget)} sx={{ diff --git a/react/admin-panel/src/views/log-view.js b/react/admin-panel/src/views/log-view.js index 5476f00..874177c 100644 --- a/react/admin-panel/src/views/log-view.js +++ b/react/admin-panel/src/views/log-view.js @@ -3,7 +3,7 @@ import {LocaleContext} from "shared/locale"; import {Link} from "react-router-dom"; import usePagination from "shared/hooks/pagination"; import {DataColumn, DataTable, DateTimeColumn, NumericColumn, StringColumn} from "shared/elements/data-table"; -import {Box, FormControl, FormGroup, FormLabel, Grid, IconButton, MenuItem, TextField} from "@mui/material"; +import {Box, FormControl, FormGroup, FormLabel, Grid, IconButton, MenuItem, styled, TextField} from "@mui/material"; import {DateTimePicker} from "@mui/x-date-pickers"; import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns'; import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; @@ -12,12 +12,19 @@ import {format, toDate} from "date-fns"; import {ExpandLess, ExpandMore} from "@mui/icons-material"; import ViewContent from "../elements/view-content"; +const StyledLogMessage = styled(Box)(props => ({ + alignSelf: "center", + "& pre": { + whiteSpace: "break-spaces" + } +})); + export default function LogView(props) { // meta const api = props.api; const showDialog = props.showDialog; - const {translate: L, requestModules, currentLocale} = useContext(LocaleContext); + const {translate: L, requestModules, currentLocale, toDateFns} = useContext(LocaleContext); const pagination = usePagination(); // data @@ -89,11 +96,11 @@ export default function LogView(props) { } - + {entry.showDetails ? entry.message : lines[0]} - + } return column; @@ -130,7 +137,7 @@ export default function LogView(props) { {L("logs.timestamp")} - + { setSaving(false); if (res.success) { - navigate("/admin/routes/" + res.routeId); + navigate("/admin/route/" + res.routeId); } else { showDialog(res.msg, L("routes.save_route_error")); } diff --git a/react/admin-panel/src/views/route/route-list.js b/react/admin-panel/src/views/route/route-list.js index 23abc38..249d274 100644 --- a/react/admin-panel/src/views/route/route-list.js +++ b/react/admin-panel/src/views/route/route-list.js @@ -131,7 +131,7 @@ export default function RouteListView(props) { } size={"small"} disabled={!props.api.hasPermission("routes/add")} - onClick={() => navigate("/admin/routes/new")} > + onClick={() => navigate("/admin/route/new")} > {L("general.add")} } size={"small"} @@ -173,7 +173,7 @@ export default function RouteListView(props) { navigate("/admin/routes/" + id)}> + onClick={() => navigate("/admin/route/" + id)}> - + { isConfigured ? : } { diff --git a/react/admin-panel/src/views/user/user-list.js b/react/admin-panel/src/views/user/user-list.js index da4444a..f7a2597 100644 --- a/react/admin-panel/src/views/user/user-list.js +++ b/react/admin-panel/src/views/user/user-list.js @@ -73,6 +73,7 @@ export default function UserListView(props) { new StringColumn(L("account.email"), "email"), groupColumn, new DateTimeColumn(L("account.registered_at"), "registeredAt"), + new DateTimeColumn(L("account.last_online"), "lastOnline"), new BoolColumn(L("account.active"), "active", { align: "center" }), new BoolColumn(L("account.confirmed"), "confirmed", { align: "center" }), new ControlsColumn(L("general.controls"), [ diff --git a/react/shared/elements/data-table.css b/react/shared/elements/data-table.css index 1852cf8..5d0e064 100644 --- a/react/shared/elements/data-table.css +++ b/react/shared/elements/data-table.css @@ -32,7 +32,7 @@ } .pagination-controls { - margin-top: 6px; + margin-top: 12px; display: grid; grid-template-columns: 75px auto; align-items: center;
{entry.showDetails ? entry.message : lines[0]}