Allow server admin to create rooms for other users and change user power levels
Change-Id: Ie96e9e0102454835536b6f42d247f9e714e28480
This commit is contained in:
parent
931fafc21d
commit
270d48607a
@ -4,7 +4,7 @@ import polyglotI18nProvider from "ra-i18n-polyglot";
|
|||||||
import authProvider from "./synapse/authProvider";
|
import authProvider from "./synapse/authProvider";
|
||||||
import dataProvider from "./synapse/dataProvider";
|
import dataProvider from "./synapse/dataProvider";
|
||||||
import { UserList, UserCreate, UserEdit } from "./components/users";
|
import { UserList, UserCreate, UserEdit } from "./components/users";
|
||||||
import { RoomList, RoomCreate, RoomShow } from "./components/rooms";
|
import { RoomList, RoomCreate, RoomShow, RoomEdit } from "./components/rooms";
|
||||||
import LoginPage from "./components/LoginPage";
|
import LoginPage from "./components/LoginPage";
|
||||||
import UserIcon from "@material-ui/icons/Group";
|
import UserIcon from "@material-ui/icons/Group";
|
||||||
import { ViewListIcon as RoomIcon } from "@material-ui/icons/ViewList";
|
import { ViewListIcon as RoomIcon } from "@material-ui/icons/ViewList";
|
||||||
@ -45,6 +45,7 @@ const App = () => (
|
|||||||
list={RoomList}
|
list={RoomList}
|
||||||
create={RoomCreate}
|
create={RoomCreate}
|
||||||
show={RoomShow}
|
show={RoomShow}
|
||||||
|
edit={RoomEdit}
|
||||||
icon={RoomIcon}
|
icon={RoomIcon}
|
||||||
/>
|
/>
|
||||||
<Resource name="connections" />
|
<Resource name="connections" />
|
||||||
|
@ -1,10 +1,14 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
|
import { Route, Link } from "react-router-dom";
|
||||||
import {
|
import {
|
||||||
AutocompleteArrayInput,
|
AutocompleteArrayInput,
|
||||||
|
AutocompleteInput,
|
||||||
BooleanInput,
|
BooleanInput,
|
||||||
BooleanField,
|
BooleanField,
|
||||||
|
Button,
|
||||||
Create,
|
Create,
|
||||||
|
Edit,
|
||||||
Datagrid,
|
Datagrid,
|
||||||
Filter,
|
Filter,
|
||||||
FormTab,
|
FormTab,
|
||||||
@ -12,24 +16,39 @@ import {
|
|||||||
Pagination,
|
Pagination,
|
||||||
ReferenceArrayInput,
|
ReferenceArrayInput,
|
||||||
ReferenceField,
|
ReferenceField,
|
||||||
|
ReferenceInput,
|
||||||
ReferenceManyField,
|
ReferenceManyField,
|
||||||
SelectField,
|
SelectField,
|
||||||
Show,
|
Show,
|
||||||
|
SimpleForm,
|
||||||
Tab,
|
Tab,
|
||||||
TabbedForm,
|
TabbedForm,
|
||||||
TabbedShowLayout,
|
TabbedShowLayout,
|
||||||
TextField,
|
TextField,
|
||||||
TextInput,
|
TextInput,
|
||||||
|
Toolbar,
|
||||||
|
useDataProvider,
|
||||||
|
useRefresh,
|
||||||
useTranslate,
|
useTranslate,
|
||||||
} from "react-admin";
|
} from "react-admin";
|
||||||
import get from "lodash/get";
|
import get from "lodash/get";
|
||||||
import { Tooltip, Typography, Chip } from "@material-ui/core";
|
import {
|
||||||
|
Tooltip,
|
||||||
|
Typography,
|
||||||
|
Chip,
|
||||||
|
Drawer,
|
||||||
|
styled,
|
||||||
|
withStyles,
|
||||||
|
Select,
|
||||||
|
MenuItem,
|
||||||
|
} from "@material-ui/core";
|
||||||
import HttpsIcon from "@material-ui/icons/Https";
|
import HttpsIcon from "@material-ui/icons/Https";
|
||||||
import NoEncryptionIcon from "@material-ui/icons/NoEncryption";
|
import NoEncryptionIcon from "@material-ui/icons/NoEncryption";
|
||||||
import PageviewIcon from "@material-ui/icons/Pageview";
|
import PageviewIcon from "@material-ui/icons/Pageview";
|
||||||
import UserIcon from "@material-ui/icons/Group";
|
import UserIcon from "@material-ui/icons/Group";
|
||||||
import ViewListIcon from "@material-ui/icons/ViewList";
|
import ViewListIcon from "@material-ui/icons/ViewList";
|
||||||
import VisibilityIcon from "@material-ui/icons/Visibility";
|
import VisibilityIcon from "@material-ui/icons/Visibility";
|
||||||
|
import ContentSave from "@material-ui/icons/Save";
|
||||||
|
|
||||||
const RoomPagination = props => (
|
const RoomPagination = props => (
|
||||||
<Pagination {...props} rowsPerPageOptions={[10, 25, 50, 100, 500, 1000]} />
|
<Pagination {...props} rowsPerPageOptions={[10, 25, 50, 100, 500, 1000]} />
|
||||||
@ -61,12 +80,13 @@ const EncryptionField = ({ source, record = {}, emptyText }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const validateDisplayName = fieldval =>
|
const validateDisplayName = fieldval => {
|
||||||
fieldval === undefined
|
return fieldval == null
|
||||||
? "synapseadmin.rooms.room_name_required"
|
? "synapseadmin.rooms.room_name_required"
|
||||||
: fieldval.length === 0
|
: fieldval.length === 0
|
||||||
? "synapseadmin.rooms.room_name_required"
|
? "synapseadmin.rooms.room_name_required"
|
||||||
: undefined;
|
: undefined;
|
||||||
|
};
|
||||||
|
|
||||||
function approximateAliasLength(alias, homeserver) {
|
function approximateAliasLength(alias, homeserver) {
|
||||||
/* TODO maybe handle punycode in homeserver name */
|
/* TODO maybe handle punycode in homeserver name */
|
||||||
@ -141,6 +161,16 @@ export const RoomCreate = props => (
|
|||||||
validate={validateAlias}
|
validate={validateAlias}
|
||||||
placeholder="#"
|
placeholder="#"
|
||||||
/>
|
/>
|
||||||
|
<ReferenceInput
|
||||||
|
reference="users"
|
||||||
|
source="owner"
|
||||||
|
filterToQuery={searchText => ({ user_id: searchText })}
|
||||||
|
>
|
||||||
|
<AutocompleteInput
|
||||||
|
optionText="displayname"
|
||||||
|
suggestionText="displayname"
|
||||||
|
/>
|
||||||
|
</ReferenceInput>
|
||||||
<BooleanInput source="public" label="synapseadmin.rooms.make_public" />
|
<BooleanInput source="public" label="synapseadmin.rooms.make_public" />
|
||||||
<BooleanInput
|
<BooleanInput
|
||||||
source="encrypt"
|
source="encrypt"
|
||||||
@ -176,6 +206,242 @@ const RoomTitle = ({ record }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Explicitely passing "to" prop
|
||||||
|
// Toolbar adds all kinds of unsupported props to its children :(
|
||||||
|
const StyledLink = styles => {
|
||||||
|
const Styled = styled(Link)(styles);
|
||||||
|
return ({ to, children }) => <Styled to={to}>{children}</Styled>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const RoomMemberEditToolbar = ({ backLink, translate, onSave, ...props }) => {
|
||||||
|
const SaveLink = StyledLink({
|
||||||
|
textDecoration: "none",
|
||||||
|
});
|
||||||
|
const CancelLink = StyledLink({
|
||||||
|
textDecoration: "none",
|
||||||
|
marginLeft: "1em",
|
||||||
|
});
|
||||||
|
const SaveIcon = styled(ContentSave)({
|
||||||
|
width: "1rem",
|
||||||
|
marginRight: "0.25em",
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Toolbar {...props}>
|
||||||
|
<SaveLink to={backLink}>
|
||||||
|
<Button onClick={onSave} variant="contained">
|
||||||
|
<React.Fragment>
|
||||||
|
<SaveIcon />
|
||||||
|
{translate("ra.action.save")}
|
||||||
|
</React.Fragment>
|
||||||
|
</Button>
|
||||||
|
</SaveLink>
|
||||||
|
<CancelLink to={backLink}>
|
||||||
|
<Button>
|
||||||
|
<React.Fragment>{translate("ra.action.cancel")}</React.Fragment>
|
||||||
|
</Button>
|
||||||
|
</CancelLink>
|
||||||
|
</Toolbar>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const RoomMemberIdField = ({ memberId, data = {} }) => {
|
||||||
|
const value = get(data[memberId], "id");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Typography component="span" variant="body2">
|
||||||
|
{value}
|
||||||
|
</Typography>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const RoomMemberRoleInput = ({ memberId, data = {}, translate, onChange }) => {
|
||||||
|
const roleValue = get(data[memberId], "role");
|
||||||
|
const [role, setRole] = React.useState(roleValue);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
onChange(roleValue);
|
||||||
|
}, [onChange, roleValue]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<Select
|
||||||
|
labelId="demo-simple-select-label"
|
||||||
|
id="demo-simple-select"
|
||||||
|
value={role}
|
||||||
|
onChange={event => {
|
||||||
|
setRole(event.target.value);
|
||||||
|
onChange(event.target.value);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<MenuItem value={"user"}>
|
||||||
|
{translate("resources.users.roles.user")}
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem value={"mod"}>
|
||||||
|
{translate("resources.users.roles.mod")}
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem value={"admin"}>
|
||||||
|
{translate("resources.users.roles.admin")}
|
||||||
|
</MenuItem>
|
||||||
|
</Select>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const RoomMemberEdit = ({ backLink, memberId, ...props }) => {
|
||||||
|
const translate = useTranslate();
|
||||||
|
const refresh = useRefresh();
|
||||||
|
const dataProvider = useDataProvider();
|
||||||
|
|
||||||
|
const [role, setRole] = React.useState();
|
||||||
|
|
||||||
|
const { id } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Edit title=" " {...props}>
|
||||||
|
<SimpleForm
|
||||||
|
toolbar={
|
||||||
|
<RoomMemberEditToolbar
|
||||||
|
backLink={backLink}
|
||||||
|
translate={translate}
|
||||||
|
onSave={() => {
|
||||||
|
dataProvider
|
||||||
|
.update("rooms", {
|
||||||
|
data: {
|
||||||
|
id,
|
||||||
|
member_roles: [{ member_id: memberId, role }],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
refresh();
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<ReferenceManyField
|
||||||
|
reference="room_members"
|
||||||
|
target="room_id"
|
||||||
|
label="resources.users.fields.id"
|
||||||
|
>
|
||||||
|
<RoomMemberIdField memberId={memberId} />
|
||||||
|
</ReferenceManyField>
|
||||||
|
<ReferenceManyField
|
||||||
|
reference="room_members"
|
||||||
|
target="room_id"
|
||||||
|
label="resources.users.fields.role"
|
||||||
|
>
|
||||||
|
<RoomMemberRoleInput
|
||||||
|
memberId={memberId}
|
||||||
|
translate={translate}
|
||||||
|
onChange={setRole}
|
||||||
|
/>
|
||||||
|
</ReferenceManyField>
|
||||||
|
</SimpleForm>
|
||||||
|
</Edit>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const drawerStyles = {
|
||||||
|
paper: {
|
||||||
|
width: 300,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const StyledDrawer = withStyles(drawerStyles)(({ classes, ...props }) => (
|
||||||
|
<Drawer {...props} classes={classes} />
|
||||||
|
));
|
||||||
|
|
||||||
|
export const RoomEdit = props => {
|
||||||
|
const translate = useTranslate();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<Edit {...props} title={<RoomTitle />}>
|
||||||
|
<TabbedForm>
|
||||||
|
<FormTab label="synapseadmin.rooms.tabs.members" icon={<UserIcon />}>
|
||||||
|
<ReferenceArrayInput
|
||||||
|
reference="users"
|
||||||
|
source="invitees"
|
||||||
|
filterToQuery={searchText => ({ user_id: searchText })}
|
||||||
|
>
|
||||||
|
<AutocompleteArrayInput
|
||||||
|
optionText="displayname"
|
||||||
|
suggestionText="displayname"
|
||||||
|
/>
|
||||||
|
</ReferenceArrayInput>
|
||||||
|
|
||||||
|
<ReferenceManyField
|
||||||
|
reference="room_members"
|
||||||
|
target="room_id"
|
||||||
|
addLabel={false}
|
||||||
|
>
|
||||||
|
<Datagrid
|
||||||
|
style={{ width: "100%" }}
|
||||||
|
rowClick={(id, basePath, record) =>
|
||||||
|
`/rooms/${encodeURIComponent(
|
||||||
|
record.parentId
|
||||||
|
)}/${encodeURIComponent(id)}`
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<TextField
|
||||||
|
source="id"
|
||||||
|
sortable={false}
|
||||||
|
label="resources.users.fields.id"
|
||||||
|
/>
|
||||||
|
<ReferenceField
|
||||||
|
label="resources.users.fields.displayname"
|
||||||
|
source="id"
|
||||||
|
reference="users"
|
||||||
|
sortable={false}
|
||||||
|
link=""
|
||||||
|
>
|
||||||
|
<TextField source="displayname" sortable={false} />
|
||||||
|
</ReferenceField>
|
||||||
|
<SelectField
|
||||||
|
source="role"
|
||||||
|
label="resources.users.fields.role"
|
||||||
|
choices={[
|
||||||
|
{
|
||||||
|
id: "user",
|
||||||
|
name: translate("resources.users.roles.user"),
|
||||||
|
},
|
||||||
|
{ id: "mod", name: translate("resources.users.roles.mod") },
|
||||||
|
{
|
||||||
|
id: "admin",
|
||||||
|
name: translate("resources.users.roles.admin"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</Datagrid>
|
||||||
|
</ReferenceManyField>
|
||||||
|
</FormTab>
|
||||||
|
</TabbedForm>
|
||||||
|
</Edit>
|
||||||
|
<Route path="/rooms/:roomId/:memberId">
|
||||||
|
{({ match }) => {
|
||||||
|
const isMatch = !!match && !!match.params;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StyledDrawer open={isMatch} anchor="right">
|
||||||
|
{isMatch ? (
|
||||||
|
<RoomMemberEdit
|
||||||
|
{...props}
|
||||||
|
memberId={
|
||||||
|
isMatch ? decodeURIComponent(match.params.memberId) : null
|
||||||
|
}
|
||||||
|
backLink={`/rooms/${match.params.roomId}`}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div />
|
||||||
|
)}
|
||||||
|
</StyledDrawer>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</Route>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const RoomShow = props => {
|
export const RoomShow = props => {
|
||||||
const translate = useTranslate();
|
const translate = useTranslate();
|
||||||
return (
|
return (
|
||||||
@ -227,6 +493,18 @@ export const RoomShow = props => {
|
|||||||
>
|
>
|
||||||
<TextField source="displayname" sortable={false} />
|
<TextField source="displayname" sortable={false} />
|
||||||
</ReferenceField>
|
</ReferenceField>
|
||||||
|
<SelectField
|
||||||
|
source="role"
|
||||||
|
label="resources.users.fields.role"
|
||||||
|
choices={[
|
||||||
|
{ id: "user", name: translate("resources.users.roles.user") },
|
||||||
|
{ id: "mod", name: translate("resources.users.roles.mod") },
|
||||||
|
{
|
||||||
|
id: "admin",
|
||||||
|
name: translate("resources.users.roles.admin"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
</Datagrid>
|
</Datagrid>
|
||||||
</ReferenceManyField>
|
</ReferenceManyField>
|
||||||
</Tab>
|
</Tab>
|
||||||
|
@ -70,6 +70,7 @@ export default {
|
|||||||
display_name: "Gerätename",
|
display_name: "Gerätename",
|
||||||
last_seen_ts: "Zeitstempel",
|
last_seen_ts: "Zeitstempel",
|
||||||
last_seen_ip: "IP-Adresse",
|
last_seen_ip: "IP-Adresse",
|
||||||
|
role: "Rolle",
|
||||||
},
|
},
|
||||||
type: {
|
type: {
|
||||||
default: "Standard",
|
default: "Standard",
|
||||||
@ -83,6 +84,11 @@ export default {
|
|||||||
action: {
|
action: {
|
||||||
erase: "Lösche Benutzerdaten",
|
erase: "Lösche Benutzerdaten",
|
||||||
},
|
},
|
||||||
|
roles: {
|
||||||
|
user: "Nutzer",
|
||||||
|
mod: "Moderator",
|
||||||
|
admin: "Administrator",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
rooms: {
|
rooms: {
|
||||||
name: "Raum |||| Räume",
|
name: "Raum |||| Räume",
|
||||||
|
@ -69,6 +69,7 @@ export default {
|
|||||||
display_name: "Device name",
|
display_name: "Device name",
|
||||||
last_seen_ts: "Timestamp",
|
last_seen_ts: "Timestamp",
|
||||||
last_seen_ip: "IP address",
|
last_seen_ip: "IP address",
|
||||||
|
role: "Role",
|
||||||
},
|
},
|
||||||
type: {
|
type: {
|
||||||
default: "Standard",
|
default: "Standard",
|
||||||
@ -82,6 +83,11 @@ export default {
|
|||||||
action: {
|
action: {
|
||||||
erase: "Erase user data",
|
erase: "Erase user data",
|
||||||
},
|
},
|
||||||
|
roles: {
|
||||||
|
user: "User",
|
||||||
|
mod: "Moderator",
|
||||||
|
admin: "Administrator",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
rooms: {
|
rooms: {
|
||||||
name: "Room |||| Rooms",
|
name: "Room |||| Rooms",
|
||||||
|
@ -25,6 +25,16 @@ const mxcUrlToHttp = mxcUrl => {
|
|||||||
return `${homeserver}/_matrix/media/r0/thumbnail/${serverName}/${mediaId}?width=24&height=24&method=scale`;
|
return `${homeserver}/_matrix/media/r0/thumbnail/${serverName}/${mediaId}?width=24&height=24&method=scale`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const powerLevelToRole = powerLevel =>
|
||||||
|
powerLevel < 100 ? (powerLevel < 50 ? "user" : "mod") : "admin";
|
||||||
|
|
||||||
|
const POWER_LEVELS = {
|
||||||
|
admin: 100,
|
||||||
|
mod: 50,
|
||||||
|
user: 0,
|
||||||
|
};
|
||||||
|
const roleToPowerLevel = role => POWER_LEVELS[role] || 0;
|
||||||
|
|
||||||
const resourceMap = {
|
const resourceMap = {
|
||||||
users: {
|
users: {
|
||||||
path: "/_synapse/admin/v2/users",
|
path: "/_synapse/admin/v2/users",
|
||||||
@ -66,8 +76,9 @@ const resourceMap = {
|
|||||||
data: "rooms",
|
data: "rooms",
|
||||||
total: json => json.total_rooms,
|
total: json => json.total_rooms,
|
||||||
create: data => ({
|
create: data => ({
|
||||||
endpoint: "/_matrix/client/r0/createRoom",
|
endpoint: "/_synapse/admin/v1/rooms",
|
||||||
body: {
|
body: {
|
||||||
|
owner: data.owner,
|
||||||
name: data.name,
|
name: data.name,
|
||||||
room_alias_name: data.canonical_alias,
|
room_alias_name: data.canonical_alias,
|
||||||
visibility: data.public ? "public" : "private",
|
visibility: data.public ? "public" : "private",
|
||||||
@ -89,6 +100,20 @@ const resourceMap = {
|
|||||||
},
|
},
|
||||||
method: "POST",
|
method: "POST",
|
||||||
}),
|
}),
|
||||||
|
delete: params => ({
|
||||||
|
endpoint: `/_synapse/admin/v1/rooms/${params.id}/delete`,
|
||||||
|
body: { erase: true },
|
||||||
|
method: "POST",
|
||||||
|
}),
|
||||||
|
transformBeforeUpdate: data => {
|
||||||
|
return {
|
||||||
|
...data,
|
||||||
|
member_roles: (data.member_roles || []).map(member => ({
|
||||||
|
member_id: member.member_id,
|
||||||
|
power_level: roleToPowerLevel(member.role),
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
},
|
||||||
},
|
},
|
||||||
devices: {
|
devices: {
|
||||||
map: d => ({
|
map: d => ({
|
||||||
@ -113,10 +138,11 @@ const resourceMap = {
|
|||||||
},
|
},
|
||||||
room_members: {
|
room_members: {
|
||||||
map: m => ({
|
map: m => ({
|
||||||
id: m,
|
role: powerLevelToRole(m.power_level),
|
||||||
|
id: m.user_id,
|
||||||
}),
|
}),
|
||||||
reference: id => ({
|
reference: id => ({
|
||||||
endpoint: `/_synapse/admin/v1/rooms/${id}/members`,
|
endpoint: `/_synapse/admin/v1/rooms/${id}/power_levels`,
|
||||||
}),
|
}),
|
||||||
data: "members",
|
data: "members",
|
||||||
},
|
},
|
||||||
@ -183,7 +209,7 @@ const dataProvider = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
getOne: (resource, params) => {
|
getOne: (resource, params) => {
|
||||||
console.log("getOne " + resource);
|
console.log("getOne " + resource, params);
|
||||||
const homeserver = localStorage.getItem("base_url");
|
const homeserver = localStorage.getItem("base_url");
|
||||||
if (!homeserver || !(resource in resourceMap)) return Promise.reject();
|
if (!homeserver || !(resource in resourceMap)) return Promise.reject();
|
||||||
|
|
||||||
@ -222,7 +248,10 @@ const dataProvider = {
|
|||||||
const endpoint_url = homeserver + ref.endpoint;
|
const endpoint_url = homeserver + ref.endpoint;
|
||||||
|
|
||||||
return jsonClient(endpoint_url).then(({ headers, json }) => ({
|
return jsonClient(endpoint_url).then(({ headers, json }) => ({
|
||||||
data: json[res.data].map(res.map),
|
data: json[res.data].map(res.map).map(element => ({
|
||||||
|
...element,
|
||||||
|
parentId: params.id,
|
||||||
|
})),
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -233,10 +262,13 @@ const dataProvider = {
|
|||||||
|
|
||||||
const res = resourceMap[resource];
|
const res = resourceMap[resource];
|
||||||
|
|
||||||
|
const transform = res.transformBeforeUpdate || (x => x);
|
||||||
|
const data = transform(params.data);
|
||||||
|
|
||||||
const endpoint_url = homeserver + res.path;
|
const endpoint_url = homeserver + res.path;
|
||||||
return jsonClient(`${endpoint_url}/${params.data.id}`, {
|
return jsonClient(`${endpoint_url}/${params.data.id}`, {
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
body: JSON.stringify(params.data, filterNullValues),
|
body: JSON.stringify(data, filterNullValues),
|
||||||
}).then(({ json }) => ({
|
}).then(({ json }) => ({
|
||||||
data: res.map(json),
|
data: res.map(json),
|
||||||
}));
|
}));
|
||||||
|
Loading…
Reference in New Issue
Block a user