From 93025629e65da63ad444d2a601d443055081a481 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Wed, 15 May 2019 15:57:18 +0200 Subject: [PATCH 01/13] Migrate verbs to roles if possible --- .../xml/SingleRepositoryUpdateProcessor.java | 15 ++ .../update/MigrateVerbsToPermissionRoles.java | 143 ++++++++++++++++++ .../repository/update/RepositoryUpdates.java | 10 ++ .../SystemRepositoryPermissionProvider.java | 2 +- .../MigrateVerbsToPermissionRolesTest.java | 76 ++++++++++ .../update/metadataWithoutRoles.xml | 25 +++ 6 files changed, 270 insertions(+), 1 deletion(-) create mode 100644 scm-dao-xml/src/main/java/sonia/scm/repository/xml/SingleRepositoryUpdateProcessor.java create mode 100644 scm-webapp/src/main/java/sonia/scm/repository/update/MigrateVerbsToPermissionRoles.java create mode 100644 scm-webapp/src/main/java/sonia/scm/repository/update/RepositoryUpdates.java create mode 100644 scm-webapp/src/test/java/sonia/scm/repository/update/MigrateVerbsToPermissionRolesTest.java create mode 100644 scm-webapp/src/test/resources/sonia/scm/repository/update/metadataWithoutRoles.xml diff --git a/scm-dao-xml/src/main/java/sonia/scm/repository/xml/SingleRepositoryUpdateProcessor.java b/scm-dao-xml/src/main/java/sonia/scm/repository/xml/SingleRepositoryUpdateProcessor.java new file mode 100644 index 0000000000..eeb95f75b0 --- /dev/null +++ b/scm-dao-xml/src/main/java/sonia/scm/repository/xml/SingleRepositoryUpdateProcessor.java @@ -0,0 +1,15 @@ +package sonia.scm.repository.xml; + +import javax.inject.Inject; +import java.nio.file.Path; +import java.util.function.BiConsumer; + +public class SingleRepositoryUpdateProcessor { + + @Inject + private PathBasedRepositoryLocationResolver locationResolver; + + public void doUpdate(BiConsumer forEachRepository) { + locationResolver.forAllPaths(forEachRepository); + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/repository/update/MigrateVerbsToPermissionRoles.java b/scm-webapp/src/main/java/sonia/scm/repository/update/MigrateVerbsToPermissionRoles.java new file mode 100644 index 0000000000..122b9a25e8 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/repository/update/MigrateVerbsToPermissionRoles.java @@ -0,0 +1,143 @@ +package sonia.scm.repository.update; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import sonia.scm.migration.UpdateException; +import sonia.scm.migration.UpdateStep; +import sonia.scm.plugin.Extension; +import sonia.scm.repository.HealthCheckFailure; +import sonia.scm.repository.Repository; +import sonia.scm.repository.RepositoryPermission; +import sonia.scm.repository.RepositoryRole; +import sonia.scm.repository.xml.SingleRepositoryUpdateProcessor; +import sonia.scm.security.SystemRepositoryPermissionProvider; +import sonia.scm.version.Version; + +import javax.inject.Inject; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElementWrapper; +import javax.xml.bind.annotation.XmlRootElement; +import java.nio.file.Path; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +@Extension +public class MigrateVerbsToPermissionRoles extends RepositoryUpdates.RepositoryUpdateType implements UpdateStep { + + public static final Logger LOG = LoggerFactory.getLogger(MigrateVerbsToPermissionRoles.class); + + private final SingleRepositoryUpdateProcessor updateProcessor; + private final SystemRepositoryPermissionProvider systemRepositoryPermissionProvider; + + @Inject + public MigrateVerbsToPermissionRoles(SingleRepositoryUpdateProcessor updateProcessor, SystemRepositoryPermissionProvider systemRepositoryPermissionProvider) { + this.updateProcessor = updateProcessor; + this.systemRepositoryPermissionProvider = systemRepositoryPermissionProvider; + } + + @Override + public void doUpdate() { + updateProcessor.doUpdate(this::update); + } + + void update(String repositoryId, Path path) { + LOG.info("updating repository {}", repositoryId); + OldRepository oldRepository = readOldRepository(path); + Repository newRepository = createNewRepository(oldRepository); + writeNewRepository(path, newRepository); + } + + private void writeNewRepository(Path path, Repository newRepository) { + try { + JAXBContext jaxbContext = JAXBContext.newInstance(Repository.class); + Marshaller marshaller = jaxbContext.createMarshaller(); + marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); + marshaller.marshal(newRepository, path.resolve("metadata.xml").toFile()); + } catch (JAXBException e) { + throw new UpdateException("could not read old repository structure", e); + } + } + + private OldRepository readOldRepository(Path path) { + try { + JAXBContext jaxbContext = JAXBContext.newInstance(OldRepository.class); + return (OldRepository) jaxbContext.createUnmarshaller().unmarshal(path.resolve("metadata.xml").toFile()); + } catch (JAXBException e) { + throw new UpdateException("could not read old repository structure", e); + } + } + + private Repository createNewRepository(OldRepository oldRepository) { + Repository repository = new Repository( + oldRepository.id, + oldRepository.type, + oldRepository.namespace, + oldRepository.name, + oldRepository.contact, + oldRepository.description, + oldRepository.permissions.stream().map(this::updatePermission).toArray(RepositoryPermission[]::new) + ); + repository.setCreationDate(oldRepository.creationDate); + repository.setHealthCheckFailures(oldRepository.healthCheckFailures); + repository.setLastModified(oldRepository.lastModified); + repository.setPublicReadable(oldRepository.publicReadable); + repository.setArchived(oldRepository.archived); + return repository; + } + + private RepositoryPermission updatePermission(RepositoryPermission repositoryPermission) { + return findMatchingRole(repositoryPermission.getVerbs()) + .map(roleName -> copyRepositoryPermissionWithRole(repositoryPermission, roleName)) + .orElse(repositoryPermission); + } + + private RepositoryPermission copyRepositoryPermissionWithRole(RepositoryPermission repositoryPermission, String roleName) { + return new RepositoryPermission(repositoryPermission.getName(), roleName, repositoryPermission.isGroupPermission()); + } + + private Optional findMatchingRole(Collection verbs) { + return systemRepositoryPermissionProvider.availableRoles() + .stream() + .filter(r -> roleMatchesVerbs(verbs, r)) + .map(RepositoryRole::getName) + .findFirst(); + } + + private boolean roleMatchesVerbs(Collection verbs, RepositoryRole r) { + return verbs.size() == r.getVerbs().size() && r.getVerbs().containsAll(verbs); + } + + @Override + public Version getTargetVersion() { + return Version.parse("1"); + } + + @XmlAccessorType(XmlAccessType.FIELD) + @XmlRootElement(name = "repositories") + private static class OldRepository { + private String contact; + private Long creationDate; + private String description; + @XmlElement(name = "healthCheckFailure") + @XmlElementWrapper(name = "healthCheckFailures") + private List healthCheckFailures; + private String id; + private Long lastModified; + private String namespace; + private String name; + @XmlElement(name = "permission") + private final Set permissions = new HashSet<>(); + @XmlElement(name = "public") + private boolean publicReadable = false; + private boolean archived = false; + private String type; + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/repository/update/RepositoryUpdates.java b/scm-webapp/src/main/java/sonia/scm/repository/update/RepositoryUpdates.java new file mode 100644 index 0000000000..2c814605c4 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/repository/update/RepositoryUpdates.java @@ -0,0 +1,10 @@ +package sonia.scm.repository.update; + +public class RepositoryUpdates { + + static class RepositoryUpdateType { + public String getAffectedDataType() { + return "repository"; + } + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/security/SystemRepositoryPermissionProvider.java b/scm-webapp/src/main/java/sonia/scm/security/SystemRepositoryPermissionProvider.java index 0350698352..444ee3e941 100644 --- a/scm-webapp/src/main/java/sonia/scm/security/SystemRepositoryPermissionProvider.java +++ b/scm-webapp/src/main/java/sonia/scm/security/SystemRepositoryPermissionProvider.java @@ -25,7 +25,7 @@ import java.util.Set; import static java.util.Collections.unmodifiableCollection; import static java.util.stream.Collectors.toList; -class SystemRepositoryPermissionProvider { +public class SystemRepositoryPermissionProvider { private static final Logger logger = LoggerFactory.getLogger(SystemRepositoryPermissionProvider.class); private static final String REPOSITORY_PERMISSION_DESCRIPTOR = "META-INF/scm/repository-permissions.xml"; diff --git a/scm-webapp/src/test/java/sonia/scm/repository/update/MigrateVerbsToPermissionRolesTest.java b/scm-webapp/src/test/java/sonia/scm/repository/update/MigrateVerbsToPermissionRolesTest.java new file mode 100644 index 0000000000..8480bd76d0 --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/repository/update/MigrateVerbsToPermissionRolesTest.java @@ -0,0 +1,76 @@ +package sonia.scm.repository.update; + +import com.google.common.io.Resources; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junitpioneer.jupiter.TempDirectory; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import sonia.scm.repository.RepositoryRole; +import sonia.scm.repository.xml.SingleRepositoryUpdateProcessor; +import sonia.scm.security.SystemRepositoryPermissionProvider; + +import java.io.IOException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import java.util.List; +import java.util.function.BiConsumer; + +import static java.util.Arrays.asList; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +@ExtendWith(TempDirectory.class) +class MigrateVerbsToPermissionRolesTest { + + private static final String EXISTING_REPOSITORY_ID = "id"; + + @Mock + private SingleRepositoryUpdateProcessor singleRepositoryUpdateProcessor; + @Mock + private SystemRepositoryPermissionProvider systemRepositoryPermissionProvider; + + @InjectMocks + private MigrateVerbsToPermissionRoles migration; + + @BeforeEach + void init(@TempDirectory.TempDir Path tempDir) throws IOException { + URL metadataUrl = Resources.getResource("sonia/scm/repository/update/metadataWithoutRoles.xml"); + Files.copy(metadataUrl.openStream(), tempDir.resolve("metadata.xml")); + doAnswer(invocation -> { + ((BiConsumer) invocation.getArgument(0)).accept(EXISTING_REPOSITORY_ID, tempDir); + return null; + }).when(singleRepositoryUpdateProcessor).doUpdate(any()); + when(systemRepositoryPermissionProvider.availableRoles()).thenReturn(Collections.singletonList(new RepositoryRole("ROLE", asList("read", "write"), ""))); + } + + @Test + void x(@TempDirectory.TempDir Path tempDir) throws IOException { + migration.doUpdate(); + + List newMetadata = Files.readAllLines(tempDir.resolve("metadata.xml")); + Assertions.assertThat(newMetadata.stream().map(String::trim)). + containsSubsequence( + "false", + "user", + "ROLE" + ) + .containsSubsequence( + "true", + "group", + "special" + ) + .doesNotContain( + "read", + "write" + ); + } + +} diff --git a/scm-webapp/src/test/resources/sonia/scm/repository/update/metadataWithoutRoles.xml b/scm-webapp/src/test/resources/sonia/scm/repository/update/metadataWithoutRoles.xml new file mode 100644 index 0000000000..4716943f47 --- /dev/null +++ b/scm-webapp/src/test/resources/sonia/scm/repository/update/metadataWithoutRoles.xml @@ -0,0 +1,25 @@ + + + + ich@du.er + 1557729536519 + + B3RQKYNzo2 + 1557825677782 + scmadmin + git + + false + user + read + write + + + true + group + special + + false + false + git + From 7af5608aeb9927036e7843b374bac9c2765ef923 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Tue, 11 Jun 2019 14:04:47 +0200 Subject: [PATCH 02/13] Change target version to 2.0.0 --- .../scm/update/repository/MigrateVerbsToPermissionRoles.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scm-webapp/src/main/java/sonia/scm/update/repository/MigrateVerbsToPermissionRoles.java b/scm-webapp/src/main/java/sonia/scm/update/repository/MigrateVerbsToPermissionRoles.java index d197f6fd9c..56227b7b13 100644 --- a/scm-webapp/src/main/java/sonia/scm/update/repository/MigrateVerbsToPermissionRoles.java +++ b/scm-webapp/src/main/java/sonia/scm/update/repository/MigrateVerbsToPermissionRoles.java @@ -117,7 +117,7 @@ public class MigrateVerbsToPermissionRoles extends RepositoryUpdates.RepositoryU @Override public Version getTargetVersion() { - return Version.parse("1"); + return Version.parse("2.0.0"); } @XmlAccessorType(XmlAccessType.FIELD) From bb38e9e2b93a437d50bd783da3b2405065a791b0 Mon Sep 17 00:00:00 2001 From: Florian Scholdei Date: Wed, 12 Jun 2019 15:22:10 +0200 Subject: [PATCH 03/13] add icon ui-component --- .../packages/ui-components/src/Icon.js | 25 +++++++++++++++++++ .../packages/ui-components/src/index.js | 1 + 2 files changed, 26 insertions(+) create mode 100644 scm-ui-components/packages/ui-components/src/Icon.js diff --git a/scm-ui-components/packages/ui-components/src/Icon.js b/scm-ui-components/packages/ui-components/src/Icon.js new file mode 100644 index 0000000000..b3b9a9b4c2 --- /dev/null +++ b/scm-ui-components/packages/ui-components/src/Icon.js @@ -0,0 +1,25 @@ +//@flow +import React from "react"; +import classNames from "classnames"; + +type Props = { + title?: string, + name: string +} + +export default class Icon extends React.Component { + + render() { + const { title, name } = this.props; + if(title) { + return ( + + ); + } + return ( + + ); + } + +} + diff --git a/scm-ui-components/packages/ui-components/src/index.js b/scm-ui-components/packages/ui-components/src/index.js index 954b0b0955..82780c94eb 100644 --- a/scm-ui-components/packages/ui-components/src/index.js +++ b/scm-ui-components/packages/ui-components/src/index.js @@ -9,6 +9,7 @@ export { validation, urls, repositories }; export { default as DateFromNow } from "./DateFromNow.js"; export { default as ErrorNotification } from "./ErrorNotification.js"; export { default as ErrorPage } from "./ErrorPage.js"; +export { default as Icon } from "./Icon.js"; export { default as Image } from "./Image.js"; export { default as Loading } from "./Loading.js"; export { default as Logo } from "./Logo.js"; From 467d0fd4aeac9e3d02e220017f4b73a62cd8927d Mon Sep 17 00:00:00 2001 From: Florian Scholdei Date: Wed, 12 Jun 2019 15:23:11 +0200 Subject: [PATCH 04/13] fix vertical alignment of icons in navigation --- .../packages/ui-components/src/navigation/NavLink.js | 3 ++- .../packages/ui-components/src/navigation/SubNavigation.js | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/scm-ui-components/packages/ui-components/src/navigation/NavLink.js b/scm-ui-components/packages/ui-components/src/navigation/NavLink.js index 98c3138a8f..e1236371a8 100644 --- a/scm-ui-components/packages/ui-components/src/navigation/NavLink.js +++ b/scm-ui-components/packages/ui-components/src/navigation/NavLink.js @@ -1,5 +1,6 @@ //@flow import * as React from "react"; +import classNames from "classnames"; import {Link, Route} from "react-router-dom"; // TODO mostly copy of PrimaryNavigationLink @@ -28,7 +29,7 @@ class NavLink extends React.Component { let showIcon = null; if (icon) { - showIcon = (<>{" "}); + showIcon = (<>{" "}); } return ( diff --git a/scm-ui-components/packages/ui-components/src/navigation/SubNavigation.js b/scm-ui-components/packages/ui-components/src/navigation/SubNavigation.js index 0a6612a173..cdf92d068d 100644 --- a/scm-ui-components/packages/ui-components/src/navigation/SubNavigation.js +++ b/scm-ui-components/packages/ui-components/src/navigation/SubNavigation.js @@ -1,6 +1,7 @@ //@flow import * as React from "react"; import { Link, Route } from "react-router-dom"; +import classNames from "classnames"; type Props = { to: string, @@ -37,7 +38,7 @@ class SubNavigation extends React.Component { return (
  • - {label} + {label} {children}
  • From d8d1513f3cdb8a43f6c4409feb82d60ba0b1d610 Mon Sep 17 00:00:00 2001 From: Florian Scholdei Date: Wed, 12 Jun 2019 15:24:32 +0200 Subject: [PATCH 05/13] add is-icon, is-darker class and colored border for card-table --- scm-ui/styles/scm.scss | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/scm-ui/styles/scm.scss b/scm-ui/styles/scm.scss index 09a4eb5c27..05bd1c2949 100644 --- a/scm-ui/styles/scm.scss +++ b/scm-ui/styles/scm.scss @@ -219,16 +219,23 @@ ul.is-separated { // card tables .card-table { border-collapse: separate; - border-spacing: 0px 5px; + border-spacing: 0 5px; tr { a { color: #363636; } + &.border-is-green td:first-child { + border-left-color: $green; + } + &.border-is-yellow td:first-child { + border-left-color: $yellow; + } &:hover { td { background-color: whitesmoke; - &:nth-child(4) { + + &.is-darker { background-color: #e1e1e1; } } @@ -238,13 +245,14 @@ ul.is-separated { } } td { - border-bottom: 1px solid whitesmoke; - background-color: #fafafa; padding: 1em 1.25em; + background-color: #fafafa; + border-bottom: 1px solid whitesmoke; + &:first-child { - border-left: 3px solid $mint; + border-left: 3px solid $grey; } - &:nth-child(4) { + &.is-darker { background-color: whitesmoke; } } @@ -318,6 +326,10 @@ form .field:not(.is-grouped) { } } +.is-icon { + color: $grey-light; +} + // label with help-icon compensation .label-icon-spacing { margin-top: 30px; From d50f6e0ec922a306c5c141f487388e6b383a9a08 Mon Sep 17 00:00:00 2001 From: Florian Scholdei Date: Wed, 12 Jun 2019 15:29:53 +0200 Subject: [PATCH 06/13] replace checkboxes with icons with hover --- scm-ui/public/locales/de/groups.json | 1 + scm-ui/public/locales/de/users.json | 1 + scm-ui/public/locales/en/groups.json | 1 + scm-ui/public/locales/en/users.json | 1 + scm-ui/src/groups/components/GroupForm.js | 10 ++++++- .../src/groups/components/table/GroupRow.js | 25 +++++++++++----- .../src/groups/components/table/GroupTable.js | 1 - .../containers/SinglePermission.js | 17 +++-------- scm-ui/src/users/components/UserForm.js | 10 ++++++- scm-ui/src/users/components/table/UserRow.js | 30 +++++++++++++------ .../src/users/components/table/UserTable.js | 1 - 11 files changed, 64 insertions(+), 34 deletions(-) diff --git a/scm-ui/public/locales/de/groups.json b/scm-ui/public/locales/de/groups.json index bb8eda1cf2..f1c5f295b1 100644 --- a/scm-ui/public/locales/de/groups.json +++ b/scm-ui/public/locales/de/groups.json @@ -6,6 +6,7 @@ "lastModified": "Zuletzt bearbeitet", "type": "Typ", "external": "Extern", + "internal": "Intern", "members": "Mitglieder" }, "groups": { diff --git a/scm-ui/public/locales/de/users.json b/scm-ui/public/locales/de/users.json index 37416c1e4f..25b6858c8b 100644 --- a/scm-ui/public/locales/de/users.json +++ b/scm-ui/public/locales/de/users.json @@ -5,6 +5,7 @@ "mail": "E-Mail", "password": "Passwort", "active": "Aktiv", + "inactive": "Inaktiv", "type": "Typ", "creationDate": "Erstellt", "lastModified": "Zuletzt bearbeitet" diff --git a/scm-ui/public/locales/en/groups.json b/scm-ui/public/locales/en/groups.json index aff350a458..f14347ed30 100644 --- a/scm-ui/public/locales/en/groups.json +++ b/scm-ui/public/locales/en/groups.json @@ -6,6 +6,7 @@ "lastModified": "Last Modified", "type": "Type", "external": "External", + "internal": "Internal", "members": "Members" }, "groups": { diff --git a/scm-ui/public/locales/en/users.json b/scm-ui/public/locales/en/users.json index d188f6221b..b492923a3f 100644 --- a/scm-ui/public/locales/en/users.json +++ b/scm-ui/public/locales/en/users.json @@ -5,6 +5,7 @@ "mail": "E-Mail", "password": "Password", "active": "Active", + "inactive": "Inactive", "type": "Type", "creationDate": "Creation Date", "lastModified": "Last Modified" diff --git a/scm-ui/src/groups/components/GroupForm.js b/scm-ui/src/groups/components/GroupForm.js index baa68de8ef..dae1229f10 100644 --- a/scm-ui/src/groups/components/GroupForm.js +++ b/scm-ui/src/groups/components/GroupForm.js @@ -9,6 +9,7 @@ import { InputField, SubmitButton, Textarea, + Icon, Checkbox } from "@scm-manager/ui-components"; import type { Group, SelectValue } from "@scm-manager/ui-types"; @@ -113,9 +114,16 @@ class GroupForm extends React.Component { if (this.isExistingGroup()) { return null; } + + const iconType = group && group.external ? ( + + ) : ( + + ); + return ( {iconType} {t("group.external")}} checked={group.external} helpText={t("groupForm.help.externalHelpText")} onChange={this.handleExternalChange} diff --git a/scm-ui/src/groups/components/table/GroupRow.js b/scm-ui/src/groups/components/table/GroupRow.js index 20bc90279e..509fe09ce4 100644 --- a/scm-ui/src/groups/components/table/GroupRow.js +++ b/scm-ui/src/groups/components/table/GroupRow.js @@ -1,29 +1,38 @@ // @flow import React from "react"; +import { translate } from "react-i18next"; import { Link } from "react-router-dom"; import type { Group } from "@scm-manager/ui-types"; -import { Checkbox } from "@scm-manager/ui-components"; +import { Icon } from "@scm-manager/ui-components"; type Props = { - group: Group + group: Group, + + // context props + t: string => string }; -export default class GroupRow extends React.Component { +class GroupRow extends React.Component { renderLink(to: string, label: string) { return {label}; } render() { - const { group } = this.props; + const { group, t } = this.props; const to = `/group/${group.name}`; + const iconType = group.external ? ( + + ) : ( + + ); + return ( - {this.renderLink(to, group.name)} + {iconType} {this.renderLink(to, group.name)} {group.description} - - - ); } } + +export default translate("groups")(GroupRow); diff --git a/scm-ui/src/groups/components/table/GroupTable.js b/scm-ui/src/groups/components/table/GroupTable.js index 85bcb813fc..c7b59b40a7 100644 --- a/scm-ui/src/groups/components/table/GroupTable.js +++ b/scm-ui/src/groups/components/table/GroupTable.js @@ -18,7 +18,6 @@ class GroupTable extends React.Component { {t("group.name")} {t("group.description")} - {t("group.external")} diff --git a/scm-ui/src/repos/permissions/containers/SinglePermission.js b/scm-ui/src/repos/permissions/containers/SinglePermission.js index bdb96de0bd..37f3436af3 100644 --- a/scm-ui/src/repos/permissions/containers/SinglePermission.js +++ b/scm-ui/src/repos/permissions/containers/SinglePermission.js @@ -11,7 +11,7 @@ import { } from "../modules/permissions"; import { connect } from "react-redux"; import type { History } from "history"; -import { Button } from "@scm-manager/ui-components"; +import { Button, Icon } from "@scm-manager/ui-components"; import DeletePermissionButton from "../components/buttons/DeletePermissionButton"; import RoleSelector from "../components/RoleSelector"; import AdvancedPermissionsDialog from "./AdvancedPermissionsDialog"; @@ -49,9 +49,6 @@ type State = { }; const styles = { - iconColor: { - color: "#9a9a9a" - }, centerMiddle: { display: "table-cell", verticalAlign: "middle !important" @@ -148,15 +145,9 @@ class SinglePermission extends React.Component { const iconType = permission && permission.groupPermission ? ( - + ) : ( - + ); return ( @@ -171,7 +162,7 @@ class SinglePermission extends React.Component { action={this.handleDetailedPermissionsPressed} /> - + { // edit existing user subtitle = ; } + + const iconType = user && user.active ? ( + + ) : ( + + ); + return ( <> {subtitle} @@ -167,7 +175,7 @@ class UserForm extends React.Component {
    {passwordChangeField} {iconType} {t("user.active")}} onChange={this.handleActiveChange} checked={user ? user.active : false} helpText={t("help.activeHelpText")} diff --git a/scm-ui/src/users/components/table/UserRow.js b/scm-ui/src/users/components/table/UserRow.js index 9cb55295fb..03c7f1ebaa 100644 --- a/scm-ui/src/users/components/table/UserRow.js +++ b/scm-ui/src/users/components/table/UserRow.js @@ -1,31 +1,43 @@ // @flow import React from "react"; +import { translate } from "react-i18next"; import { Link } from "react-router-dom"; import type { User } from "@scm-manager/ui-types"; +import { Icon } from "@scm-manager/ui-components"; type Props = { - user: User + user: User, + + // context props + t: string => string }; -export default class UserRow extends React.Component { +class UserRow extends React.Component { renderLink(to: string, label: string) { return {label}; } render() { - const { user } = this.props; + const { user, t } = this.props; const to = `/user/${user.name}`; + const iconType = user.active ? ( + + ) : ( + + ); + return ( - - {this.renderLink(to, user.name)} - {this.renderLink(to, user.displayName)} + + {iconType} {this.renderLink(to, user.name)} + + {this.renderLink(to, user.displayName)} + {user.mail} - - - ); } } + +export default translate("users")(UserRow); diff --git a/scm-ui/src/users/components/table/UserTable.js b/scm-ui/src/users/components/table/UserTable.js index 8c012f11f4..ab29dca9e7 100644 --- a/scm-ui/src/users/components/table/UserTable.js +++ b/scm-ui/src/users/components/table/UserTable.js @@ -19,7 +19,6 @@ class UserTable extends React.Component { {t("user.name")} {t("user.displayName")} {t("user.mail")} - {t("user.active")} From 5f435a524e4930a894ef9cb823dc8a68a5a622f8 Mon Sep 17 00:00:00 2001 From: Florian Scholdei Date: Wed, 12 Jun 2019 16:27:16 +0200 Subject: [PATCH 07/13] clarifiy checkbox --- .../packages/ui-components/src/forms/Radio.js | 25 +++++++++---------- scm-ui/src/groups/components/GroupForm.js | 6 ++--- scm-ui/src/users/components/UserForm.js | 20 ++++++++++----- 3 files changed, 29 insertions(+), 22 deletions(-) diff --git a/scm-ui-components/packages/ui-components/src/forms/Radio.js b/scm-ui-components/packages/ui-components/src/forms/Radio.js index 6460e67070..7e61b25ec0 100644 --- a/scm-ui-components/packages/ui-components/src/forms/Radio.js +++ b/scm-ui-components/packages/ui-components/src/forms/Radio.js @@ -22,19 +22,18 @@ class Radio extends React.Component { render() { return ( - - + ); } } diff --git a/scm-ui/src/groups/components/GroupForm.js b/scm-ui/src/groups/components/GroupForm.js index dae1229f10..07e9085af7 100644 --- a/scm-ui/src/groups/components/GroupForm.js +++ b/scm-ui/src/groups/components/GroupForm.js @@ -116,14 +116,14 @@ class GroupForm extends React.Component { } const iconType = group && group.external ? ( - + <>{t("group.external")} ) : ( - + <>{t("group.internal")} ); return ( {iconType} {t("group.external")}} + label={iconType} checked={group.external} helpText={t("groupForm.help.externalHelpText")} onChange={this.handleExternalChange} diff --git a/scm-ui/src/users/components/UserForm.js b/scm-ui/src/users/components/UserForm.js index 48314bef47..a6de2e7bca 100644 --- a/scm-ui/src/users/components/UserForm.js +++ b/scm-ui/src/users/components/UserForm.js @@ -17,6 +17,8 @@ type Props = { submitForm: User => void, user?: User, loading?: boolean, + + // context props t: string => string }; @@ -138,11 +140,17 @@ class UserForm extends React.Component { subtitle = ; } - const iconType = user && user.active ? ( - - ) : ( - - ); + const iconType = + user && user.active ? ( + <> + {t("user.active")} + + ) : ( + <> + {t("user.inactive")}{" "} + + + ); return ( <> @@ -175,7 +183,7 @@ class UserForm extends React.Component {
    {passwordChangeField} {iconType} {t("user.active")}} + label={iconType} onChange={this.handleActiveChange} checked={user ? user.active : false} helpText={t("help.activeHelpText")} From 2727679f6e7cc7bd8bec4bc94aee2030b68f79ad Mon Sep 17 00:00:00 2001 From: Florian Scholdei Date: Wed, 12 Jun 2019 16:35:08 +0200 Subject: [PATCH 08/13] rename local const --- scm-ui/src/groups/components/GroupForm.js | 4 ++-- scm-ui/src/users/components/UserForm.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scm-ui/src/groups/components/GroupForm.js b/scm-ui/src/groups/components/GroupForm.js index 07e9085af7..637be3faff 100644 --- a/scm-ui/src/groups/components/GroupForm.js +++ b/scm-ui/src/groups/components/GroupForm.js @@ -115,7 +115,7 @@ class GroupForm extends React.Component { return null; } - const iconType = group && group.external ? ( + const label = group && group.external ? ( <>{t("group.external")} ) : ( <>{t("group.internal")} @@ -123,7 +123,7 @@ class GroupForm extends React.Component { return ( { subtitle = ; } - const iconType = + const label = user && user.active ? ( <> {t("user.active")} @@ -183,7 +183,7 @@ class UserForm extends React.Component {
    {passwordChangeField} Date: Thu, 13 Jun 2019 06:24:35 +0200 Subject: [PATCH 09/13] Heed peer review --- .../MigrateVerbsToPermissionRoles.java | 21 +++++++++++++++---- .../update/repository/RepositoryUpdates.java | 10 --------- .../MigrateVerbsToPermissionRolesTest.java | 2 +- 3 files changed, 18 insertions(+), 15 deletions(-) delete mode 100644 scm-webapp/src/main/java/sonia/scm/update/repository/RepositoryUpdates.java diff --git a/scm-webapp/src/main/java/sonia/scm/update/repository/MigrateVerbsToPermissionRoles.java b/scm-webapp/src/main/java/sonia/scm/update/repository/MigrateVerbsToPermissionRoles.java index 56227b7b13..b8b00e1554 100644 --- a/scm-webapp/src/main/java/sonia/scm/update/repository/MigrateVerbsToPermissionRoles.java +++ b/scm-webapp/src/main/java/sonia/scm/update/repository/MigrateVerbsToPermissionRoles.java @@ -30,17 +30,19 @@ import java.util.Optional; import java.util.Set; @Extension -public class MigrateVerbsToPermissionRoles extends RepositoryUpdates.RepositoryUpdateType implements UpdateStep { +public class MigrateVerbsToPermissionRoles implements UpdateStep { public static final Logger LOG = LoggerFactory.getLogger(MigrateVerbsToPermissionRoles.class); private final SingleRepositoryUpdateProcessor updateProcessor; private final SystemRepositoryPermissionProvider systemRepositoryPermissionProvider; + private final JAXBContext jaxbContext; @Inject public MigrateVerbsToPermissionRoles(SingleRepositoryUpdateProcessor updateProcessor, SystemRepositoryPermissionProvider systemRepositoryPermissionProvider) { this.updateProcessor = updateProcessor; this.systemRepositoryPermissionProvider = systemRepositoryPermissionProvider; + jaxbContext = createJAXBContext(); } @Override @@ -57,7 +59,6 @@ public class MigrateVerbsToPermissionRoles extends RepositoryUpdates.RepositoryU private void writeNewRepository(Path path, Repository newRepository) { try { - JAXBContext jaxbContext = JAXBContext.newInstance(Repository.class); Marshaller marshaller = jaxbContext.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); marshaller.marshal(newRepository, path.resolve("metadata.xml").toFile()); @@ -68,7 +69,6 @@ public class MigrateVerbsToPermissionRoles extends RepositoryUpdates.RepositoryU private OldRepository readOldRepository(Path path) { try { - JAXBContext jaxbContext = JAXBContext.newInstance(OldRepository.class); return (OldRepository) jaxbContext.createUnmarshaller().unmarshal(path.resolve("metadata.xml").toFile()); } catch (JAXBException e) { throw new UpdateException("could not read old repository structure", e); @@ -115,9 +115,22 @@ public class MigrateVerbsToPermissionRoles extends RepositoryUpdates.RepositoryU return verbs.size() == r.getVerbs().size() && r.getVerbs().containsAll(verbs); } + private JAXBContext createJAXBContext() { + try { + return JAXBContext.newInstance(Repository.class); + } catch (JAXBException e) { + throw new UpdateException("could not create XML marshaller", e); + } + } + @Override public Version getTargetVersion() { - return Version.parse("2.0.0"); + return Version.parse("2.0.2"); + } + + @Override + public String getAffectedDataType() { + return "sonia.scm.repository.xml"; } @XmlAccessorType(XmlAccessType.FIELD) diff --git a/scm-webapp/src/main/java/sonia/scm/update/repository/RepositoryUpdates.java b/scm-webapp/src/main/java/sonia/scm/update/repository/RepositoryUpdates.java deleted file mode 100644 index 864f9aeb65..0000000000 --- a/scm-webapp/src/main/java/sonia/scm/update/repository/RepositoryUpdates.java +++ /dev/null @@ -1,10 +0,0 @@ -package sonia.scm.update.repository; - -public class RepositoryUpdates { - - static class RepositoryUpdateType { - public String getAffectedDataType() { - return "repository"; - } - } -} diff --git a/scm-webapp/src/test/java/sonia/scm/update/repository/MigrateVerbsToPermissionRolesTest.java b/scm-webapp/src/test/java/sonia/scm/update/repository/MigrateVerbsToPermissionRolesTest.java index 536af6e865..8d4ceb1a14 100644 --- a/scm-webapp/src/test/java/sonia/scm/update/repository/MigrateVerbsToPermissionRolesTest.java +++ b/scm-webapp/src/test/java/sonia/scm/update/repository/MigrateVerbsToPermissionRolesTest.java @@ -52,7 +52,7 @@ class MigrateVerbsToPermissionRolesTest { } @Test - void x(@TempDirectory.TempDir Path tempDir) throws IOException { + void shouldUpdateToRolesIfPossible(@TempDirectory.TempDir Path tempDir) throws IOException { migration.doUpdate(); List newMetadata = Files.readAllLines(tempDir.resolve("metadata.xml")); From a14a2060b6a961afc0035a629f0a71c7c5785b40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Thu, 13 Jun 2019 06:41:46 +0200 Subject: [PATCH 10/13] Fix context --- .../repository/MigrateVerbsToPermissionRoles.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/scm-webapp/src/main/java/sonia/scm/update/repository/MigrateVerbsToPermissionRoles.java b/scm-webapp/src/main/java/sonia/scm/update/repository/MigrateVerbsToPermissionRoles.java index b8b00e1554..0b96a58385 100644 --- a/scm-webapp/src/main/java/sonia/scm/update/repository/MigrateVerbsToPermissionRoles.java +++ b/scm-webapp/src/main/java/sonia/scm/update/repository/MigrateVerbsToPermissionRoles.java @@ -36,13 +36,15 @@ public class MigrateVerbsToPermissionRoles implements UpdateStep { private final SingleRepositoryUpdateProcessor updateProcessor; private final SystemRepositoryPermissionProvider systemRepositoryPermissionProvider; - private final JAXBContext jaxbContext; + private final JAXBContext jaxbContextNewRepository; + private final JAXBContext jaxbContextOldRepository; @Inject public MigrateVerbsToPermissionRoles(SingleRepositoryUpdateProcessor updateProcessor, SystemRepositoryPermissionProvider systemRepositoryPermissionProvider) { this.updateProcessor = updateProcessor; this.systemRepositoryPermissionProvider = systemRepositoryPermissionProvider; - jaxbContext = createJAXBContext(); + jaxbContextNewRepository = createJAXBContext(Repository.class); + jaxbContextOldRepository = createJAXBContext(OldRepository.class); } @Override @@ -59,7 +61,7 @@ public class MigrateVerbsToPermissionRoles implements UpdateStep { private void writeNewRepository(Path path, Repository newRepository) { try { - Marshaller marshaller = jaxbContext.createMarshaller(); + Marshaller marshaller = jaxbContextNewRepository.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); marshaller.marshal(newRepository, path.resolve("metadata.xml").toFile()); } catch (JAXBException e) { @@ -69,7 +71,7 @@ public class MigrateVerbsToPermissionRoles implements UpdateStep { private OldRepository readOldRepository(Path path) { try { - return (OldRepository) jaxbContext.createUnmarshaller().unmarshal(path.resolve("metadata.xml").toFile()); + return (OldRepository) jaxbContextOldRepository.createUnmarshaller().unmarshal(path.resolve("metadata.xml").toFile()); } catch (JAXBException e) { throw new UpdateException("could not read old repository structure", e); } @@ -115,9 +117,9 @@ public class MigrateVerbsToPermissionRoles implements UpdateStep { return verbs.size() == r.getVerbs().size() && r.getVerbs().containsAll(verbs); } - private JAXBContext createJAXBContext() { + private JAXBContext createJAXBContext(Class clazz) { try { - return JAXBContext.newInstance(Repository.class); + return JAXBContext.newInstance(clazz); } catch (JAXBException e) { throw new UpdateException("could not create XML marshaller", e); } From e266b1edb539103782b62077576f322e3c1197a7 Mon Sep 17 00:00:00 2001 From: Rene Pfeuffer Date: Thu, 13 Jun 2019 06:07:26 +0000 Subject: [PATCH 11/13] Close branch feature/migrate_custom_roles From cd99402f78bd98fce3fe24fad1800c972440654b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Thu, 13 Jun 2019 09:38:44 +0200 Subject: [PATCH 12/13] Revert create forms --- scm-ui/src/groups/components/GroupForm.js | 12 ++---------- scm-ui/src/users/components/UserForm.js | 19 +------------------ 2 files changed, 3 insertions(+), 28 deletions(-) diff --git a/scm-ui/src/groups/components/GroupForm.js b/scm-ui/src/groups/components/GroupForm.js index 637be3faff..04a5e6f35f 100644 --- a/scm-ui/src/groups/components/GroupForm.js +++ b/scm-ui/src/groups/components/GroupForm.js @@ -9,7 +9,6 @@ import { InputField, SubmitButton, Textarea, - Icon, Checkbox } from "@scm-manager/ui-components"; import type { Group, SelectValue } from "@scm-manager/ui-types"; @@ -114,16 +113,9 @@ class GroupForm extends React.Component { if (this.isExistingGroup()) { return null; } - - const label = group && group.external ? ( - <>{t("group.external")} - ) : ( - <>{t("group.internal")} - ); - return ( { ); }; - isExistingGroup = () => !! this.props.group; + isExistingGroup = () => !!this.props.group; render() { const { loading, t } = this.props; diff --git a/scm-ui/src/users/components/UserForm.js b/scm-ui/src/users/components/UserForm.js index 02dc310c03..c473e92322 100644 --- a/scm-ui/src/users/components/UserForm.js +++ b/scm-ui/src/users/components/UserForm.js @@ -5,7 +5,6 @@ import type { User } from "@scm-manager/ui-types"; import { Subtitle, Checkbox, - Icon, InputField, PasswordConfirmation, SubmitButton, @@ -17,8 +16,6 @@ type Props = { submitForm: User => void, user?: User, loading?: boolean, - - // context props t: string => string }; @@ -83,7 +80,6 @@ class UserForm extends React.Component { return ( this.props.user.displayName === user.displayName && this.props.user.mail === user.mail && - this.props.user.admin === user.admin && this.props.user.active === user.active ); } else { @@ -139,19 +135,6 @@ class UserForm extends React.Component { // edit existing user subtitle = ; } - - const label = - user && user.active ? ( - <> - {t("user.active")} - - ) : ( - <> - {t("user.inactive")}{" "} - - - ); - return ( <> {subtitle} @@ -183,7 +166,7 @@ class UserForm extends React.Component {
    {passwordChangeField} Date: Thu, 13 Jun 2019 08:03:10 +0000 Subject: [PATCH 13/13] Close branch feature/prettier_tables