diff --git a/.gradle/6.7/executionHistory/executionHistory.lock b/.gradle/6.7/executionHistory/executionHistory.lock new file mode 100644 index 0000000000000000000000000000000000000000..deb5f5b5e3140eb437a060755c82e6ad0017a534 Binary files /dev/null and b/.gradle/6.7/executionHistory/executionHistory.lock differ diff --git a/.gradle/6.7/fileChanges/last-build.bin b/.gradle/6.7/fileChanges/last-build.bin new file mode 100644 index 0000000000000000000000000000000000000000..f76dd238ade08917e6712764a16a22005a50573d Binary files /dev/null and b/.gradle/6.7/fileChanges/last-build.bin differ diff --git a/.gradle/6.7/fileHashes/fileHashes.lock b/.gradle/6.7/fileHashes/fileHashes.lock new file mode 100644 index 0000000000000000000000000000000000000000..350f6626b873da0a0ad7c4383f28ac25a0fc23bc Binary files /dev/null and b/.gradle/6.7/fileHashes/fileHashes.lock differ diff --git a/.gradle/6.7/gc.properties b/.gradle/6.7/gc.properties new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/.gradle/buildOutputCleanup/buildOutputCleanup.lock new file mode 100644 index 0000000000000000000000000000000000000000..068d78e5c3ce8ab592dcda8633797dfadbd863eb Binary files /dev/null and b/.gradle/buildOutputCleanup/buildOutputCleanup.lock differ diff --git a/.gradle/buildOutputCleanup/cache.properties b/.gradle/buildOutputCleanup/cache.properties new file mode 100644 index 0000000000000000000000000000000000000000..a92031e95d5829aafb8060746dac018cbdd6f72d --- /dev/null +++ b/.gradle/buildOutputCleanup/cache.properties @@ -0,0 +1,2 @@ +#Wed Feb 03 17:11:57 WEST 2021 +gradle.version=6.7 diff --git a/.gradle/checksums/checksums.lock b/.gradle/checksums/checksums.lock new file mode 100644 index 0000000000000000000000000000000000000000..61faee961424c0c9ae3f8863bfc4014ab6a2db96 Binary files /dev/null and b/.gradle/checksums/checksums.lock differ diff --git a/.gradle/configuration-cache/gc.properties b/.gradle/configuration-cache/gc.properties new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.gradle/vcs-1/gc.properties b/.gradle/vcs-1/gc.properties new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/jams-react-client/public/locales/en/translation.json b/jams-react-client/public/locales/en/translation.json index 0ca38adde08d25a48b0ee2afb4b9225d9bbdc0f0..8f30c98f3af08902a06ccb66ed9fe11a2329d9d0 100644 --- a/jams-react-client/public/locales/en/translation.json +++ b/jams-react-client/public/locales/en/translation.json @@ -236,5 +236,8 @@ "add_user_to_a_group": "Add user to a group", "remove_from_group": "Remove from group", "myprofile": "My profile", - "select_blueprint": "Select a blueprint" + "select_blueprint": "Select a blueprint", + "add_moderator_to_blueprint": "Add moderator to blueprint ...", + "add_moderator_to": "Add moderator to", + "remove_moderator": "Remove moderator" } diff --git a/jams-react-client/public/locales/fr/translation.json b/jams-react-client/public/locales/fr/translation.json index 0d32af775bac23f6f34e497253d5af47ef0ea568..e1a21ea1dfcf7a3f8cda726067efe8822861576a 100644 --- a/jams-react-client/public/locales/fr/translation.json +++ b/jams-react-client/public/locales/fr/translation.json @@ -236,5 +236,8 @@ "add_user_to_a_group": "Ajouter l'utilisateur à un groupe", "remove_from_group": "Retirer du groupe", "myprofile": "Mon profil", - "select_blueprint": "Select a blueprint" + "select_blueprint": "Select a blueprint", + "add_moderator_to_blueprint": "Add moderator to blueprint ...", + "add_moderator_to": "Add moderator to", + "remove_moderator": "Remove moderator" } diff --git a/jams-react-client/src/components/Drawer/Drawer.js b/jams-react-client/src/components/Drawer/Drawer.js index 6cb438483107c600b28110cae9dba2db59b13ded..ea35a6dc46f74bb5919be0d6dcf1dc8c5388ddff 100644 --- a/jams-react-client/src/components/Drawer/Drawer.js +++ b/jams-react-client/src/components/Drawer/Drawer.js @@ -44,7 +44,7 @@ export default function TemporaryDrawer(props) { const listUsers = () => ( <List> - {props.type === "user" ? ( props.targets && props.targets.map((target) => ( + {props.type === "user" ? ( props.targets && props.targets.filter((target) => !props.existingTargets.some(t => target.username == t.username)).map((target) => ( <ListItem button key={target.username} @@ -72,7 +72,7 @@ export default function TemporaryDrawer(props) { } /> </ListItem> - ))) : ( props.targets && props.targets.map((target) => ( + ))) : ( props.targets && props.targets.filter((target) => !props.existingTargets.some(t => target.name == t.name)).map((target) => ( <ListItem button key={target.name} diff --git a/jams-react-client/src/views/Blueprint/EditBlueprintPermissions.js b/jams-react-client/src/views/Blueprint/EditBlueprintPermissions.js index 8e25bee744e6fc18ad895f1c278b8dc34a8fcf71..e7e369a2f6f0bd83d34bdc49f5c6c5306a4069c0 100644 --- a/jams-react-client/src/views/Blueprint/EditBlueprintPermissions.js +++ b/jams-react-client/src/views/Blueprint/EditBlueprintPermissions.js @@ -1,7 +1,10 @@ import React from "react"; +import { Link, useHistory } from "react-router-dom"; // @material-ui/core components import { makeStyles } from "@material-ui/core/styles"; import Checkbox from "@material-ui/core/Checkbox"; +import classnames from "classnames"; + // core components import Grid from "@material-ui/core/Grid"; import GridItem from "components/Grid/GridItem.js"; @@ -12,6 +15,17 @@ import CardIcon from "components/Card/CardIcon.js"; import CardBody from "components/Card/CardBody.js"; import FormGroup from "@material-ui/core/FormGroup"; import FormControlLabel from "@material-ui/core/FormControlLabel"; +import Button from "components/CustomButtons/Button.js"; + +import AddCircleOutlineIcon from '@material-ui/icons/AddCircleOutline'; + +import Avatar from "@material-ui/core/Avatar"; + +import Table from "@material-ui/core/Table"; +import TableHead from '@material-ui/core/TableHead'; +import TableRow from "@material-ui/core/TableRow"; +import TableBody from "@material-ui/core/TableBody"; +import TableCell from "@material-ui/core/TableCell"; import Snackbar from "@material-ui/core/Snackbar"; @@ -19,9 +33,18 @@ import { hexToRgb, blackColor } from "assets/jss/material-dashboard-react.js"; import PriorityHighOutlinedIcon from "@material-ui/icons/PriorityHighOutlined"; +import noProfilePicture from "assets/img/faces/no-profile-picture.png"; + import axios from "axios"; import configApiCall from "../../api"; -import { api_path_blueprints } from "../../globalUrls"; +import { + api_path_blueprints, + api_path_get_user_directory_search, + api_path_get_ns_name_from_addr, + api_path_get_user_profile +} from "../../globalUrls"; + +import TemporaryDrawer from "components/Drawer/Drawer" import dashboardStyle from "assets/jss/material-dashboard-react/views/dashboardStyle.js"; @@ -29,6 +52,8 @@ import MuiAlert from "@material-ui/lab/Alert"; import i18next from "i18next"; +import auth from "auth.js"; + const styles = (theme) => ({ ...dashboardStyle, root: { @@ -92,12 +117,20 @@ function Alert(props) { export default function EditBlueprintPermissions(props) { const classes = useStyles(); + const history = useHistory(); + + const tableCellClasses = classnames(classes.tableCell); + + const [openDrawer, setOpenDrawer] = React.useState(false); + const [users, setUsers] = React.useState([]); + const [blueprintModerators, setBlueprintModerators] = React.useState([]); const [videoEnabled, setVideoEnabled] = React.useState(true); const [publicInCalls, setPublicInCalls] = React.useState(true); const [autoAnswer, setAutoAnswer] = React.useState(false); const [peerDiscovery, setPeerDiscovery] = React.useState(true); const [rendezVous, setRendezVous] = React.useState(false); + const [defaultModerators, setDefaultModerators] = React.useState(""); const [upnpEnabled, setUpnpEnabled] = React.useState(true); const [turnEnabled, setTurnEnabled] = React.useState(true); @@ -112,7 +145,39 @@ export default function EditBlueprintPermissions(props) { const [message, setMessage] = React.useState(false); const [severity, setSeverity] = React.useState("success"); + const searchUsers = (value) => { + axios( + configApiCall( + api_path_get_user_directory_search, + "GET", + { queryString: value ? value : "*", page: "1" }, + null + ) + ) + .then((response) => { + let profiles = []; + const profilesResults = response.data.profiles; + profilesResults.forEach((profile) =>{ + let existingUser = false; + users.forEach((user)=>{ + if(profile.username === user.username) existingUser = true; + }) + if(!existingUser) profiles.push(profile); + }) + setUsers(profiles); + }) + .catch((error) => { + console.log(error); + setUsers([]); + if (error.response.status === 401) { + auth.authenticated = false; + history.push("/"); + } + }); + }; + React.useEffect(() => { + searchUsers(); axios( configApiCall( api_path_blueprints + "?name=" + props.blueprintName, @@ -128,6 +193,7 @@ export default function EditBlueprintPermissions(props) { setAutoAnswer(policyData["autoAnswer"]); setPeerDiscovery(policyData["peerDiscovery"]); setRendezVous(policyData["rendezVous"]); + setDefaultModerators(policyData["defaultModerators"]); setUpnpEnabled(policyData["upnpEnabled"]); setTurnEnabled(policyData["turnEnabled"]); @@ -137,6 +203,23 @@ export default function EditBlueprintPermissions(props) { setProxyEnabled(policyData["proxyEnabled"]); setProxyServer(policyData["proxyServer"]); setDhtProxyListUrl(policyData["dhtProxyListUrl"]); + + policyData["defaultModerators"].split("/").forEach((id) => { + if(id !== "undefined" && id !== ""){ + axios(configApiCall(api_path_get_ns_name_from_addr + id), null, null).then((usernameResponse) => { + let username = usernameResponse.data.name; + axios(configApiCall(api_path_get_user_profile + username), null, null).then((userProfileResponse) => { + let userProfiles = blueprintModerators; + userProfileResponse.data["id"] = id; + userProfiles.push(userProfileResponse.data) + setBlueprintModerators(userProfiles); + // This state update is added to refresh the list of moderators + setOpenDrawer(true); + setOpenDrawer(false); + }); + }); + } + }) }) .catch((error) => { console.log( @@ -157,6 +240,7 @@ export default function EditBlueprintPermissions(props) { accountDiscovery: peerDiscovery, accountPublish: peerDiscovery, rendezVous: rendezVous, + defaultModerators: defaultModerators, upnpEnabled: upnpEnabled, turnEnabled: turnEnabled, turnServer: turnServer, @@ -214,6 +298,28 @@ export default function EditBlueprintPermissions(props) { setOpen(false); }; + const addModeratorToBlueprint = (user) => { + if(defaultModerators.includes(user.id)){ + alert(`${user.username} is already a moderator of ${props.blueprintName}`); + } + else { + handleUpdatePermissions("defaultModerators", defaultModerators + user.id + "/"); + setDefaultModerators(defaultModerators + user.id + "/"); + let newBlueprintModerators = blueprintModerators; + newBlueprintModerators.push(user); + setBlueprintModerators(newBlueprintModerators); + } + } + + const removeModeratorFromBlueprint = (user) => { + let newDefaultModerators = defaultModerators.replace( user.id + "/", "") + handleUpdatePermissions("defaultModerators", newDefaultModerators); + setDefaultModerators(newDefaultModerators); + let newBlueprintModerators = blueprintModerators; + newBlueprintModerators.splice(newBlueprintModerators.indexOf(user), 1); + setBlueprintModerators(newBlueprintModerators); + } + return ( <div> <Snackbar @@ -227,6 +333,17 @@ export default function EditBlueprintPermissions(props) { {message} </Alert> </Snackbar> + <TemporaryDrawer + openDrawer={openDrawer} + setOpenDrawer={setOpenDrawer} + direction="right" + placeholder={i18next.t("add_moderator_to_blueprint", "Add moderator to blueprint ...")} + searchTargets={searchUsers} + targets={users} + existingTargets={blueprintModerators} + addElementToTarget={addModeratorToBlueprint} + type="user" + /> <GridContainer> <GridItem xs={12} sm={12} md={6}> <Card profile> @@ -342,6 +459,51 @@ export default function EditBlueprintPermissions(props) { </CardBody> </Card> </GridItem> + <GridItem xs={12} sm={12} md={12}> + <Button color="primary" onClick={() => {setOpenDrawer(true)}}><AddCircleOutlineIcon /> {i18next.t("add_moderator_to", "Add moderator to")} {props.blueprintName}</Button> + <Table className={classes.table}> + <TableHead> + <TableRow> + <TableCell align="left"></TableCell> + <TableCell align="left">{i18next.t("username", "Username")}</TableCell> + <TableCell align="left">{i18next.t("first_name", "First name")}</TableCell> + <TableCell align="left">{i18next.t("last_name", "Last name")}</TableCell> + <TableCell align="right">{i18next.t("action", "Action")}</TableCell> + </TableRow> + </TableHead> + <TableBody> + {blueprintModerators.map(user => + <TableRow key={user.username} className={classes.tableRow}> + <TableCell className={tableCellClasses}> + <Link to={`/user/${user.username}`}> + <Avatar + style={{ marginRight: "10px" }} + alt={user.username} + src={ + user.profilePicture + ? "data:image/png;base64, " + user.profilePicture + : noProfilePicture + } + /> + </Link> + </TableCell> + <TableCell className={tableCellClasses}> + <Link to={`/user/${user.username}`}>{user.username}</Link> + </TableCell> + <TableCell className={tableCellClasses}> + <Link to={`/user/${user.username}`}>{user.firstName}</Link> + </TableCell> + <TableCell className={tableCellClasses}> + <Link to={`/user/${user.username}`}>{user.lastName}</Link> + </TableCell> + <TableCell align="right" className={classes.tableActions}> + <Button color="primary" onClick={() => removeModeratorFromBlueprint(user)}>{i18next.t("remove_moderator", "Remove moderator")}</Button> + </TableCell> + </TableRow> + )} + </TableBody> + </Table> + </GridItem> </GridContainer> </div> ); diff --git a/jams-react-client/src/views/Blueprints/Blueprints.js b/jams-react-client/src/views/Blueprints/Blueprints.js index 6fd57cd5dce290edfcab3291b3981440c8bfe1ca..9dc6c21e50192ff17f6d41b0b39202871a6d7f8f 100644 --- a/jams-react-client/src/views/Blueprints/Blueprints.js +++ b/jams-react-client/src/views/Blueprints/Blueprints.js @@ -188,6 +188,7 @@ export default function Blueprints() { accountDiscovery: true, accountPublish: true, rendezVous: false, + defaultModerators: "", upnpEnabled: true, }; diff --git a/jams-react-client/src/views/Contacts/Contacts.js b/jams-react-client/src/views/Contacts/Contacts.js index d101d23f3e919700d57e4740d2f607ee39f06baa..6464f091ae780c2b07376d9d52640a1a82960e60 100644 --- a/jams-react-client/src/views/Contacts/Contacts.js +++ b/jams-react-client/src/views/Contacts/Contacts.js @@ -148,10 +148,10 @@ export default function Users(props) { null ) ).then((response) => { - contact.name = response.data.name; + contact.username = response.data.name; axios( configApiCall( - api_path_get_user_profile + contact.name, + api_path_get_user_profile + contact.username, "GET", null, null @@ -191,10 +191,10 @@ export default function Users(props) { null ) ).then((response) => { - contact.name = response.data.name; + contact.username = response.data.name; axios( configApiCall( - api_path_get_user_profile + contact.name, + api_path_get_user_profile + contact.username, "GET", null, null @@ -336,6 +336,7 @@ export default function Users(props) { placeholder={i18next.t("add_contact", "Add contact…")} searchTargets={searchContacts} targets={users} + existingTargets={contacts} addElementToTarget={addContactToUser} targetName={props.username} type="user" @@ -418,8 +419,8 @@ export default function Users(props) { key={contact.uri} style={{ display: contact.display }} > - {contact.name && <Card profile> - <a href={`/user/${contact.name}`}> + {contact.username && <Card profile> + <a href={`/user/${contact.username}`}> <CardBody profile> <CardAvatar profile> <img @@ -436,13 +437,13 @@ export default function Users(props) { </h4> <ul> <li> - {contact.name && <img + {contact.username && <img src={jami} width="20" alt="Jami" style={{ marginRight: "10px" }} />} - {contact.name && ` ${contact.name}`} + {contact.username && ` ${contact.username}`} </li> <li> {contact.organization && <BusinessOutlinedIcon @@ -458,7 +459,7 @@ export default function Users(props) { <IconButton color="secondary" onClick={() => { - handleRemoveContact(contact.uri, contact.name); + handleRemoveContact(contact.uri, contact.username); }} > <DeleteOutlineIcon /> diff --git a/jams-react-client/src/views/Groups/EditGroup.js b/jams-react-client/src/views/Groups/EditGroup.js index 4c0ddf6ce201e03563e2be83241be5bc3b03bae7..5953ae3ad395ccd5c610d79ab260ea7f689d36a5 100644 --- a/jams-react-client/src/views/Groups/EditGroup.js +++ b/jams-react-client/src/views/Groups/EditGroup.js @@ -352,6 +352,7 @@ export default function EditGroup(props) { placeholder={i18next.t("add_user_to_group", "Add user to group ...")} searchTargets={searchUsers} targets={users} + existingTargets={groupMembers} addElementToTarget={addUserInGroup} targetName={name} type="user" diff --git a/jams-react-client/src/views/UserProfile/DisplayUserProfile.js b/jams-react-client/src/views/UserProfile/DisplayUserProfile.js index d5bcfa316d51b773181226ef3d71f8449b6fc3b0..282ff263fc38bce73ff1c10ea3fb89ce639196a3 100644 --- a/jams-react-client/src/views/UserProfile/DisplayUserProfile.js +++ b/jams-react-client/src/views/UserProfile/DisplayUserProfile.js @@ -535,6 +535,7 @@ export default function DisplayUserProfile(props) { placeholder={i18next.t("add_user_to_group", "Add user to group ...")} searchTargets={searchGroups} targets={groups} + existingTargets={groupMemberships} addElementToTarget={addUserToGroup} targetName={props.username} type="group" diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/device/DeviceServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/device/DeviceServlet.java index e7edda95c22d95a6d02d0291769c1f89f88f0612..9df155dd0ddeb73ad5aac68dde2c53e9fb31ec2d 100644 --- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/device/DeviceServlet.java +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/device/DeviceServlet.java @@ -193,6 +193,10 @@ public class DeviceServlet extends HttpServlet { obj.add("Account.displayName", obj.get("displayName")); obj.remove("displayName"); } + if (obj.get("defaultModerators") != null) { + obj.add("Account.defaultModerators", obj.get("defaultModerators")); + obj.remove("defaultModerators"); + } resp.getOutputStream().write((obj.toString()).getBytes()); }