From 3e9f59ef478ac2064a45eba4bb7059c6dcabb141 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Thu, 21 Mar 2019 09:58:36 +0100 Subject: [PATCH] support for managing external managed groups --- .../src/main/java/sonia/scm/group/Group.java | 31 ++++++- .../packages/ui-types/src/Group.js | 1 + scm-ui/public/locales/de/groups.json | 5 +- scm-ui/public/locales/en/groups.json | 5 +- scm-ui/src/groups/components/GroupForm.js | 90 ++++++++++++++----- scm-ui/src/groups/components/table/Details.js | 8 +- .../src/groups/components/table/GroupRow.js | 54 +++++------ .../src/groups/components/table/GroupTable.js | 67 +++++++------- .../sonia/scm/api/v2/resources/GroupDto.java | 1 + 9 files changed, 175 insertions(+), 87 deletions(-) diff --git a/scm-core/src/main/java/sonia/scm/group/Group.java b/scm-core/src/main/java/sonia/scm/group/Group.java index 8860545c93..c3806be847 100644 --- a/scm-core/src/main/java/sonia/scm/group/Group.java +++ b/scm-core/src/main/java/sonia/scm/group/Group.java @@ -50,6 +50,7 @@ import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlRootElement; import java.util.Arrays; +import java.util.Collections; import java.util.List; //~--- JDK imports ------------------------------------------------------------ @@ -195,6 +196,7 @@ public class Group extends BasicPropertiesAware group.setMembers(members); group.setType(type); group.setDescription(description); + group.setExternal(true); } /** @@ -224,6 +226,7 @@ public class Group extends BasicPropertiesAware && Objects.equal(description, other.description) && Objects.equal(members, other.members) && Objects.equal(type, other.type) + && Objects.equal(external, other.external) && Objects.equal(creationDate, other.creationDate) && Objects.equal(lastModified, other.lastModified) && Objects.equal(properties, other.properties); @@ -270,6 +273,7 @@ public class Group extends BasicPropertiesAware .add("description", description) .add("members", members) .add("type", type) + .add("external", external) .add("creationDate", creationDate) .add("lastModified", lastModified) .add("properties", properties) @@ -339,8 +343,9 @@ public class Group extends BasicPropertiesAware */ public List getMembers() { - if (members == null) - { + if (external) { + return Collections.emptyList(); + } else if (members == null) { members = Lists.newArrayList(); } @@ -370,6 +375,15 @@ public class Group extends BasicPropertiesAware return type; } + /** + * Returns {@code true} if the members of the groups managed external of scm-manager. + * + * @return {@code true} if the group is an external group + */ + public boolean isExternal() { + return external; + } + /** * Returns true if the member is a member of this group. * @@ -463,8 +477,21 @@ public class Group extends BasicPropertiesAware this.type = type; } + /** + * {@code true} to mark the group as external. + * + * @param {@code true} for a external group + */ + public void setExternal(boolean external) + { + this.external = external; + } + //~--- fields --------------------------------------------------------------- + /** external group */ + private boolean external = false; + /** timestamp of the creation date of this group */ private Long creationDate; diff --git a/scm-ui-components/packages/ui-types/src/Group.js b/scm-ui-components/packages/ui-types/src/Group.js index 69c6fb4985..995ccb08de 100644 --- a/scm-ui-components/packages/ui-types/src/Group.js +++ b/scm-ui-components/packages/ui-types/src/Group.js @@ -10,6 +10,7 @@ export type Group = Collection & { name: string, description: string, type: string, + external: boolean, members: string[], _embedded: { members: Member[] diff --git a/scm-ui/public/locales/de/groups.json b/scm-ui/public/locales/de/groups.json index 1b2504a644..897cab02d3 100644 --- a/scm-ui/public/locales/de/groups.json +++ b/scm-ui/public/locales/de/groups.json @@ -5,6 +5,7 @@ "creationDate": "Erstellt", "lastModified": "Zuletzt bearbeitet", "type": "Typ", + "external": "Extern", "members": "Mitglieder" }, "groups": { @@ -49,13 +50,15 @@ }, "groupForm": { "subtitle": "Gruppe bearbeiten", + "externalSubtitle": "Externe Gruppe bearbeiten", "submit": "Speichern", "nameError": "Name ist ungültig", "descriptionError": "Beschreibung ist ungültig", "help": { "nameHelpText": "Eindeutiger Name der Gruppe", "descriptionHelpText": "Eine kurze Beschreibung der Gruppe", - "memberHelpText": "Benutzername des Mitglieds der Gruppe" + "memberHelpText": "Benutzername des Mitglieds der Gruppe", + "externalHelpText": "Mitglieder dieser Gruppe werden von einem externen System wie z.B.: einem LDAP-Server verwaltet" } }, "deleteGroup": { diff --git a/scm-ui/public/locales/en/groups.json b/scm-ui/public/locales/en/groups.json index 3b34e5722b..a54249988a 100644 --- a/scm-ui/public/locales/en/groups.json +++ b/scm-ui/public/locales/en/groups.json @@ -5,6 +5,7 @@ "creationDate": "Creation Date", "lastModified": "Last Modified", "type": "Type", + "external": "External", "members": "Members" }, "groups": { @@ -49,13 +50,15 @@ }, "groupForm": { "subtitle": "Edit Group", + "externalSubtitle": "Edit external group", "submit": "Submit", "nameError": "Group name is invalid", "descriptionError": "Description is invalid", "help": { "nameHelpText": "Unique name of the group", "descriptionHelpText": "A short description of the group", - "memberHelpText": "Usernames of the group members" + "memberHelpText": "Usernames of the group members", + "externalHelpText": "Members are managed by an external system such as LDAP" } }, "deleteGroup": { diff --git a/scm-ui/src/groups/components/GroupForm.js b/scm-ui/src/groups/components/GroupForm.js index 8693cb9b47..d3b6799860 100644 --- a/scm-ui/src/groups/components/GroupForm.js +++ b/scm-ui/src/groups/components/GroupForm.js @@ -8,7 +8,8 @@ import { MemberNameTable, InputField, SubmitButton, - Textarea + Textarea, + Checkbox } from "@scm-manager/ui-components"; import type { Group, SelectValue } from "@scm-manager/ui-types"; @@ -67,16 +68,68 @@ class GroupForm extends React.Component { submit = (event: Event) => { event.preventDefault(); if (this.isValid()) { - this.props.submitForm(this.state.group); + const { group } = this.state; + if (group.external) { + group.members = []; + } + this.props.submitForm(group); } }; + renderMemberfields = (group: Group) => { + if (group.external) { + return null; + } + + const { loadUserSuggestions, t } = this.props; + return ( + <> + + + + + ); + }; + + renderExternalField = (group: Group) => { + const { t } = this.props; + if (this.isExistingGroup()) { + return null; + } + return ( + + ); + }; + + isExistingGroup = () => !! this.props.group; + render() { const { loading, t } = this.props; const { group } = this.state; let nameField = null; let subtitle = null; - if (!this.props.group) { + if (!this.isExistingGroup()) { // create new group nameField = ( { helpText={t("groupForm.help.nameHelpText")} /> ); + } else if (group.external) { + subtitle = ; } else { - // edit existing group subtitle = ; } @@ -106,26 +160,8 @@ class GroupForm extends React.Component { validationError={false} helpText={t("groupForm.help.descriptionHelpText")} /> - - - - + {this.renderExternalField(group)} + {this.renderMemberfields(group)} { group: { ...this.state.group, description } }); }; + + handleExternalChange = (external: boolean) => { + this.setState({ + group: { ...this.state.group, external } + }); + }; } export default translate("groups")(GroupForm); diff --git a/scm-ui/src/groups/components/table/Details.js b/scm-ui/src/groups/components/table/Details.js index 4391310d01..4d64e3c63e 100644 --- a/scm-ui/src/groups/components/table/Details.js +++ b/scm-ui/src/groups/components/table/Details.js @@ -2,7 +2,7 @@ import React from "react"; import type { Group } from "@scm-manager/ui-types"; import GroupMember from "./GroupMember"; -import { DateFromNow } from "@scm-manager/ui-components"; +import { DateFromNow, Checkbox } from "@scm-manager/ui-components"; import { translate } from "react-i18next"; import injectSheet from "react-jss"; @@ -34,6 +34,12 @@ class Details extends React.Component { {t("group.description")} {group.description} + + {t("group.external")} + + + + {t("group.type")} {group.type} diff --git a/scm-ui/src/groups/components/table/GroupRow.js b/scm-ui/src/groups/components/table/GroupRow.js index ccff8bd193..e99299012c 100644 --- a/scm-ui/src/groups/components/table/GroupRow.js +++ b/scm-ui/src/groups/components/table/GroupRow.js @@ -1,25 +1,29 @@ -// @flow -import React from "react"; -import { Link } from "react-router-dom"; -import type { Group } from "@scm-manager/ui-types"; - -type Props = { - group: Group -}; - -export default class GroupRow extends React.Component { - renderLink(to: string, label: string) { - return {label}; - } - - render() { - const { group } = this.props; - const to = `/group/${group.name}`; - return ( - - {this.renderLink(to, group.name)} - {group.description} - - ); - } -} +// @flow +import React from "react"; +import { Link } from "react-router-dom"; +import type { Group } from "@scm-manager/ui-types"; +import { Checkbox } from "@scm-manager/ui-components" + +type Props = { + group: Group +}; + +export default class GroupRow extends React.Component { + renderLink(to: string, label: string) { + return {label}; + } + + render() { + const { group } = this.props; + const to = `/group/${group.name}`; + return ( + + {this.renderLink(to, group.name)} + {group.description} + + + + + ); + } +} diff --git a/scm-ui/src/groups/components/table/GroupTable.js b/scm-ui/src/groups/components/table/GroupTable.js index cd38180fc4..85bcb813fc 100644 --- a/scm-ui/src/groups/components/table/GroupTable.js +++ b/scm-ui/src/groups/components/table/GroupTable.js @@ -1,33 +1,34 @@ -// @flow -import React from "react"; -import { translate } from "react-i18next"; -import GroupRow from "./GroupRow"; -import type { Group } from "@scm-manager/ui-types"; - -type Props = { - t: string => string, - groups: Group[] -}; - -class GroupTable extends React.Component { - render() { - const { groups, t } = this.props; - return ( - - - - - - - - - {groups.map((group, index) => { - return ; - })} - -
{t("group.name")}{t("group.description")}
- ); - } -} - -export default translate("groups")(GroupTable); +// @flow +import React from "react"; +import { translate } from "react-i18next"; +import GroupRow from "./GroupRow"; +import type { Group } from "@scm-manager/ui-types"; + +type Props = { + t: string => string, + groups: Group[] +}; + +class GroupTable extends React.Component { + render() { + const { groups, t } = this.props; + return ( + + + + + + + + + + {groups.map((group, index) => { + return ; + })} + +
{t("group.name")}{t("group.description")}{t("group.external")}
+ ); + } +} + +export default translate("groups")(GroupTable); diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupDto.java index a150570316..bdbab5f9ff 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupDto.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupDto.java @@ -27,6 +27,7 @@ public class GroupDto extends HalRepresentation { private String type; private Map properties; private List members; + private boolean external; GroupDto(Links links, Embedded embedded) { super(links, embedded);