permission update, routes, etc.
This commit is contained in:
parent
a8f4c84f60
commit
50ae32595d
@ -34,6 +34,7 @@ namespace Core\API\Permission {
|
|||||||
use Core\API\Parameter\StringType;
|
use Core\API\Parameter\StringType;
|
||||||
use Core\API\PermissionAPI;
|
use Core\API\PermissionAPI;
|
||||||
use Core\Driver\SQL\Column\Column;
|
use Core\Driver\SQL\Column\Column;
|
||||||
|
use Core\Driver\SQL\Condition\CondIn;
|
||||||
use Core\Driver\SQL\Condition\CondLike;
|
use Core\Driver\SQL\Condition\CondLike;
|
||||||
use Core\Driver\SQL\Query\Insert;
|
use Core\Driver\SQL\Query\Insert;
|
||||||
use Core\Driver\SQL\Strategy\UpdateStrategy;
|
use Core\Driver\SQL\Strategy\UpdateStrategy;
|
||||||
@ -168,25 +169,25 @@ namespace Core\API\Permission {
|
|||||||
return $this->createError("This method cannot be updated.");
|
return $this->createError("This method cannot be updated.");
|
||||||
}
|
}
|
||||||
|
|
||||||
$groups = $this->getParam("groups");
|
$groupIds = array_unique($this->getParam("groups"));
|
||||||
if (!empty($groups)) {
|
if (!empty($groupIds)) {
|
||||||
sort($groups);
|
sort($groupIds);
|
||||||
$availableGroups = Group::findAll($sql);
|
$availableGroups = Group::findAll($sql, new CondIn(new Column("id"), $groupIds));
|
||||||
foreach ($groups as $groupId) {
|
foreach ($groupIds as $groupId) {
|
||||||
if (!isset($availableGroups[$groupId])) {
|
if (!isset($availableGroups[$groupId])) {
|
||||||
return $this->createError("Unknown group id: $groupId");
|
return $this->createError("Group with id=$groupId does not exist.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($description === null) {
|
if ($description === null) {
|
||||||
$updateQuery = $sql->insert("ApiPermission", ["method", "groups", "isCore"])
|
$updateQuery = $sql->insert("ApiPermission", ["method", "groups", "isCore"])
|
||||||
->onDuplicateKeyStrategy(new UpdateStrategy(["method"], ["groups" => $groups]))
|
->onDuplicateKeyStrategy(new UpdateStrategy(["method"], ["groups" => $groupIds]))
|
||||||
->addRow($method, $groups, false);
|
->addRow($method, $groupIds, false);
|
||||||
} else {
|
} else {
|
||||||
$updateQuery = $sql->insert("ApiPermission", ["method", "groups", "isCore", "description"])
|
$updateQuery = $sql->insert("ApiPermission", ["method", "groups", "isCore", "description"])
|
||||||
->onDuplicateKeyStrategy(new UpdateStrategy(["method"], ["groups" => $groups, "description" => $description]))
|
->onDuplicateKeyStrategy(new UpdateStrategy(["method"], ["groups" => $groupIds, "description" => $description]))
|
||||||
->addRow($method, $groups, false, $description);
|
->addRow($method, $groupIds, false, $description);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->success = $updateQuery->execute() !== false;
|
$this->success = $updateQuery->execute() !== false;
|
||||||
|
@ -67,19 +67,11 @@ namespace Core\API\Routes {
|
|||||||
use Core\API\Parameter\Parameter;
|
use Core\API\Parameter\Parameter;
|
||||||
use Core\API\Parameter\StringType;
|
use Core\API\Parameter\StringType;
|
||||||
use Core\API\RoutesAPI;
|
use Core\API\RoutesAPI;
|
||||||
use Core\Driver\SQL\Condition\Compare;
|
|
||||||
use Core\Driver\SQL\Condition\CondBool;
|
|
||||||
use Core\Driver\SQL\Query\Insert;
|
use Core\Driver\SQL\Query\Insert;
|
||||||
use Core\Driver\SQL\Query\StartTransaction;
|
|
||||||
use Core\Objects\Context;
|
use Core\Objects\Context;
|
||||||
use Core\Objects\DatabaseEntity\Group;
|
use Core\Objects\DatabaseEntity\Group;
|
||||||
use Core\Objects\DatabaseEntity\Route;
|
use Core\Objects\DatabaseEntity\Route;
|
||||||
use Core\Objects\Router\DocumentRoute;
|
|
||||||
use Core\Objects\Router\RedirectPermanentlyRoute;
|
|
||||||
use Core\Objects\Router\RedirectRoute;
|
|
||||||
use Core\Objects\Router\RedirectTemporaryRoute;
|
|
||||||
use Core\Objects\Router\Router;
|
use Core\Objects\Router\Router;
|
||||||
use Core\Objects\Router\StaticFileRoute;
|
|
||||||
|
|
||||||
class Fetch extends RoutesAPI {
|
class Fetch extends RoutesAPI {
|
||||||
|
|
||||||
@ -419,5 +411,39 @@ namespace Core\API\Routes {
|
|||||||
$insert->addRow(self::getEndpoint(), [Group::ADMIN, Group::SUPPORT], "Allows users to regenerate the routing cache", true);
|
$insert->addRow(self::getEndpoint(), [Group::ADMIN, Group::SUPPORT], "Allows users to regenerate the routing cache", true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Check extends RoutesAPI {
|
||||||
|
public function __construct(Context $context, bool $externalCall) {
|
||||||
|
parent::__construct($context, $externalCall, [
|
||||||
|
"id" => new Parameter("id", Parameter::TYPE_INT),
|
||||||
|
"path" => new StringType("path")
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function _execute(): bool {
|
||||||
|
$sql = $this->context->getSQL();
|
||||||
|
$routeId = $this->getParam("id");
|
||||||
|
$route = Route::find($sql, $routeId);
|
||||||
|
if ($route === false) {
|
||||||
|
$this->lastError = $sql->getLastError();
|
||||||
|
return false;
|
||||||
|
} else if ($route === null) {
|
||||||
|
return $this->createError("Route not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->success = true;
|
||||||
|
$path = $this->getParam("path");
|
||||||
|
$this->result["match"] = $route->match($path);
|
||||||
|
return $this->success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getDefaultACL(Insert $insert): void {
|
||||||
|
$insert->addRow("routes/check",
|
||||||
|
[Group::ADMIN, Group::MODERATOR],
|
||||||
|
"Users with this permission can see, if a route is matched with the given path for debugging purposes",
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,14 +183,14 @@ namespace Core\API\User {
|
|||||||
$groups = [];
|
$groups = [];
|
||||||
$sql = $this->context->getSQL();
|
$sql = $this->context->getSQL();
|
||||||
|
|
||||||
// TODO: Currently low-privileged users can request any groups here, so a simple privilege escalation is possible. \
|
|
||||||
// what do? limit access to user/create to admins only?
|
|
||||||
$requestedGroups = array_unique($this->getParam("groups"));
|
$requestedGroups = array_unique($this->getParam("groups"));
|
||||||
if (!empty($requestedGroups)) {
|
if (!empty($requestedGroups)) {
|
||||||
$groups = Group::findAll($sql, new CondIn(new Column("id"), $requestedGroups));
|
$availableGroups = Group::findAll($sql, new CondIn(new Column("id"), $requestedGroups));
|
||||||
foreach ($requestedGroups as $groupId) {
|
foreach ($requestedGroups as $groupId) {
|
||||||
if (!isset($groups[$groupId])) {
|
if (!isset($availableGroups[$groupId])) {
|
||||||
return $this->createError("Group with id=$groupId does not exist.");
|
return $this->createError("Group with id=$groupId does not exist.");
|
||||||
|
} else if ($groupId === Group::ADMIN && !$this->context->getUser()->hasGroup(Group::ADMIN)) {
|
||||||
|
return $this->createError("You cannot create users with administrator groups.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -632,7 +632,7 @@ namespace Core\API\User {
|
|||||||
public function __construct(Context $context, bool $externalCall = false) {
|
public function __construct(Context $context, bool $externalCall = false) {
|
||||||
$parameters = array(
|
$parameters = array(
|
||||||
"username" => new StringType("username", 32),
|
"username" => new StringType("username", 32),
|
||||||
'email' => new Parameter('email', Parameter::TYPE_EMAIL),
|
"email" => new Parameter("email", Parameter::TYPE_EMAIL),
|
||||||
"password" => new StringType("password"),
|
"password" => new StringType("password"),
|
||||||
"confirmPassword" => new StringType("confirmPassword"),
|
"confirmPassword" => new StringType("confirmPassword"),
|
||||||
);
|
);
|
||||||
@ -746,7 +746,7 @@ namespace Core\API\User {
|
|||||||
'fullName' => new StringType('fullName', 64, true, NULL),
|
'fullName' => new StringType('fullName', 64, true, NULL),
|
||||||
'email' => new Parameter('email', Parameter::TYPE_EMAIL, true, NULL),
|
'email' => new Parameter('email', Parameter::TYPE_EMAIL, true, NULL),
|
||||||
'password' => new StringType('password', -1, true, NULL),
|
'password' => new StringType('password', -1, true, NULL),
|
||||||
'groups' => new Parameter('groups', Parameter::TYPE_ARRAY, true, NULL),
|
'groups' => new ArrayType('groups', Parameter::TYPE_INT, true, true, NULL),
|
||||||
'confirmed' => new Parameter('confirmed', Parameter::TYPE_BOOLEAN, true, NULL)
|
'confirmed' => new Parameter('confirmed', Parameter::TYPE_BOOLEAN, true, NULL)
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -777,19 +777,18 @@ namespace Core\API\User {
|
|||||||
|
|
||||||
$groupIds = array();
|
$groupIds = array();
|
||||||
if (!is_null($groups)) {
|
if (!is_null($groups)) {
|
||||||
$param = new Parameter('groupId', Parameter::TYPE_INT);
|
$groupIds = array_unique($groups);
|
||||||
|
|
||||||
foreach ($groups as $groupId) {
|
|
||||||
if (!$param->parseParam($groupId)) {
|
|
||||||
$value = print_r($groupId, true);
|
|
||||||
return $this->createError("Invalid Type for groupId in parameter groups: '$value' (Required: " . $param->getTypeName() . ")");
|
|
||||||
}
|
|
||||||
|
|
||||||
$groupIds[] = $param->value;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($id === $currentUser->getId() && !in_array(Group::ADMIN, $groupIds)) {
|
if ($id === $currentUser->getId() && !in_array(Group::ADMIN, $groupIds)) {
|
||||||
return $this->createError("Cannot remove Administrator group from own user.");
|
return $this->createError("Cannot remove Administrator group from own user.");
|
||||||
|
} else if (in_array(Group::ADMIN, $groupIds) && !$currentUser->hasGroup(Group::ADMIN)) {
|
||||||
|
return $this->createError("You cannot add the administrator group to other users.");
|
||||||
|
}
|
||||||
|
|
||||||
|
$availableGroups = Group::findAll($sql, new CondIn(new Column("id"), $groupIds));
|
||||||
|
foreach ($groupIds as $groupId) {
|
||||||
|
if (!isset($availableGroups[$groupId])) {
|
||||||
|
return $this->createError("Group with id=$groupId does not exist.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ use Core\Elements\Document;
|
|||||||
use Core\Objects\DatabaseEntity\GpgKey;
|
use Core\Objects\DatabaseEntity\GpgKey;
|
||||||
use Core\Objects\DatabaseEntity\Language;
|
use Core\Objects\DatabaseEntity\Language;
|
||||||
use Core\Objects\Router\Router;
|
use Core\Objects\Router\Router;
|
||||||
|
use DateTimeInterface;
|
||||||
|
|
||||||
// Source: https://www.rfc-editor.org/rfc/rfc9116
|
// Source: https://www.rfc-editor.org/rfc/rfc9116
|
||||||
class Security extends Document {
|
class Security extends Document {
|
||||||
@ -42,12 +43,12 @@ class Security extends Document {
|
|||||||
|
|
||||||
$lines = [
|
$lines = [
|
||||||
"# This project is based on the open-source framework hosted on https://github.com/rhergenreder/web-base",
|
"# This project is based on the open-source framework hosted on https://github.com/rhergenreder/web-base",
|
||||||
"# Any non-site specific issues can be reported via the github security reporting feature:",
|
"# Any non site-specific issues can be reported via the github security reporting feature:",
|
||||||
"# https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing/privately-reporting-a-security-vulnerability",
|
"# https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing/privately-reporting-a-security-vulnerability",
|
||||||
"",
|
"",
|
||||||
"Canonical: $baseUrl/.well-known/security.txt",
|
"Canonical: $baseUrl/.well-known/security.txt",
|
||||||
"Preferred-Languages: $languageCodes",
|
"Preferred-Languages: $languageCodes",
|
||||||
"Expires: " . $expires->format(\DateTime::ATOM),
|
"Expires: " . $expires->format(DateTimeInterface::ATOM),
|
||||||
"",
|
"",
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -85,6 +86,9 @@ class Security extends Document {
|
|||||||
return "Error exporting public key: " . $res["msg"];
|
return "Error exporting public key: " . $res["msg"];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
http_response_code(412);
|
||||||
|
return "No gpg key configured yet.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,8 +5,11 @@ return [
|
|||||||
"dashboard" => "Dashboard",
|
"dashboard" => "Dashboard",
|
||||||
"visitor_statistics" => "Besucherstatistiken",
|
"visitor_statistics" => "Besucherstatistiken",
|
||||||
"user_groups" => "Benutzer & Gruppen",
|
"user_groups" => "Benutzer & Gruppen",
|
||||||
|
"users" => "Benutzer",
|
||||||
|
"groups" => "Gruppen",
|
||||||
"page_routes" => "Seiten & Routen",
|
"page_routes" => "Seiten & Routen",
|
||||||
"settings" => "Einstellungen",
|
"settings" => "Einstellungen",
|
||||||
|
"acl" => "Zugriffsberechtigung",
|
||||||
"logs" => "Logs",
|
"logs" => "Logs",
|
||||||
"help" => "Hilfe",
|
"help" => "Hilfe",
|
||||||
];
|
];
|
@ -4,9 +4,12 @@ return [
|
|||||||
"title" => "Administration",
|
"title" => "Administration",
|
||||||
"dashboard" => "Dashboard",
|
"dashboard" => "Dashboard",
|
||||||
"visitor_statistics" => "Visitor Statistics",
|
"visitor_statistics" => "Visitor Statistics",
|
||||||
"user_groups" => "User & Groups",
|
"user_groups" => "Users & Groups",
|
||||||
|
"users" => "Users",
|
||||||
|
"groups" => "Groups",
|
||||||
"page_routes" => "Pages & Routes",
|
"page_routes" => "Pages & Routes",
|
||||||
"settings" => "Settings",
|
"settings" => "Settings",
|
||||||
|
"acl" => "Access Control",
|
||||||
"logs" => "Logs",
|
"logs" => "Logs",
|
||||||
"help" => "Help",
|
"help" => "Help",
|
||||||
];
|
];
|
@ -56,6 +56,10 @@ abstract class Route extends DatabaseEntity {
|
|||||||
$this->active = true;
|
$this->active = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isActive(): bool {
|
||||||
|
return $this->active;
|
||||||
|
}
|
||||||
|
|
||||||
private static function parseParamType(?string $type): ?int {
|
private static function parseParamType(?string $type): ?int {
|
||||||
if ($type === null || trim($type) === "") {
|
if ($type === null || trim($type) === "") {
|
||||||
return null;
|
return null;
|
||||||
@ -115,7 +119,7 @@ abstract class Route extends DatabaseEntity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function match(string $url) {
|
public function match(string $url): bool|array {
|
||||||
|
|
||||||
# /test/{abc}/{param:?}/{xyz:int}/{aaa:int?}
|
# /test/{abc}/{param:?}/{xyz:int}/{aaa:int?}
|
||||||
$patternParts = self::getParts(Router::cleanURL($this->pattern, false));
|
$patternParts = self::getParts(Router::cleanURL($this->pattern, false));
|
||||||
|
@ -61,7 +61,7 @@ class DocumentRoute extends Route {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function match(string $url) {
|
public function match(string $url): bool|array {
|
||||||
$match = parent::match($url);
|
$match = parent::match($url);
|
||||||
if ($match === false || !$this->loadClass()) {
|
if ($match === false || !$this->loadClass()) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -16,8 +16,8 @@ import clsx from "clsx";
|
|||||||
const Overview = lazy(() => import('./views/overview'));
|
const Overview = lazy(() => import('./views/overview'));
|
||||||
const UserListView = lazy(() => import('./views/user/user-list'));
|
const UserListView = lazy(() => import('./views/user/user-list'));
|
||||||
const UserEditView = lazy(() => import('./views/user/user-edit'));
|
const UserEditView = lazy(() => import('./views/user/user-edit'));
|
||||||
const GroupListView = lazy(() => import('./views/group-list'));
|
const GroupListView = lazy(() => import('./views/group/group-list'));
|
||||||
const EditGroupView = lazy(() => import('./views/group-edit'));
|
const EditGroupView = lazy(() => import('./views/group/group-edit'));
|
||||||
const LogView = lazy(() => import("./views/log-view"));
|
const LogView = lazy(() => import("./views/log-view"));
|
||||||
const AccessControlList = lazy(() => import("./views/access-control-list"));
|
const AccessControlList = lazy(() => import("./views/access-control-list"));
|
||||||
|
|
||||||
|
@ -29,9 +29,13 @@ export default function Sidebar(props) {
|
|||||||
"icon": "chart-bar",
|
"icon": "chart-bar",
|
||||||
},
|
},
|
||||||
"users": {
|
"users": {
|
||||||
"name": "admin.user_groups",
|
"name": "admin.users",
|
||||||
"icon": "users"
|
"icon": "users"
|
||||||
},
|
},
|
||||||
|
"groups": {
|
||||||
|
"name": "admin.groups",
|
||||||
|
"icon": "users-cog"
|
||||||
|
},
|
||||||
"pages": {
|
"pages": {
|
||||||
"name": "admin.page_routes",
|
"name": "admin.page_routes",
|
||||||
"icon": "copy",
|
"icon": "copy",
|
||||||
|
@ -5,30 +5,40 @@ import {DataColumn, DataTable, NumericColumn, StringColumn} from "shared/element
|
|||||||
import {Button, IconButton} from "@material-ui/core";
|
import {Button, IconButton} from "@material-ui/core";
|
||||||
import EditIcon from '@mui/icons-material/Edit';
|
import EditIcon from '@mui/icons-material/Edit';
|
||||||
import AddIcon from '@mui/icons-material/Add';
|
import AddIcon from '@mui/icons-material/Add';
|
||||||
|
import usePagination from "shared/hooks/pagination";
|
||||||
|
|
||||||
|
|
||||||
export default function GroupListView(props) {
|
export default function GroupListView(props) {
|
||||||
|
|
||||||
|
// meta
|
||||||
const {translate: L, requestModules, currentLocale} = useContext(LocaleContext);
|
const {translate: L, requestModules, currentLocale} = useContext(LocaleContext);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const pagination = usePagination();
|
||||||
|
const api = props.api;
|
||||||
|
|
||||||
|
// data
|
||||||
|
const [groups, setGroups] = useState([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
requestModules(props.api, ["general", "account"], currentLocale).then(data => {
|
requestModules(props.api, ["general", "account"], currentLocale).then(data => {
|
||||||
if (!data.success) {
|
if (!data.success) {
|
||||||
alert(data.msg);
|
props.showDialog(data.msg, "Error fetching localization");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, [currentLocale]);
|
}, [currentLocale]);
|
||||||
|
|
||||||
const onFetchGroups = useCallback(async (page, count, orderBy, sortOrder) => {
|
const onFetchGroups = useCallback(async (page, count, orderBy, sortOrder) => {
|
||||||
let res = await props.api.fetchGroups(page, count, orderBy, sortOrder);
|
|
||||||
|
api.fetchGroups(page, count, orderBy, sortOrder).then((res) => {
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
return Promise.resolve([res.groups, res.pagination]);
|
setGroups(res.groups);
|
||||||
|
pagination.update(res.pagination);
|
||||||
} else {
|
} else {
|
||||||
props.showAlert("Error fetching groups", res.msg);
|
props.showDialog(res.msg, "Error fetching groups");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}, []);
|
});
|
||||||
|
}, [api, pagination]);
|
||||||
|
|
||||||
const actionColumn = (() => {
|
const actionColumn = (() => {
|
||||||
let column = new DataColumn(L("general.actions"), null, false);
|
let column = new DataColumn(L("general.actions"), null, false);
|
||||||
@ -69,9 +79,15 @@ export default function GroupListView(props) {
|
|||||||
{L("general.create_new")}
|
{L("general.create_new")}
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
<DataTable className={"table table-striped"}
|
<DataTable
|
||||||
|
data={groups}
|
||||||
|
pagination={pagination}
|
||||||
|
defaultSortOrder={"asc"}
|
||||||
|
defaultSortColumn={0}
|
||||||
|
className={"table table-striped"}
|
||||||
fetchData={onFetchGroups}
|
fetchData={onFetchGroups}
|
||||||
placeholder={"No groups to display"}
|
placeholder={"No groups to display"}
|
||||||
|
title={L("account.groups")}
|
||||||
columns={columnDefinitions} />
|
columns={columnDefinitions} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
@ -123,8 +123,8 @@ export function DataTable(props) {
|
|||||||
{title ?
|
{title ?
|
||||||
<h3>
|
<h3>
|
||||||
{fetchData ?
|
{fetchData ?
|
||||||
<IconButton onClick={() => onFetchData(true)}>
|
<IconButton onClick={() => onFetchData(true)} title={L("general.reload")}>
|
||||||
<CachedIcon/>
|
<CachedIcon/>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
: <></>
|
: <></>
|
||||||
}
|
}
|
||||||
|
@ -50,6 +50,7 @@ export default function Dialog(props) {
|
|||||||
|
|
||||||
switch (input.type) {
|
switch (input.type) {
|
||||||
case 'label':
|
case 'label':
|
||||||
|
delete inputProps.value;
|
||||||
inputElements.push(<span {...inputProps}>{input.value}</span>);
|
inputElements.push(<span {...inputProps}>{input.value}</span>);
|
||||||
break;
|
break;
|
||||||
case 'text':
|
case 'text':
|
||||||
@ -57,11 +58,9 @@ export default function Dialog(props) {
|
|||||||
inputElements.push(<TextField
|
inputElements.push(<TextField
|
||||||
{...inputProps}
|
{...inputProps}
|
||||||
type={input.type}
|
type={input.type}
|
||||||
sx={{marginTop: 1}}
|
|
||||||
size={"small"} fullWidth={true}
|
size={"small"} fullWidth={true}
|
||||||
key={"input-" + input.name}
|
key={"input-" + input.name}
|
||||||
value={inputData[input.name] || ""}
|
value={inputData[input.name] || ""}
|
||||||
defaultValue={input.defaultValue || ""}
|
|
||||||
onChange={e => setInputData({ ...inputData, [input.name]: e.target.value })}
|
onChange={e => setInputData({ ...inputData, [input.name]: e.target.value })}
|
||||||
/>)
|
/>)
|
||||||
break;
|
break;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import React, {useState} from "react";
|
import React, {useState} from "react";
|
||||||
import {Box, MenuItem, Select, Pagination as MuiPagination} from "@mui/material";
|
import {Box, MenuItem, Select, Pagination as MuiPagination} from "@mui/material";
|
||||||
import {sprintf} from "sprintf-js";
|
import {sprintf} from "sprintf-js";
|
||||||
|
import {FormControl} from "@material-ui/core";
|
||||||
|
|
||||||
class Pagination {
|
class Pagination {
|
||||||
|
|
||||||
@ -57,6 +58,7 @@ class Pagination {
|
|||||||
options = options || [10, 25, 50, 100];
|
options = options || [10, 25, 50, 100];
|
||||||
|
|
||||||
return <Box display={"grid"} gridTemplateColumns={"75px auto"} className={"pagination-controls"}>
|
return <Box display={"grid"} gridTemplateColumns={"75px auto"} className={"pagination-controls"}>
|
||||||
|
<FormControl>
|
||||||
<Select
|
<Select
|
||||||
value={this.data.pageSize}
|
value={this.data.pageSize}
|
||||||
className={"pagination-page-size"}
|
className={"pagination-page-size"}
|
||||||
@ -66,6 +68,7 @@ class Pagination {
|
|||||||
>
|
>
|
||||||
{options.map(size => <MenuItem key={"size-" + size} value={size}>{size}</MenuItem>)}
|
{options.map(size => <MenuItem key={"size-" + size} value={size}>{size}</MenuItem>)}
|
||||||
</Select>
|
</Select>
|
||||||
|
</FormControl>
|
||||||
<MuiPagination
|
<MuiPagination
|
||||||
count={this.getPageCount()}
|
count={this.getPageCount()}
|
||||||
onChange={(_, page) => this.setPage(page)}
|
onChange={(_, page) => this.setPage(page)}
|
||||||
|
Loading…
Reference in New Issue
Block a user