Compare commits
22 Commits
0.2.0-amp
...
AMP/2020.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ab649fbf70 | ||
|
|
c41b8ab846 | ||
|
|
7f16f784f9 | ||
|
|
1002b6464a | ||
|
|
50b770a312 | ||
|
|
2d0ce50444 | ||
|
|
1fb89c9e58 | ||
|
|
8a4c0fe0fe | ||
|
|
dd022eab04 | ||
|
|
880223e5de | ||
|
|
76fdc80e3e | ||
|
|
375649756f | ||
|
|
662735a91f | ||
|
|
437fd70d6d | ||
|
|
1e6e526e3c | ||
|
|
0823976edd | ||
|
|
d812cff5fc | ||
|
|
a39033e25b | ||
|
|
d3cd2e9e33 | ||
|
|
24abcd4e4a | ||
|
|
c1c32e3268 | ||
|
|
ca15435625 |
@@ -4,6 +4,7 @@
|
||||
"description": "Admin GUI for the Matrix.org server Synapse",
|
||||
"author": "Awesome Technologies Innovationslabor GmbH",
|
||||
"license": "Apache-2.0",
|
||||
"homepage": ".",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Awesome-Technologies/synapse-admin"
|
||||
|
||||
11
src/App.js
11
src/App.js
@@ -4,7 +4,7 @@ import polyglotI18nProvider from "ra-i18n-polyglot";
|
||||
import authProvider from "./synapse/authProvider";
|
||||
import dataProvider from "./synapse/dataProvider";
|
||||
import { UserList, UserCreate, UserEdit } from "./components/users";
|
||||
import { RoomList } from "./components/rooms";
|
||||
import { RoomList, RoomCreate, RoomShow } from "./components/rooms";
|
||||
import LoginPage from "./components/LoginPage";
|
||||
import UserIcon from "@material-ui/icons/Group";
|
||||
import { ViewListIcon as RoomIcon } from "@material-ui/icons/ViewList";
|
||||
@@ -40,8 +40,15 @@ const App = () => (
|
||||
edit={UserEdit}
|
||||
icon={UserIcon}
|
||||
/>
|
||||
<Resource name="rooms" list={RoomList} icon={RoomIcon} />
|
||||
<Resource
|
||||
name="rooms"
|
||||
list={RoomList}
|
||||
create={RoomCreate}
|
||||
show={RoomShow}
|
||||
icon={RoomIcon}
|
||||
/>
|
||||
<Resource name="connections" />
|
||||
<Resource name="servernotices" />
|
||||
</Admin>
|
||||
);
|
||||
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
import React, { useState } from "react";
|
||||
import {
|
||||
fetchUtils,
|
||||
FormDataConsumer,
|
||||
Notification,
|
||||
useLogin,
|
||||
useNotify,
|
||||
useLocale,
|
||||
useSetLocale,
|
||||
useTranslate,
|
||||
PasswordInput,
|
||||
TextInput,
|
||||
} from "react-admin";
|
||||
import { Field, Form } from "react-final-form";
|
||||
import { Form, useForm } from "react-final-form";
|
||||
import {
|
||||
Avatar,
|
||||
Button,
|
||||
@@ -34,7 +38,7 @@ const useStyles = makeStyles(theme => ({
|
||||
backgroundSize: "cover",
|
||||
},
|
||||
card: {
|
||||
minWidth: 300,
|
||||
minWidth: "30em",
|
||||
marginTop: "6em",
|
||||
},
|
||||
avatar: {
|
||||
@@ -70,7 +74,7 @@ const LoginPage = ({ theme }) => {
|
||||
var locale = useLocale();
|
||||
const setLocale = useSetLocale();
|
||||
const translate = useTranslate();
|
||||
const homeserver = localStorage.getItem("base_url");
|
||||
const base_url = localStorage.getItem("base_url");
|
||||
|
||||
const renderInput = ({
|
||||
meta: { touched, error } = {},
|
||||
@@ -88,15 +92,23 @@ const LoginPage = ({ theme }) => {
|
||||
|
||||
const validate = values => {
|
||||
const errors = {};
|
||||
if (!values.homeserver) {
|
||||
errors.homeserver = translate("ra.validation.required");
|
||||
}
|
||||
if (!values.username) {
|
||||
errors.username = translate("ra.validation.required");
|
||||
}
|
||||
if (!values.password) {
|
||||
errors.password = translate("ra.validation.required");
|
||||
}
|
||||
if (!values.base_url) {
|
||||
errors.base_url = translate("ra.validation.required");
|
||||
} else {
|
||||
if (!values.base_url.match(/^(http|https):\/\//)) {
|
||||
errors.base_url = translate("synapseadmin.auth.protocol_error");
|
||||
} else if (
|
||||
!values.base_url.match(/^(http|https):\/\/[a-zA-Z0-9\-.]+(:\d{1,5})?$/)
|
||||
) {
|
||||
errors.base_url = translate("synapseadmin.auth.url_error");
|
||||
}
|
||||
}
|
||||
return errors;
|
||||
};
|
||||
|
||||
@@ -115,9 +127,75 @@ const LoginPage = ({ theme }) => {
|
||||
});
|
||||
};
|
||||
|
||||
const extractHomeServer = username => {
|
||||
const usernameRegex = /@[a-zA-Z0-9._=\-/]+:([a-zA-Z0-9\-.]+\.[a-zA-Z]+)/;
|
||||
if (!username) return null;
|
||||
const res = username.match(usernameRegex);
|
||||
if (res) return res[1];
|
||||
return null;
|
||||
};
|
||||
|
||||
const UserData = ({ formData }) => {
|
||||
const form = useForm();
|
||||
|
||||
const handleUsernameChange = _ => {
|
||||
if (formData.base_url) return;
|
||||
// check if username is a full qualified userId then set base_url accordially
|
||||
const home_server = extractHomeServer(formData.username);
|
||||
const wellKnownUrl = `https://${home_server}/.well-known/matrix/client`;
|
||||
if (home_server) {
|
||||
// fetch .well-known entry to get base_url
|
||||
fetchUtils
|
||||
.fetchJson(wellKnownUrl, { method: "GET" })
|
||||
.then(({ json }) => {
|
||||
form.change("base_url", json["m.homeserver"].base_url);
|
||||
})
|
||||
.catch(_ => {
|
||||
// if there is no .well-known entry, try the home server name
|
||||
form.change("base_url", `https://${home_server}`);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={classes.input}>
|
||||
<TextInput
|
||||
autoFocus
|
||||
name="username"
|
||||
component={renderInput}
|
||||
label={translate("ra.auth.username")}
|
||||
disabled={loading}
|
||||
onBlur={handleUsernameChange}
|
||||
fullWidth
|
||||
/>
|
||||
</div>
|
||||
<div className={classes.input}>
|
||||
<PasswordInput
|
||||
name="password"
|
||||
component={renderInput}
|
||||
label={translate("ra.auth.password")}
|
||||
type="password"
|
||||
disabled={loading}
|
||||
fullWidth
|
||||
/>
|
||||
</div>
|
||||
<div className={classes.input}>
|
||||
<TextInput
|
||||
name="base_url"
|
||||
component={renderInput}
|
||||
label={translate("synapseadmin.auth.base_url")}
|
||||
disabled={loading}
|
||||
fullWidth
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Form
|
||||
initialValues={{ homeserver: homeserver }}
|
||||
initialValues={{ base_url: base_url }}
|
||||
onSubmit={handleSubmit}
|
||||
validate={validate}
|
||||
render={({ handleSubmit }) => (
|
||||
@@ -146,32 +224,9 @@ const LoginPage = ({ theme }) => {
|
||||
<MenuItem value="en">English</MenuItem>
|
||||
</Select>
|
||||
</div>
|
||||
<div className={classes.input}>
|
||||
<Field
|
||||
autoFocus
|
||||
name="homeserver"
|
||||
component={renderInput}
|
||||
label={translate("synapseadmin.auth.homeserver")}
|
||||
disabled={loading}
|
||||
/>
|
||||
</div>
|
||||
<div className={classes.input}>
|
||||
<Field
|
||||
name="username"
|
||||
component={renderInput}
|
||||
label={translate("ra.auth.username")}
|
||||
disabled={loading}
|
||||
/>
|
||||
</div>
|
||||
<div className={classes.input}>
|
||||
<Field
|
||||
name="password"
|
||||
component={renderInput}
|
||||
label={translate("ra.auth.password")}
|
||||
type="password"
|
||||
disabled={loading}
|
||||
/>
|
||||
</div>
|
||||
<FormDataConsumer>
|
||||
{formDataProps => <UserData {...formDataProps} />}
|
||||
</FormDataConsumer>
|
||||
</div>
|
||||
<CardActions className={classes.actions}>
|
||||
<Button
|
||||
|
||||
100
src/components/ServerNotices.js
Normal file
100
src/components/ServerNotices.js
Normal file
@@ -0,0 +1,100 @@
|
||||
import React, { Fragment, useState } from "react";
|
||||
import {
|
||||
Button,
|
||||
SaveButton,
|
||||
SimpleForm,
|
||||
TextInput,
|
||||
Toolbar,
|
||||
required,
|
||||
useCreate,
|
||||
useNotify,
|
||||
useTranslate,
|
||||
} from "react-admin";
|
||||
import MessageIcon from "@material-ui/icons/Message";
|
||||
import IconCancel from "@material-ui/icons/Cancel";
|
||||
import Dialog from "@material-ui/core/Dialog";
|
||||
import DialogContent from "@material-ui/core/DialogContent";
|
||||
import DialogContentText from "@material-ui/core/DialogContentText";
|
||||
import DialogTitle from "@material-ui/core/DialogTitle";
|
||||
|
||||
const ServerNoticeDialog = ({ open, loading, onClose, onSend }) => {
|
||||
const translate = useTranslate();
|
||||
|
||||
const ServerNoticeToolbar = props => (
|
||||
<Toolbar {...props}>
|
||||
<SaveButton label="resources.servernotices.action.send" />
|
||||
<Button label="ra.action.cancel" onClick={onClose}>
|
||||
<IconCancel />
|
||||
</Button>
|
||||
</Toolbar>
|
||||
);
|
||||
|
||||
return (
|
||||
<Dialog open={open} onClose={onClose} loading={loading}>
|
||||
<DialogTitle>
|
||||
{translate("resources.servernotices.action.send")}
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText>
|
||||
{translate("resources.servernotices.helper.send")}
|
||||
</DialogContentText>
|
||||
<SimpleForm
|
||||
toolbar={<ServerNoticeToolbar />}
|
||||
submitOnEnter={false}
|
||||
redirect={false}
|
||||
save={onSend}
|
||||
>
|
||||
<TextInput
|
||||
source="body"
|
||||
label="resources.servernotices.fields.body"
|
||||
fullWidth
|
||||
multiline
|
||||
rows="4"
|
||||
resettable
|
||||
validate={required()}
|
||||
/>
|
||||
</SimpleForm>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export const ServerNoticeButton = ({ record }) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
const notify = useNotify();
|
||||
const [create, { loading }] = useCreate("servernotices");
|
||||
|
||||
const handleDialogOpen = () => setOpen(true);
|
||||
const handleDialogClose = () => setOpen(false);
|
||||
|
||||
const handleSend = values => {
|
||||
create(
|
||||
{ payload: { data: { id: record.id, ...values } } },
|
||||
{
|
||||
onSuccess: () => {
|
||||
notify("resources.servernotices.action.send_success");
|
||||
handleDialogClose();
|
||||
},
|
||||
onFailure: () =>
|
||||
notify("resources.servernotices.action.send_failure", "error"),
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<Button
|
||||
label="resources.servernotices.send"
|
||||
onClick={handleDialogOpen}
|
||||
disabled={loading}
|
||||
>
|
||||
<MessageIcon />
|
||||
</Button>
|
||||
<ServerNoticeDialog
|
||||
open={open}
|
||||
onClose={handleDialogClose}
|
||||
onSend={handleSend}
|
||||
/>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
@@ -1,5 +1,24 @@
|
||||
import React from "react";
|
||||
import { Datagrid, List, TextField, Pagination } from "react-admin";
|
||||
import {
|
||||
AutocompleteArrayInput,
|
||||
BooleanInput,
|
||||
Create,
|
||||
Datagrid,
|
||||
FormTab,
|
||||
List,
|
||||
Pagination,
|
||||
ReferenceArrayField,
|
||||
ReferenceArrayInput,
|
||||
Show,
|
||||
Tab,
|
||||
TabbedForm,
|
||||
TabbedShowLayout,
|
||||
TextField,
|
||||
TextInput,
|
||||
useTranslate,
|
||||
} from "react-admin";
|
||||
import ViewListIcon from "@material-ui/icons/ViewList";
|
||||
import UserIcon from "@material-ui/icons/Group";
|
||||
|
||||
const RoomPagination = props => (
|
||||
<Pagination {...props} rowsPerPageOptions={[10, 25, 50, 100, 500, 1000]} />
|
||||
@@ -7,7 +26,7 @@ const RoomPagination = props => (
|
||||
|
||||
export const RoomList = props => (
|
||||
<List {...props} pagination={<RoomPagination />}>
|
||||
<Datagrid>
|
||||
<Datagrid rowClick="show">
|
||||
<TextField source="room_id" />
|
||||
<TextField source="name" />
|
||||
<TextField source="canonical_alias" />
|
||||
@@ -15,3 +34,134 @@ export const RoomList = props => (
|
||||
</Datagrid>
|
||||
</List>
|
||||
);
|
||||
|
||||
const validateDisplayName = fieldval =>
|
||||
fieldval === undefined
|
||||
? "synapseadmin.rooms.room_name_required"
|
||||
: fieldval.length === 0
|
||||
? "synapseadmin.rooms.room_name_required"
|
||||
: undefined;
|
||||
|
||||
function approximateAliasLength(alias, homeserver) {
|
||||
/* TODO maybe handle punycode in homeserver name */
|
||||
|
||||
var te;
|
||||
|
||||
// Support for TextEncoder is quite widespread, but the polyfill is
|
||||
// pretty large; We will only underestimate the size with the regular
|
||||
// length attribute of String, so we never prevent the user from using
|
||||
// an alias that is short enough for the server, but too long for our
|
||||
// heuristic.
|
||||
try {
|
||||
te = new TextEncoder();
|
||||
} catch (err) {
|
||||
if (err instanceof ReferenceError) {
|
||||
te = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
const aliasLength = te === undefined ? alias.length : te.encode(alias).length;
|
||||
|
||||
return "#".length + aliasLength + ":".length + homeserver.length;
|
||||
}
|
||||
|
||||
const validateAlias = fieldval => {
|
||||
if (fieldval === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
const homeserver = localStorage.getItem("home_server");
|
||||
|
||||
if (approximateAliasLength(fieldval, homeserver) > 255) {
|
||||
return "synapseadmin.rooms.alias_too_long";
|
||||
}
|
||||
};
|
||||
|
||||
const removeLeadingWhitespace = fieldVal =>
|
||||
fieldVal === undefined ? undefined : fieldVal.trimStart();
|
||||
const replaceAllWhitespace = fieldVal =>
|
||||
fieldVal === undefined ? undefined : fieldVal.replace(/\s/, "_");
|
||||
const removeLeadingSigil = fieldVal =>
|
||||
fieldVal === undefined
|
||||
? undefined
|
||||
: fieldVal.startsWith("#")
|
||||
? fieldVal.substr(1)
|
||||
: fieldVal;
|
||||
|
||||
const validateHasAliasIfPublic = formdata => {
|
||||
let errors = {};
|
||||
if (formdata.public) {
|
||||
if (
|
||||
formdata.canonical_alias === undefined ||
|
||||
formdata.canonical_alias.trim().length === 0
|
||||
) {
|
||||
errors.canonical_alias = "synapseadmin.rooms.alias_required_if_public";
|
||||
}
|
||||
}
|
||||
return errors;
|
||||
};
|
||||
|
||||
export const RoomCreate = props => (
|
||||
<Create {...props}>
|
||||
<TabbedForm validate={validateHasAliasIfPublic}>
|
||||
<FormTab label="synapseadmin.rooms.details" icon={<ViewListIcon />}>
|
||||
<TextInput
|
||||
source="name"
|
||||
parse={removeLeadingWhitespace}
|
||||
validate={validateDisplayName}
|
||||
/>
|
||||
<TextInput
|
||||
source="canonical_alias"
|
||||
parse={fv => replaceAllWhitespace(removeLeadingSigil(fv))}
|
||||
validate={validateAlias}
|
||||
placeholder="#"
|
||||
/>
|
||||
<BooleanInput source="public" label="synapseadmin.rooms.make_public" />
|
||||
</FormTab>
|
||||
<FormTab
|
||||
label="resources.rooms.fields.invite_members"
|
||||
icon={<UserIcon />}
|
||||
>
|
||||
<ReferenceArrayInput
|
||||
reference="users"
|
||||
source="invitees"
|
||||
filterToQuery={searchText => ({ user_id: searchText })}
|
||||
>
|
||||
<AutocompleteArrayInput
|
||||
optionText="displayname"
|
||||
suggestionText="displayname"
|
||||
/>
|
||||
</ReferenceArrayInput>
|
||||
</FormTab>
|
||||
</TabbedForm>
|
||||
</Create>
|
||||
);
|
||||
|
||||
const RoomTitle = ({ record }) => {
|
||||
const translate = useTranslate();
|
||||
return (
|
||||
<span>
|
||||
{translate("resources.rooms.name", 1)} {record ? `"${record.name}"` : ""}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
export const RoomShow = props => (
|
||||
<Show {...props} title={<RoomTitle />}>
|
||||
<TabbedShowLayout>
|
||||
<Tab label="synapseadmin.rooms.details" icon={<ViewListIcon />}>
|
||||
<TextField source="id" disabled />
|
||||
<TextField source="name" />
|
||||
<TextField source="canonical_alias" />
|
||||
<TextField source="join_rules" />
|
||||
<TextField source="guest_access" />
|
||||
</Tab>
|
||||
<Tab label="resources.rooms.fields.joined_members" icon={<UserIcon />}>
|
||||
<ReferenceArrayField reference="users" source="members">
|
||||
<Datagrid>
|
||||
<TextField source="id" />
|
||||
<TextField source="displayname" />
|
||||
</Datagrid>
|
||||
</ReferenceArrayField>
|
||||
</Tab>
|
||||
</TabbedShowLayout>
|
||||
</Show>
|
||||
);
|
||||
|
||||
@@ -32,6 +32,7 @@ import {
|
||||
Pagination,
|
||||
} from "react-admin";
|
||||
import SaveQrButton from "./SaveQrButton";
|
||||
import { ServerNoticeButton } from "./ServerNotices";
|
||||
|
||||
const UserPagination = props => (
|
||||
<Pagination {...props} rowsPerPageOptions={[10, 25, 50, 100, 500, 1000]} />
|
||||
@@ -39,7 +40,7 @@ const UserPagination = props => (
|
||||
|
||||
const UserFilter = props => (
|
||||
<Filter {...props}>
|
||||
<SearchInput source="q" alwaysOn />
|
||||
<SearchInput source="user_id" alwaysOn />
|
||||
<BooleanInput source="guests" alwaysOn />
|
||||
<BooleanInput
|
||||
label="resources.users.fields.show_deactivated"
|
||||
@@ -71,24 +72,9 @@ export const UserList = props => (
|
||||
pagination={<UserPagination />}
|
||||
>
|
||||
<Datagrid rowClick="edit">
|
||||
<ReferenceField
|
||||
source="Avatar"
|
||||
reference="users"
|
||||
link={false}
|
||||
sortable={false}
|
||||
>
|
||||
<ImageField source="avatar_url" title="displayname" />
|
||||
</ReferenceField>
|
||||
<TextField source="id" />
|
||||
{/* Hack since the users endpoint does not give displaynames in the list*/}
|
||||
<ReferenceField
|
||||
source="name"
|
||||
reference="users"
|
||||
link={false}
|
||||
sortable={false}
|
||||
>
|
||||
<TextField source="displayname" />
|
||||
</ReferenceField>
|
||||
<ImageField source="avatar_url" title="displayname" />
|
||||
<TextField source="id" sortable={false} />
|
||||
<TextField source="displayname" />
|
||||
<BooleanField source="is_guest" sortable={false} />
|
||||
<BooleanField source="admin" sortable={false} />
|
||||
<BooleanField source="deactivated" sortable={false} />
|
||||
@@ -190,6 +176,7 @@ const UserEditToolbar = props => {
|
||||
label="resources.users.action.erase"
|
||||
title={translate("resources.users.helper.erase")}
|
||||
/>
|
||||
<ServerNoticeButton />
|
||||
</Toolbar>
|
||||
);
|
||||
};
|
||||
@@ -217,8 +204,17 @@ export const UserCreate = props => (
|
||||
</Create>
|
||||
);
|
||||
|
||||
const UserTitle = ({ record }) => {
|
||||
const translate = useTranslate();
|
||||
return (
|
||||
<span>
|
||||
{translate("resources.users.name")}{" "}
|
||||
{record ? `"${record.displayname}"` : ""}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
export const UserEdit = props => (
|
||||
<Edit {...props}>
|
||||
<Edit {...props} title={<UserTitle />}>
|
||||
<TabbedForm toolbar={<UserEditToolbar />}>
|
||||
<FormTab label="resources.users.name" icon={<PersonPinIcon />}>
|
||||
<TextInput source="id" disabled />
|
||||
|
||||
@@ -4,8 +4,11 @@ export default {
|
||||
...germanMessages,
|
||||
synapseadmin: {
|
||||
auth: {
|
||||
homeserver: "Heimserver",
|
||||
base_url: "Heimserver URL",
|
||||
welcome: "Willkommen bei Synapse-admin",
|
||||
username_error: "Bitte vollständigen Nutzernamen angeben: '@user:domain'",
|
||||
protocol_error: "Die URL muss mit 'http://' oder 'https://' beginnen",
|
||||
url_error: "Keine gültige Matrix Server URL",
|
||||
},
|
||||
action: {
|
||||
save_and_show: "Speichern und QR Code erzeugen",
|
||||
@@ -16,6 +19,16 @@ export default {
|
||||
invalid_user_id:
|
||||
"Muss eine vollständige Matrix Benutzer-ID sein, z.B. @benutzer_id:homeserver",
|
||||
},
|
||||
rooms: {
|
||||
details: "Raumdetails",
|
||||
room_name: "Raumname",
|
||||
make_public: "Öffentlicher Raum",
|
||||
room_name_required: "Muss angegeben werden",
|
||||
alias_required_if_public: "Muss für öffentliche Räume angegeben werden.",
|
||||
alias: "Alias",
|
||||
alias_too_long:
|
||||
"Darf zusammen mit der Domain des Homeservers 255 bytes nicht überschreiten",
|
||||
},
|
||||
},
|
||||
resources: {
|
||||
users: {
|
||||
@@ -55,6 +68,8 @@ export default {
|
||||
name: "Name",
|
||||
canonical_alias: "Alias",
|
||||
joined_members: "Mitglieder",
|
||||
invite_members: "Mitglieder einladen",
|
||||
invitees: "Einladungen",
|
||||
},
|
||||
},
|
||||
connections: {
|
||||
@@ -65,6 +80,41 @@ export default {
|
||||
user_agent: "User Agent",
|
||||
},
|
||||
},
|
||||
servernotices: {
|
||||
name: "Serverbenachrichtigungen",
|
||||
send: "Servernachricht versenden",
|
||||
fields: {
|
||||
body: "Nachricht",
|
||||
},
|
||||
action: {
|
||||
send: "Sende Nachricht",
|
||||
send_success: "Nachricht erfolgreich versendet.",
|
||||
send_failure: "Beim Versenden ist ein Fehler aufgetreten.",
|
||||
},
|
||||
helper: {
|
||||
send:
|
||||
'Sendet eine Serverbenachrichtigung an die ausgewählten Nutzer. Hierfür muss das Feature "Server Notices" auf dem Server aktiviert sein.',
|
||||
},
|
||||
},
|
||||
},
|
||||
ra: {
|
||||
...germanMessages.ra,
|
||||
auth: {
|
||||
...germanMessages.ra.auth,
|
||||
auth_check_error: "Anmeldung fehlgeschlagen",
|
||||
},
|
||||
input: {
|
||||
...germanMessages.ra.input,
|
||||
password: {
|
||||
...germanMessages.ra.input.password,
|
||||
toggle_hidden: "Anzeigen",
|
||||
toggle_visible: "Verstecken",
|
||||
},
|
||||
},
|
||||
notification: {
|
||||
...germanMessages.ra.notifiaction,
|
||||
logged_out: "Abgemeldet",
|
||||
},
|
||||
},
|
||||
ra: {
|
||||
...germanMessages.ra,
|
||||
|
||||
@@ -4,8 +4,11 @@ export default {
|
||||
...englishMessages,
|
||||
synapseadmin: {
|
||||
auth: {
|
||||
homeserver: "Homeserver",
|
||||
base_url: "Homeserver URL",
|
||||
welcome: "Welcome to Synapse-admin",
|
||||
username_error: "Please enter fully qualified user ID: '@user:domain'",
|
||||
protocol_error: "URL has to start with 'http://' or 'https://'",
|
||||
url_error: "Not a valid Matrix server URL",
|
||||
},
|
||||
action: {
|
||||
save_and_show: "Create QR code",
|
||||
@@ -16,6 +19,16 @@ export default {
|
||||
invalid_user_id:
|
||||
"Must be a fully qualified Matrix user-id, e.g. @user_id:homeserver",
|
||||
},
|
||||
rooms: {
|
||||
details: "Room Details",
|
||||
room_name: "Room Name",
|
||||
make_public: "Make room public",
|
||||
room_name_required: "Must be provided",
|
||||
alias_required_if_public: "Must be provided for a public room",
|
||||
alias: "Alias",
|
||||
alias_too_long:
|
||||
"Must not exceed 255 bytes including the domain of the homeserver.",
|
||||
},
|
||||
},
|
||||
resources: {
|
||||
users: {
|
||||
@@ -55,6 +68,9 @@ export default {
|
||||
name: "Name",
|
||||
canonical_alias: "Alias",
|
||||
joined_members: "Members",
|
||||
invite_members: "Invite Members",
|
||||
|
||||
invitees: "Invitations",
|
||||
},
|
||||
},
|
||||
connections: {
|
||||
@@ -65,5 +81,21 @@ export default {
|
||||
user_agent: "User agent",
|
||||
},
|
||||
},
|
||||
servernotices: {
|
||||
name: "Server Notices",
|
||||
send: "Send server notices",
|
||||
fields: {
|
||||
body: "Message",
|
||||
},
|
||||
action: {
|
||||
send: "Send note",
|
||||
send_success: "Server notice successfully sent.",
|
||||
send_failure: "An error has occurred.",
|
||||
},
|
||||
helper: {
|
||||
send:
|
||||
'Sends a server notice to the selected users. The feature "Server Notices" has to be activated at the server.',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,23 +1,8 @@
|
||||
import { fetchUtils } from "react-admin";
|
||||
|
||||
const ensureHttpsForUrl = url => {
|
||||
if (/^https:\/\//i.test(url)) {
|
||||
return url;
|
||||
}
|
||||
const domain = url.replace(/http.?:\/\//g, "");
|
||||
return "https://" + domain;
|
||||
};
|
||||
|
||||
const stripTrailingSlash = str => {
|
||||
if (!str) {
|
||||
return;
|
||||
}
|
||||
return str.endsWith("/") ? str.slice(0, -1) : str;
|
||||
};
|
||||
|
||||
const authProvider = {
|
||||
// called when the user attempts to log in
|
||||
login: ({ homeserver, username, password }) => {
|
||||
login: ({ base_url, username, password }) => {
|
||||
console.log("login ");
|
||||
const options = {
|
||||
method: "POST",
|
||||
@@ -28,17 +13,16 @@ const authProvider = {
|
||||
}),
|
||||
};
|
||||
|
||||
const url = window.decodeURIComponent(homeserver);
|
||||
const trimmed_url = url.trim().replace(/\s/g, "");
|
||||
const login_api_url =
|
||||
ensureHttpsForUrl(trimmed_url) + "/_matrix/client/r0/login";
|
||||
// use the base_url from login instead of the well_known entry from the
|
||||
// server, since the admin might want to access the admin API via some
|
||||
// private address
|
||||
localStorage.setItem("base_url", base_url);
|
||||
|
||||
const decoded_base_url = window.decodeURIComponent(base_url);
|
||||
const login_api_url = decoded_base_url + "/_matrix/client/r0/login";
|
||||
|
||||
return fetchUtils.fetchJson(login_api_url, options).then(({ json }) => {
|
||||
const normalized_base_url = stripTrailingSlash(
|
||||
json.well_known["m.homeserver"].base_url
|
||||
);
|
||||
localStorage.setItem("base_url", normalized_base_url);
|
||||
localStorage.setItem("home_server_url", json.home_server);
|
||||
localStorage.setItem("home_server", json.home_server);
|
||||
localStorage.setItem("user_id", json.user_id);
|
||||
localStorage.setItem("access_token", json.access_token);
|
||||
localStorage.setItem("device_id", json.device_id);
|
||||
|
||||
@@ -23,13 +23,15 @@ const resourceMap = {
|
||||
is_guest: !!u.is_guest,
|
||||
admin: !!u.admin,
|
||||
deactivated: !!u.deactivated,
|
||||
displayname: u.display_name || u.displayname,
|
||||
}),
|
||||
data: "users",
|
||||
total: (json, from, perPage) => {
|
||||
return json.next_token
|
||||
? parseInt(json.next_token, 10) + perPage
|
||||
: from + json.users.length;
|
||||
},
|
||||
total: json => json.total,
|
||||
create: data => ({
|
||||
endpoint: `/_synapse/admin/v2/users/${data.id}`,
|
||||
body: data,
|
||||
method: "PUT",
|
||||
}),
|
||||
delete: id => ({
|
||||
endpoint: `/_synapse/admin/v1/deactivate/${id}`,
|
||||
body: { erase: true },
|
||||
@@ -41,13 +43,22 @@ const resourceMap = {
|
||||
map: r => ({
|
||||
...r,
|
||||
id: r.room_id,
|
||||
alias: r.canonical_alias,
|
||||
members: r.joined_members,
|
||||
}),
|
||||
data: "rooms",
|
||||
total: json => {
|
||||
return json.total_rooms;
|
||||
},
|
||||
total: json => json.total_rooms,
|
||||
create: data => ({
|
||||
endpoint: "/_matrix/client/r0/createRoom",
|
||||
body: {
|
||||
name: data.name,
|
||||
room_alias_name: data.canonical_alias,
|
||||
visibility: data.public ? "public" : "private",
|
||||
invite:
|
||||
Array.isArray(data.invitees) && data.invitees.length > 0
|
||||
? data.invitees
|
||||
: undefined,
|
||||
},
|
||||
method: "POST",
|
||||
})
|
||||
},
|
||||
connections: {
|
||||
path: "/_synapse/admin/v1/whois",
|
||||
@@ -57,6 +68,20 @@ const resourceMap = {
|
||||
}),
|
||||
data: "connections",
|
||||
},
|
||||
servernotices: {
|
||||
map: n => ({ id: n.event_id }),
|
||||
create: data => ({
|
||||
endpoint: "/_synapse/admin/v1/send_server_notice",
|
||||
body: {
|
||||
user_id: data.id,
|
||||
content: {
|
||||
msgtype: "m.text",
|
||||
body: data.body,
|
||||
},
|
||||
},
|
||||
method: "POST",
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
function filterNullValues(key, value) {
|
||||
@@ -85,27 +110,8 @@ const dataProvider = {
|
||||
|
||||
const res = resourceMap[resource];
|
||||
|
||||
const homeserver_url = homeserver + res.path;
|
||||
const url = `${homeserver_url}?${stringify(query)}`;
|
||||
|
||||
// searching for users is not implemented in admin api
|
||||
if (params.filter.q) {
|
||||
console.log("searching");
|
||||
const search_query = { limit: perPage, search_term: params.filter.q };
|
||||
const search_url =
|
||||
homeserver + `/_matrix/client/r0/user_directory/search`;
|
||||
return jsonClient(search_url, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(search_query),
|
||||
}).then(({ json }) => ({
|
||||
data: json["results"].map(u => ({
|
||||
...u,
|
||||
id: u.user_id,
|
||||
name: u.user_id,
|
||||
})),
|
||||
total: json.limited ? perPage * 2 : json.results.length,
|
||||
}));
|
||||
}
|
||||
const endpoint_url = homeserver + res.path;
|
||||
const url = `${endpoint_url}?${stringify(query)}`;
|
||||
|
||||
return jsonClient(url).then(({ json }) => ({
|
||||
data: json[res.data].map(res.map),
|
||||
@@ -120,8 +126,8 @@ const dataProvider = {
|
||||
|
||||
const res = resourceMap[resource];
|
||||
|
||||
const homeserver_url = homeserver + res.path;
|
||||
return jsonClient(`${homeserver_url}/${params.id}`).then(({ json }) => ({
|
||||
const endpoint_url = homeserver + res.path;
|
||||
return jsonClient(`${endpoint_url}/${params.id}`).then(({ json }) => ({
|
||||
data: res.map(json),
|
||||
}));
|
||||
},
|
||||
@@ -133,9 +139,9 @@ const dataProvider = {
|
||||
|
||||
const res = resourceMap[resource];
|
||||
|
||||
const homeserver_url = homeserver + res.path;
|
||||
const endpoint_url = homeserver + res.path;
|
||||
return Promise.all(
|
||||
params.ids.map(id => jsonClient(`${homeserver_url}/${id}`))
|
||||
params.ids.map(id => jsonClient(`${endpoint_url}/${id}`))
|
||||
).then(responses => ({
|
||||
data: responses.map(({ json }) => res.map(json)),
|
||||
}));
|
||||
@@ -160,8 +166,8 @@ const dataProvider = {
|
||||
|
||||
const res = resourceMap[resource];
|
||||
|
||||
const homeserver_url = homeserver + res.path;
|
||||
const url = `${homeserver_url}?${stringify(query)}`;
|
||||
const endpoint_url = homeserver + res.path;
|
||||
const url = `${endpoint_url}?${stringify(query)}`;
|
||||
|
||||
return jsonClient(url).then(({ headers, json }) => ({
|
||||
data: json,
|
||||
@@ -176,8 +182,8 @@ const dataProvider = {
|
||||
|
||||
const res = resourceMap[resource];
|
||||
|
||||
const homeserver_url = homeserver + res.path;
|
||||
return jsonClient(`${homeserver_url}/${params.data.id}`, {
|
||||
const endpoint_url = homeserver + res.path;
|
||||
return jsonClient(`${endpoint_url}/${params.data.id}`, {
|
||||
method: "PUT",
|
||||
body: JSON.stringify(params.data, filterNullValues),
|
||||
}).then(({ json }) => ({
|
||||
@@ -192,9 +198,9 @@ const dataProvider = {
|
||||
|
||||
const res = resourceMap[resource];
|
||||
|
||||
const homeserver_url = homeserver + res.path;
|
||||
const endpoint_url = homeserver + res.path;
|
||||
return Promise.all(
|
||||
params.ids.map(id => jsonClient(`${homeserver_url}/${id}`), {
|
||||
params.ids.map(id => jsonClient(`${endpoint_url}/${id}`), {
|
||||
method: "PUT",
|
||||
body: JSON.stringify(params.data, filterNullValues),
|
||||
})
|
||||
@@ -209,11 +215,13 @@ const dataProvider = {
|
||||
if (!homeserver || !(resource in resourceMap)) return Promise.reject();
|
||||
|
||||
const res = resourceMap[resource];
|
||||
if (!("create" in res)) return Promise.reject();
|
||||
|
||||
const homeserver_url = homeserver + res.path;
|
||||
return jsonClient(`${homeserver_url}/${params.data.id}`, {
|
||||
method: "PUT",
|
||||
body: JSON.stringify(params.data, filterNullValues),
|
||||
const create = res["create"](params.data);
|
||||
const endpoint_url = homeserver + create.endpoint;
|
||||
return jsonClient(endpoint_url, {
|
||||
method: create.method,
|
||||
body: JSON.stringify(create.body, filterNullValues),
|
||||
}).then(({ json }) => ({
|
||||
data: res.map(json),
|
||||
}));
|
||||
@@ -228,16 +236,16 @@ const dataProvider = {
|
||||
|
||||
if ("delete" in res) {
|
||||
const del = res["delete"](params.id);
|
||||
const homeserver_url = homeserver + del.endpoint;
|
||||
return jsonClient(homeserver_url, {
|
||||
const endpoint_url = homeserver + del.endpoint;
|
||||
return jsonClient(endpoint_url, {
|
||||
method: del.method,
|
||||
body: JSON.stringify(del.body),
|
||||
}).then(({ json }) => ({
|
||||
data: json,
|
||||
}));
|
||||
} else {
|
||||
const homeserver_url = homeserver + res.path;
|
||||
return jsonClient(`${homeserver_url}/${params.id}`, {
|
||||
const endpoint_url = homeserver + res.path;
|
||||
return jsonClient(`${endpoint_url}/${params.id}`, {
|
||||
method: "DELETE",
|
||||
body: JSON.stringify(params.data, filterNullValues),
|
||||
}).then(({ json }) => ({
|
||||
@@ -257,8 +265,8 @@ const dataProvider = {
|
||||
return Promise.all(
|
||||
params.ids.map(id => {
|
||||
const del = res["delete"](id);
|
||||
const homeserver_url = homeserver + del.endpoint;
|
||||
return jsonClient(homeserver_url, {
|
||||
const endpoint_url = homeserver + del.endpoint;
|
||||
return jsonClient(endpoint_url, {
|
||||
method: del.method,
|
||||
body: JSON.stringify(del.body),
|
||||
});
|
||||
@@ -267,10 +275,10 @@ const dataProvider = {
|
||||
data: responses.map(({ json }) => json),
|
||||
}));
|
||||
} else {
|
||||
const homeserver_url = homeserver + res.path;
|
||||
const endpoint_url = homeserver + res.path;
|
||||
return Promise.all(
|
||||
params.ids.map(id =>
|
||||
jsonClient(`${homeserver_url}/${id}`, {
|
||||
jsonClient(`${endpoint_url}/${id}`, {
|
||||
method: "DELETE",
|
||||
body: JSON.stringify(params.data, filterNullValues),
|
||||
})
|
||||
|
||||
211
yarn.lock
211
yarn.lock
@@ -1142,10 +1142,10 @@
|
||||
"@types/istanbul-reports" "^1.1.1"
|
||||
"@types/yargs" "^13.0.0"
|
||||
|
||||
"@jest/types@^25.2.6":
|
||||
version "25.2.6"
|
||||
resolved "https://registry.yarnpkg.com/@jest/types/-/types-25.2.6.tgz#c12f44af9bed444438091e4b59e7ed05f8659cb6"
|
||||
integrity sha512-myJTTV37bxK7+3NgKc4Y/DlQ5q92/NOwZsZ+Uch7OXdElxOg61QYc72fPYNAjlvbnJ2YvbXLamIsa9tj48BmyQ==
|
||||
"@jest/types@^25.4.0":
|
||||
version "25.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@jest/types/-/types-25.4.0.tgz#5afeb8f7e1cba153a28e5ac3c9fe3eede7206d59"
|
||||
integrity sha512-XBeaWNzw2PPnGW5aXvZt3+VO60M+34RY3XDsCK5tW7kyj3RK0XClRutCfjqcBuaR2aBQTbluEDME9b5MB9UAPw==
|
||||
dependencies:
|
||||
"@types/istanbul-lib-coverage" "^2.0.0"
|
||||
"@types/istanbul-reports" "^1.1.1"
|
||||
@@ -1153,19 +1153,20 @@
|
||||
chalk "^3.0.0"
|
||||
|
||||
"@material-ui/core@^4.3.3":
|
||||
version "4.9.9"
|
||||
resolved "https://registry.yarnpkg.com/@material-ui/core/-/core-4.9.9.tgz#902dc37eeb415dd3288feb2c92e0dc27d9caed48"
|
||||
integrity sha512-Gp0UdJLxPEnkn7O0QpJ2/LOeIuT8nX9e6CjQFuLnOy10rUGjRsOZ2T170Y057xdUmw1VNE+0bvkkO6dOghxt4g==
|
||||
version "4.9.11"
|
||||
resolved "https://registry.yarnpkg.com/@material-ui/core/-/core-4.9.11.tgz#02f45f4dbea6db191fdc0d993323d64cfae613fd"
|
||||
integrity sha512-S2Ha9GpTxzl29XMeMc8dQX2pj97yApNzuhe/23If53fMdg5Fmd3SgbE1bMbyXeKhxwtXZjOFxd0vU+W/sez8Ew==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.4.4"
|
||||
"@material-ui/styles" "^4.9.6"
|
||||
"@material-ui/system" "^4.9.6"
|
||||
"@material-ui/types" "^5.0.0"
|
||||
"@material-ui/react-transition-group" "^4.2.0"
|
||||
"@material-ui/styles" "^4.9.10"
|
||||
"@material-ui/system" "^4.9.10"
|
||||
"@material-ui/types" "^5.0.1"
|
||||
"@material-ui/utils" "^4.9.6"
|
||||
"@types/react-transition-group" "^4.2.0"
|
||||
clsx "^1.0.2"
|
||||
clsx "^1.0.4"
|
||||
hoist-non-react-statics "^3.3.2"
|
||||
popper.js "^1.14.1"
|
||||
popper.js "^1.16.1-lts"
|
||||
prop-types "^15.7.2"
|
||||
react-is "^16.8.0"
|
||||
react-transition-group "^4.3.0"
|
||||
@@ -1177,14 +1178,24 @@
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.4.4"
|
||||
|
||||
"@material-ui/styles@^4.3.3", "@material-ui/styles@^4.9.6":
|
||||
version "4.9.6"
|
||||
resolved "https://registry.yarnpkg.com/@material-ui/styles/-/styles-4.9.6.tgz#924a30bf7c9b91af9c8f19c12c8573b8a4ecd085"
|
||||
integrity sha512-ijgwStEkw1OZ6gCz18hkjycpr/3lKs1hYPi88O/AUn4vMuuGEGAIrqKVFq/lADmZUNF3DOFIk8LDkp7zmjPxtA==
|
||||
"@material-ui/react-transition-group@^4.2.0":
|
||||
version "4.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@material-ui/react-transition-group/-/react-transition-group-4.3.0.tgz#92529142addb5cc179dbf42d246c7e3fe4d6104b"
|
||||
integrity sha512-CwQ0aXrlUynUTY6sh3UvKuvye1o92en20VGAs6TORnSxUYeRmkX8YeTUN3lAkGiBX1z222FxLFO36WWh6q73rQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.5.5"
|
||||
dom-helpers "^5.0.1"
|
||||
loose-envify "^1.4.0"
|
||||
prop-types "^15.6.2"
|
||||
|
||||
"@material-ui/styles@^4.3.3", "@material-ui/styles@^4.9.10":
|
||||
version "4.9.10"
|
||||
resolved "https://registry.yarnpkg.com/@material-ui/styles/-/styles-4.9.10.tgz#182ccdd0bc8525a459486499bbaebcd92b0db3ab"
|
||||
integrity sha512-EXIXlqVyFDnjXF6tj72y6ZxiSy+mHtrsCo3Srkm3XUeu3Z01aftDBy7ZSr3TQ02gXHTvDSBvegp3Le6p/tl7eA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.4.4"
|
||||
"@emotion/hash" "^0.8.0"
|
||||
"@material-ui/types" "^5.0.0"
|
||||
"@material-ui/types" "^5.0.1"
|
||||
"@material-ui/utils" "^4.9.6"
|
||||
clsx "^1.0.2"
|
||||
csstype "^2.5.2"
|
||||
@@ -1199,19 +1210,19 @@
|
||||
jss-plugin-vendor-prefixer "^10.0.3"
|
||||
prop-types "^15.7.2"
|
||||
|
||||
"@material-ui/system@^4.9.6":
|
||||
version "4.9.6"
|
||||
resolved "https://registry.yarnpkg.com/@material-ui/system/-/system-4.9.6.tgz#fd060540224da4d1740da8ca6e7af288e217717e"
|
||||
integrity sha512-QtfoAePyqXoZ2HUVSwGb1Ro0kucMCvVjbI0CdYIR21t0Opgfm1Oer6ni9P5lfeXA39xSt0wCierw37j+YES48Q==
|
||||
"@material-ui/system@^4.9.10":
|
||||
version "4.9.10"
|
||||
resolved "https://registry.yarnpkg.com/@material-ui/system/-/system-4.9.10.tgz#5de6ec7bea0f222b10b45e5bd5bb8b9a7b938926"
|
||||
integrity sha512-E+t0baX2TBZk6ALm8twG6objpsxLdMM4MDm1++LMt2m7CetCAEc3aIAfDaprk4+tm5hFT1Cah5dRWk8EeIFQYw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.4.4"
|
||||
"@material-ui/utils" "^4.9.6"
|
||||
prop-types "^15.7.2"
|
||||
|
||||
"@material-ui/types@^5.0.0":
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@material-ui/types/-/types-5.0.0.tgz#26d6259dc6b39f4c2e1e9aceff7a11e031941741"
|
||||
integrity sha512-UeH2BuKkwDndtMSS0qgx1kCzSMw+ydtj0xx/XbFtxNSTlXydKwzs5gVW5ZKsFlAkwoOOQ9TIsyoCC8hq18tOwg==
|
||||
"@material-ui/types@^5.0.1":
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@material-ui/types/-/types-5.0.1.tgz#c4954063cdc196eb327ee62c041368b1aebb6d61"
|
||||
integrity sha512-wURPSY7/3+MAtng3i26g+WKwwNE3HEeqa/trDBR5+zWKmcjO+u9t7Npu/J1r+3dmIa/OeziN9D/18IrBKvKffw==
|
||||
|
||||
"@material-ui/utils@^4.9.6":
|
||||
version "4.9.6"
|
||||
@@ -1418,9 +1429,9 @@
|
||||
wait-for-expect "^1.2.0"
|
||||
|
||||
"@testing-library/dom@^7.1.0":
|
||||
version "7.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-7.2.0.tgz#948894c2ef52017d299c35da02e085498363cd1e"
|
||||
integrity sha512-K1Sao38VxsTrjTkFkzeW8m/oCtgCI5lANCE7u9ZaF+TTL3uKuiZ+vazeurxjvRHAsE6PvXjOIl6JFuZfgcWJSQ==
|
||||
version "7.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-7.2.1.tgz#bb3b31d669bbe0c4939dadd95d69caa3c1d0b372"
|
||||
integrity sha512-xIGoHlQ2ZiEL1dJIFKNmLDypzYF+4OJTTASRctl/aoIDaS5y/pRVHRigoqvPUV11mdJoR71IIgi/6UviMgyz4g==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.9.2"
|
||||
"@types/testing-library__dom" "^7.0.0"
|
||||
@@ -1429,9 +1440,9 @@
|
||||
pretty-format "^25.1.0"
|
||||
|
||||
"@testing-library/jest-dom@^5.1.1":
|
||||
version "5.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.3.0.tgz#2ae813b8b0eb69e8808f75d3af8efa3f0dc4d7ec"
|
||||
integrity sha512-Cdhpc3BHL888X55qBNyra9eM0UG63LCm/FqCWTa1Ou/0MpsUbQTM9vW1NU6/jBQFoSLgkFfDG5XVpm2V0dOm/A==
|
||||
version "5.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.5.0.tgz#4707023e8f572021e8a84f65721303ff60828d88"
|
||||
integrity sha512-7sWHrpxG4Yd8TmryI7Rtbx8Ff4mbs3ASye3oshQIuHvsCR+QHgr7rTR/PfeXvOmwUwR36wSTTAvrLKsPmr6VEQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.9.2"
|
||||
"@types/testing-library__jest-dom" "^5.0.2"
|
||||
@@ -1444,9 +1455,9 @@
|
||||
redent "^3.0.0"
|
||||
|
||||
"@testing-library/react@^10.0.2":
|
||||
version "10.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-10.0.2.tgz#8eca7aa52d810cf7150048a2829fdc487162006d"
|
||||
integrity sha512-YT6Mw0oJz7R6vlEkmo1FlUD+K15FeXApOB5Ffm9zooFVnrwkt00w18dUJFMOh1yRp9wTdVRonbor7o4PIpFCmA==
|
||||
version "10.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-10.0.3.tgz#7781adc75cce172f8cda28faa77be29c6270ab43"
|
||||
integrity sha512-EVmd3ghDFBEjOSLnISUdi7OcLdP6tsqjmTprpMaBz5TreoM8jnxGKIPkLD579CE0LYGqK01iffQiy6wwW/RUig==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.9.2"
|
||||
"@testing-library/dom" "^7.1.0"
|
||||
@@ -1461,9 +1472,9 @@
|
||||
"@testing-library/dom" "^5.6.1"
|
||||
|
||||
"@testing-library/user-event@^10.0.1":
|
||||
version "10.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-10.0.1.tgz#9a02dbbc813135f25778b17f92a63933a58e8af5"
|
||||
integrity sha512-M63ftowo1QpAGMnWyz7df0ygqnu4XyF68Sty7mivMAz2HLcY1uLoN3qcen6WMobdY0MoZUi4+BLsziSDAP62Vg==
|
||||
version "10.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-10.0.2.tgz#c44197998a9b7a8d60ff66bb9d41635d9648f064"
|
||||
integrity sha512-fVeP4U37BIYdp9nBRKEITFSLPqgCSS7Og6LHvxoQ2JSOTJ1NJI4Dfesv4uNXxvNNcJgBS88V+Tc6h8vbDsa2iA==
|
||||
|
||||
"@types/babel__core@^7.1.0":
|
||||
version "7.1.7"
|
||||
@@ -1595,9 +1606,9 @@
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react@*":
|
||||
version "16.9.32"
|
||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.32.tgz#f6368625b224604148d1ddf5920e4fefbd98d383"
|
||||
integrity sha512-fmejdp0CTH00mOJmxUPPbWCEBWPvRIL4m8r0qD+BSDUqmutPyGQCHifzMpMzdvZwROdEdL78IuZItntFWgPXHQ==
|
||||
version "16.9.34"
|
||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.34.tgz#f7d5e331c468f53affed17a8a4d488cd44ea9349"
|
||||
integrity sha512-8AJlYMOfPe1KGLKyHpflCg5z46n0b5DbRfqDksxBLBTUpB75ypDBAO9eCUcjNwE6LCUslwTz00yyG/X9gaVtow==
|
||||
dependencies:
|
||||
"@types/prop-types" "*"
|
||||
csstype "^2.2.0"
|
||||
@@ -3023,7 +3034,7 @@ clone-deep@^4.0.1:
|
||||
kind-of "^6.0.2"
|
||||
shallow-clone "^3.0.0"
|
||||
|
||||
clsx@^1.0.2:
|
||||
clsx@^1.0.2, clsx@^1.0.4:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.0.tgz#62937c6adfea771247c34b54d320fb99624f5702"
|
||||
integrity sha512-3avwM37fSK5oP6M5rQ9CNe99lwxhXDOeSWVPAOYF6OazUTgZCMb0yWlJpmdD74REy1gkEaFiub2ULv4fq9GUhA==
|
||||
@@ -3266,9 +3277,9 @@ core-js-compat@^3.6.2:
|
||||
semver "7.0.0"
|
||||
|
||||
core-js-pure@^3.0.0:
|
||||
version "3.6.4"
|
||||
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.4.tgz#4bf1ba866e25814f149d4e9aaa08c36173506e3a"
|
||||
integrity sha512-epIhRLkXdgv32xIUFaaAry2wdxZYBi6bgM7cB136dzzXXa+dFyRLTZeLUJxnd8ShrmyVXBub63n2NHo2JAt8Cw==
|
||||
version "3.6.5"
|
||||
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.5.tgz#c79e75f5e38dbc85a662d91eea52b8256d53b813"
|
||||
integrity sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA==
|
||||
|
||||
core-js@^1.0.0:
|
||||
version "1.2.7"
|
||||
@@ -4252,9 +4263,9 @@ escodegen@^1.11.0, escodegen@^1.9.1:
|
||||
source-map "~0.6.1"
|
||||
|
||||
eslint-config-prettier@^6.10.1:
|
||||
version "6.10.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.10.1.tgz#129ef9ec575d5ddc0e269667bf09defcd898642a"
|
||||
integrity sha512-svTy6zh1ecQojvpbJSgH3aei/Rt7C6i090l5f2WQ4aB05lYHeZIR1qL4wZyyILTbtmnbHP5Yn8MrsOJMGa8RkQ==
|
||||
version "6.11.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.11.0.tgz#f6d2238c1290d01c859a8b5c1f7d352a0b0da8b1"
|
||||
integrity sha512-oB8cpLWSAjOVFEJhhyMZh6NOEOtBVziaqdDQ86+qhDHFbZXoRTM7pNSvFRfW/W/L/LrQ38C99J5CGuRBBzBsdA==
|
||||
dependencies:
|
||||
get-stdin "^6.0.0"
|
||||
|
||||
@@ -4333,9 +4344,9 @@ eslint-plugin-jsx-a11y@6.2.3:
|
||||
jsx-ast-utils "^2.2.1"
|
||||
|
||||
eslint-plugin-prettier@^3.1.2:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.2.tgz#432e5a667666ab84ce72f945c72f77d996a5c9ba"
|
||||
integrity sha512-GlolCC9y3XZfv3RQfwGew7NnuFDKsfI4lbvRK+PIIo23SFH+LemGs4cKwzAaRa+Mdb+lQO/STaIayno8T5sJJA==
|
||||
version "3.1.3"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.3.tgz#ae116a0fc0e598fdae48743a4430903de5b4e6ca"
|
||||
integrity sha512-+HG5jmu/dN3ZV3T6eCD7a4BlAySdN7mLIbJYo0z1cFQuI+r2DiTJEFeF68ots93PsnrMxbzIZ2S/ieX+mkrBeQ==
|
||||
dependencies:
|
||||
prettier-linter-helpers "^1.0.0"
|
||||
|
||||
@@ -6223,15 +6234,15 @@ jest-diff@^24.9.0:
|
||||
jest-get-type "^24.9.0"
|
||||
pretty-format "^24.9.0"
|
||||
|
||||
jest-diff@^25.1.0, jest-diff@^25.2.1, jest-diff@^25.2.6:
|
||||
version "25.2.6"
|
||||
resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-25.2.6.tgz#a6d70a9ab74507715ea1092ac513d1ab81c1b5e7"
|
||||
integrity sha512-KuadXImtRghTFga+/adnNrv9s61HudRMR7gVSbP35UKZdn4IK2/0N0PpGZIqtmllK9aUyye54I3nu28OYSnqOg==
|
||||
jest-diff@^25.1.0, jest-diff@^25.2.1, jest-diff@^25.4.0:
|
||||
version "25.4.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-25.4.0.tgz#260b70f19a46c283adcad7f081cae71eb784a634"
|
||||
integrity sha512-kklLbJVXW0y8UKOWOdYhI6TH5MG6QAxrWiBMgQaPIuhj3dNFGirKCd+/xfplBXICQ7fI+3QcqHm9p9lWu1N6ug==
|
||||
dependencies:
|
||||
chalk "^3.0.0"
|
||||
diff-sequences "^25.2.6"
|
||||
jest-get-type "^25.2.6"
|
||||
pretty-format "^25.2.6"
|
||||
pretty-format "^25.4.0"
|
||||
|
||||
jest-docblock@^24.3.0:
|
||||
version "24.9.0"
|
||||
@@ -6356,14 +6367,14 @@ jest-matcher-utils@^24.9.0:
|
||||
pretty-format "^24.9.0"
|
||||
|
||||
jest-matcher-utils@^25.1.0:
|
||||
version "25.2.7"
|
||||
resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-25.2.7.tgz#53fad3c11fc42e92e374306df543026712c957a3"
|
||||
integrity sha512-jNYmKQPRyPO3ny0KY1I4f0XW4XnpJ3Nx5ovT4ik0TYDOYzuXJW40axqOyS61l/voWbVT9y9nZ1THL1DlpaBVpA==
|
||||
version "25.4.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-25.4.0.tgz#dc3e7aec402a1e567ed80b572b9ad285878895e6"
|
||||
integrity sha512-yPMdtj7YDgXhnGbc66bowk8AkQ0YwClbbwk3Kzhn5GVDrciiCr27U4NJRbrqXbTdtxjImONITg2LiRIw650k5A==
|
||||
dependencies:
|
||||
chalk "^3.0.0"
|
||||
jest-diff "^25.2.6"
|
||||
jest-diff "^25.4.0"
|
||||
jest-get-type "^25.2.6"
|
||||
pretty-format "^25.2.6"
|
||||
pretty-format "^25.4.0"
|
||||
|
||||
jest-message-util@^24.9.0:
|
||||
version "24.9.0"
|
||||
@@ -8220,7 +8231,7 @@ pnp-webpack-plugin@1.6.4:
|
||||
dependencies:
|
||||
ts-pnp "^1.1.6"
|
||||
|
||||
popper.js@^1.14.1:
|
||||
popper.js@^1.16.1-lts:
|
||||
version "1.16.1"
|
||||
resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.16.1.tgz#2a223cb3dc7b6213d740e40372be40de43e65b1b"
|
||||
integrity sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==
|
||||
@@ -8918,9 +8929,9 @@ prettier-linter-helpers@^1.0.0:
|
||||
fast-diff "^1.1.2"
|
||||
|
||||
prettier@^2.0.0:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.0.3.tgz#9a06f0e94a51420e78b6925568b5bec72afe41ea"
|
||||
integrity sha512-5qpBDBHO9fpE0zruKiTZm8Gxmz7kknO+WlQR/ivV+RMwgDw/WjOgmxLDn66MPrxq/WZPx/EgEZzh87xJO5E6Fw==
|
||||
version "2.0.5"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.0.5.tgz#d6d56282455243f2f92cc1716692c08aa31522d4"
|
||||
integrity sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==
|
||||
|
||||
pretty-bytes@^5.1.0:
|
||||
version "5.3.0"
|
||||
@@ -8945,12 +8956,12 @@ pretty-format@^24.8.0, pretty-format@^24.9.0:
|
||||
ansi-styles "^3.2.0"
|
||||
react-is "^16.8.4"
|
||||
|
||||
pretty-format@^25.1.0, pretty-format@^25.2.1, pretty-format@^25.2.6:
|
||||
version "25.2.6"
|
||||
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-25.2.6.tgz#542a1c418d019bbf1cca2e3620443bc1323cb8d7"
|
||||
integrity sha512-DEiWxLBaCHneffrIT4B+TpMvkV9RNvvJrd3lY9ew1CEQobDzEXmYT1mg0hJhljZty7kCc10z13ohOFAE8jrUDg==
|
||||
pretty-format@^25.1.0, pretty-format@^25.2.1, pretty-format@^25.4.0:
|
||||
version "25.4.0"
|
||||
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-25.4.0.tgz#c58801bb5c4926ff4a677fe43f9b8b99812c7830"
|
||||
integrity sha512-PI/2dpGjXK5HyXexLPZU/jw5T9Q6S1YVXxxVxco+LIqzUFHXIbKZKdUVt7GcX7QUCr31+3fzhi4gN4/wUYPVxQ==
|
||||
dependencies:
|
||||
"@jest/types" "^25.2.6"
|
||||
"@jest/types" "^25.4.0"
|
||||
ansi-regex "^5.0.0"
|
||||
ansi-styles "^4.0.0"
|
||||
react-is "^16.12.0"
|
||||
@@ -9151,10 +9162,10 @@ querystringify@^2.1.1:
|
||||
resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e"
|
||||
integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==
|
||||
|
||||
ra-core@^3.4.0:
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/ra-core/-/ra-core-3.4.0.tgz#1d4b49b78cf60eb2b0d2c1584ccfce73611ab9a6"
|
||||
integrity sha512-hpC0r6s6Uodj3e8T06uvBbk/dcv2H/C2Gih+ouAyuKOR4gglAWIPqcpy6SfN5cZrBxbGkvDCFSKDNNMw5067+g==
|
||||
ra-core@^3.4.1:
|
||||
version "3.4.1"
|
||||
resolved "https://registry.yarnpkg.com/ra-core/-/ra-core-3.4.1.tgz#4390f7f317a1bbdabf03b6cd2d4484d7048450ef"
|
||||
integrity sha512-XHcYAU36aMhIAmspdQ299SLCclu7qMmPS/IXN1VPVlRJHAIurK5tgRUMStAgDRO05qIkU5Xnb9C9PA63VmlPwA==
|
||||
dependencies:
|
||||
"@testing-library/react" "^8.0.7"
|
||||
classnames "~2.2.5"
|
||||
@@ -9167,30 +9178,30 @@ ra-core@^3.4.0:
|
||||
recompose "~0.26.0"
|
||||
reselect "~3.0.0"
|
||||
|
||||
ra-i18n-polyglot@^3.4.0:
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/ra-i18n-polyglot/-/ra-i18n-polyglot-3.4.0.tgz#fcc2067e55e953c58f496d579837241615eca85b"
|
||||
integrity sha512-53VWZ3C9cza9cqfibzGVBdFAA/zc3c8pLY2yt5RKNiD1taHylZyX0doebC///jOe7OwUaOHv1KnD5WdLaNM93Q==
|
||||
ra-i18n-polyglot@^3.4.1:
|
||||
version "3.4.1"
|
||||
resolved "https://registry.yarnpkg.com/ra-i18n-polyglot/-/ra-i18n-polyglot-3.4.1.tgz#269618aaae5fd525e36cb52cf1709e5fcd79bb9b"
|
||||
integrity sha512-i13N/wGi7aOxKeABdJ54wwYJZPUeLLUQ23C1TAbNynFro1pIHl2j4lxRBhRIlYPwNKdsUjuLzt30fdcVIGH+FQ==
|
||||
dependencies:
|
||||
node-polyglot "^2.2.2"
|
||||
ra-core "^3.4.0"
|
||||
ra-core "^3.4.1"
|
||||
|
||||
ra-language-english@^3.4.0:
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/ra-language-english/-/ra-language-english-3.4.0.tgz#1296dd9bd632f175e71f885c9bdbc06379b49d5a"
|
||||
integrity sha512-H2ZapYrn4jPyj7/+aA2X4XUaXQ6t2y5gR8DlSsRZlkmlHVWcpUFlyG2gZCYTDAeVKXO5Dedgf09KlYLJ6p7p0w==
|
||||
ra-language-english@^3.4.1:
|
||||
version "3.4.1"
|
||||
resolved "https://registry.yarnpkg.com/ra-language-english/-/ra-language-english-3.4.1.tgz#04a117d9991a2da32f313819041cac7aa2a6337c"
|
||||
integrity sha512-bAoJyIGL3LJ/8hIvQ+gsHYlFKwgpOalQb3ZUdJReU+vAt52nQqN/3BxknxSMRFOxH0iMQk9k3B+EakDtiesH3Q==
|
||||
dependencies:
|
||||
ra-core "^3.4.0"
|
||||
ra-core "^3.4.1"
|
||||
|
||||
ra-language-german@^2.1.2:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/ra-language-german/-/ra-language-german-2.1.2.tgz#d183093d470ab499ece91838cb67222db88f2e4f"
|
||||
integrity sha512-N+BaBP0z98ujaKVlAMIKTfWHgmTiWD8sPQrU5vA3+b5zY9U0mMB4VjvU8sQQPR7rZE0gsRgS/X4V6ycDtNL6iQ==
|
||||
|
||||
ra-ui-materialui@^3.4.0:
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/ra-ui-materialui/-/ra-ui-materialui-3.4.0.tgz#9f3f573384ee8c8e91637627fb888bd51a4fd0b9"
|
||||
integrity sha512-nV10aD4Z8ZgmZUcnfNTzAelQCxqdc5cG/KxyYvcqqV9j0oGBrqC9dr00bKLIBHc1oFTVvLwcdPd4p78tO6B+5g==
|
||||
ra-ui-materialui@^3.4.2:
|
||||
version "3.4.2"
|
||||
resolved "https://registry.yarnpkg.com/ra-ui-materialui/-/ra-ui-materialui-3.4.2.tgz#6ab59b44cbe351eee2afccfeb118ee3ea67db28d"
|
||||
integrity sha512-P1WigQJzIGeMy2jpAoMF0PlkDKZuufUEp/J0y9YFPumHvd5Dvzzz4XCytlL+362XkzgtxVWWH+WlX8CoYD3Y2w==
|
||||
dependencies:
|
||||
autosuggest-highlight "^3.1.1"
|
||||
classnames "~2.2.5"
|
||||
@@ -9257,9 +9268,9 @@ raw-body@2.4.0:
|
||||
unpipe "1.0.0"
|
||||
|
||||
react-admin@^3.4.0:
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/react-admin/-/react-admin-3.4.0.tgz#d2806d9bab6f1c73e4f065d6f339389dd8126195"
|
||||
integrity sha512-tgxCAYPkoZa7LC8ImFU8pQVFf5ja4dbxtx4/bgCOYJ7/QbTYdwFhzwuhfspnzRWDO3Tvkx3VTiuObQuR+JIDug==
|
||||
version "3.4.2"
|
||||
resolved "https://registry.yarnpkg.com/react-admin/-/react-admin-3.4.2.tgz#081a9f930e5822cf31c4cc747d058a9522f945e4"
|
||||
integrity sha512-C0YeuWgd1fskhOk9oG6uyZidw7IEd5MkQWBKahDOY4PW59dZk8hFSuRzkVkaF8B/c/2oM1kCGuVR5likYPrJEA==
|
||||
dependencies:
|
||||
"@material-ui/core" "^4.3.3"
|
||||
"@material-ui/icons" "^4.2.1"
|
||||
@@ -9267,10 +9278,10 @@ react-admin@^3.4.0:
|
||||
connected-react-router "^6.5.2"
|
||||
final-form "^4.18.5"
|
||||
final-form-arrays "^3.0.1"
|
||||
ra-core "^3.4.0"
|
||||
ra-i18n-polyglot "^3.4.0"
|
||||
ra-language-english "^3.4.0"
|
||||
ra-ui-materialui "^3.4.0"
|
||||
ra-core "^3.4.1"
|
||||
ra-i18n-polyglot "^3.4.1"
|
||||
ra-language-english "^3.4.1"
|
||||
ra-ui-materialui "^3.4.2"
|
||||
react-final-form "^6.3.3"
|
||||
react-final-form-arrays "^3.1.1"
|
||||
react-redux "^7.1.0"
|
||||
@@ -10587,9 +10598,9 @@ string.prototype.trim@^1.1.2, string.prototype.trim@^1.2.1:
|
||||
function-bind "^1.1.1"
|
||||
|
||||
string.prototype.trimend@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.0.tgz#ee497fd29768646d84be2c9b819e292439614373"
|
||||
integrity sha512-EEJnGqa/xNfIg05SxiPSqRS7S9qwDhYts1TSLR1BQfYUfPe1stofgGKvwERK9+9yf+PpfBMlpBaCHucXGPQfUA==
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz#85812a6b847ac002270f5808146064c995fb6913"
|
||||
integrity sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==
|
||||
dependencies:
|
||||
define-properties "^1.1.3"
|
||||
es-abstract "^1.17.5"
|
||||
@@ -10613,9 +10624,9 @@ string.prototype.trimright@^2.1.1:
|
||||
string.prototype.trimend "^1.0.0"
|
||||
|
||||
string.prototype.trimstart@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.0.tgz#afe596a7ce9de905496919406c9734845f01a2f2"
|
||||
integrity sha512-iCP8g01NFYiiBOnwG1Xc3WZLyoo+RuBymwIlWncShXDDJYWN6DbnM3odslBJdgCdRlq94B5s63NWAZlcn2CS4w==
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz#14af6d9f34b053f7cfc89b72f8f2ee14b9039a54"
|
||||
integrity sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==
|
||||
dependencies:
|
||||
define-properties "^1.1.3"
|
||||
es-abstract "^1.17.5"
|
||||
|
||||
Reference in New Issue
Block a user