From 68302fef926e912b528f47b0453271c47200cba3 Mon Sep 17 00:00:00 2001 From: Florian Scholdei Date: Fri, 18 Jan 2019 09:42:47 +0100 Subject: [PATCH 001/120] repository navigation --- scm-ui/public/locales/en/repos.json | 12 +++++------- scm-ui/src/repos/components/PermissionsNavLink.js | 2 +- scm-ui/src/repos/containers/RepositoryRoot.js | 13 ++++--------- 3 files changed, 10 insertions(+), 17 deletions(-) diff --git a/scm-ui/public/locales/en/repos.json b/scm-ui/public/locales/en/repos.json index d6cdaa1d8d..a1b006a1da 100644 --- a/scm-ui/public/locales/en/repos.json +++ b/scm-ui/public/locales/en/repos.json @@ -19,13 +19,11 @@ "repository-root": { "error-title": "Error", "error-subtitle": "Unknown repository error", - "actions-label": "Actions", - "back-label": "Back", - "navigation-label": "Navigation", - "history": "Commits", - "information": "Information", - "permissions": "Permissions", - "sources": "Sources" + "navigationLabel": "Repository Navigation", + "historyNavLink": "Commits", + "informationNavLink": "Information", + "permissionsNavLink": "Permissions", + "sourcesNavLink": "Sources" }, "create": { "title": "Create Repository", diff --git a/scm-ui/src/repos/components/PermissionsNavLink.js b/scm-ui/src/repos/components/PermissionsNavLink.js index cb6d0e0723..937980fd3d 100644 --- a/scm-ui/src/repos/components/PermissionsNavLink.js +++ b/scm-ui/src/repos/components/PermissionsNavLink.js @@ -20,7 +20,7 @@ class PermissionsNavLink extends React.Component { } const { permissionUrl, t } = this.props; return ( - + ); } } diff --git a/scm-ui/src/repos/containers/RepositoryRoot.js b/scm-ui/src/repos/containers/RepositoryRoot.js index e73348babc..bdfa74d5d7 100644 --- a/scm-ui/src/repos/containers/RepositoryRoot.js +++ b/scm-ui/src/repos/containers/RepositoryRoot.js @@ -168,13 +168,13 @@ class RepositoryRoot extends React.Component {
-
- +
+ @@ -182,24 +182,19 @@ class RepositoryRoot extends React.Component { repository={repository} linkName="sources" to={`${url}/sources`} - label={t("repository-root.sources")} + label={t("repository-root.sourcesNavLink")} activeOnlyWhenExact={false} /> -
-
- - -
From af164d31f435f1ff8b54d589d84af9adc8f67fe5 Mon Sep 17 00:00:00 2001 From: Florian Scholdei Date: Fri, 18 Jan 2019 09:51:27 +0100 Subject: [PATCH 002/120] updated user navigation --- scm-ui/public/locales/en/users.json | 14 ++++---------- .../users/components/navLinks/EditUserNavLink.js | 2 +- .../components/navLinks/SetPasswordNavLink.js | 2 +- scm-ui/src/users/containers/SingleUser.js | 8 ++------ 4 files changed, 8 insertions(+), 18 deletions(-) diff --git a/scm-ui/public/locales/en/users.json b/scm-ui/public/locales/en/users.json index 2a9ee7b79d..e7a637bc2b 100644 --- a/scm-ui/public/locales/en/users.json +++ b/scm-ui/public/locales/en/users.json @@ -26,12 +26,6 @@ "cancel": "No" } }, - "edit-user-button": { - "label": "Edit" - }, - "set-password-button": { - "label": "Set password" - }, "user-form": { "submit": "Submit" }, @@ -42,10 +36,10 @@ "single-user": { "error-title": "Error", "error-subtitle": "Unknown user error", - "navigation-label": "Navigation", - "actions-label": "Actions", - "information-label": "Information", - "back-label": "Back" + "navigationLabel": "User Navigation", + "informationNavLink": "Information", + "editNavLink": "Edit", + "setPasswordNavLink": "Set password" }, "validation": { "mail-invalid": "This email is invalid", diff --git a/scm-ui/src/users/components/navLinks/EditUserNavLink.js b/scm-ui/src/users/components/navLinks/EditUserNavLink.js index 9999428212..3689b445d8 100644 --- a/scm-ui/src/users/components/navLinks/EditUserNavLink.js +++ b/scm-ui/src/users/components/navLinks/EditUserNavLink.js @@ -17,7 +17,7 @@ class EditUserNavLink extends React.Component { if (!this.isEditable()) { return null; } - return ; + return ; } isEditable = () => { diff --git a/scm-ui/src/users/components/navLinks/SetPasswordNavLink.js b/scm-ui/src/users/components/navLinks/SetPasswordNavLink.js index 43b7a4b5a4..e9f28d841e 100644 --- a/scm-ui/src/users/components/navLinks/SetPasswordNavLink.js +++ b/scm-ui/src/users/components/navLinks/SetPasswordNavLink.js @@ -17,7 +17,7 @@ class ChangePasswordNavLink extends React.Component { if (!this.hasPermissionToSetPassword()) { return null; } - return ; + return ; } hasPermissionToSetPassword = () => { diff --git a/scm-ui/src/users/containers/SingleUser.js b/scm-ui/src/users/containers/SingleUser.js index 5f20598962..c980585141 100644 --- a/scm-ui/src/users/containers/SingleUser.js +++ b/scm-ui/src/users/containers/SingleUser.js @@ -109,10 +109,10 @@ class SingleUser extends React.Component {
-
+
{ passwordUrl={`${url}/password`} />
-
- - -
From 3318e5291e67413cf205eac2183ee963a466ba32 Mon Sep 17 00:00:00 2001 From: Florian Scholdei Date: Fri, 18 Jan 2019 10:10:03 +0100 Subject: [PATCH 003/120] updated config navigation --- scm-ui/public/locales/en/config.json | 10 +- scm-ui/src/config/containers/Config.js | 173 ++++++++++--------- scm-ui/src/config/containers/GlobalConfig.js | 6 +- 3 files changed, 94 insertions(+), 95 deletions(-) diff --git a/scm-ui/public/locales/en/config.json b/scm-ui/public/locales/en/config.json index 1a33da8c8b..436b96cab8 100644 --- a/scm-ui/public/locales/en/config.json +++ b/scm-ui/public/locales/en/config.json @@ -1,12 +1,10 @@ { "config": { - "navigation-title": "Navigation" - }, - "global-config": { + "navigationLabel": "Configuration Navigation", + "globalConfigurationNavLink": "Global Configuration", "title": "Configuration", - "navigation-label": "Global Configuration", - "error-title": "Error", - "error-subtitle": "Unknown Config Error" + "errorTitle": "Error", + "errorSubtitle": "Unknown Config Error" }, "config-form": { "submit": "Submit", diff --git a/scm-ui/src/config/containers/Config.js b/scm-ui/src/config/containers/Config.js index 60dc9bb75f..04de525c95 100644 --- a/scm-ui/src/config/containers/Config.js +++ b/scm-ui/src/config/containers/Config.js @@ -1,86 +1,87 @@ -// @flow -import React from "react"; -import { translate } from "react-i18next"; -import { Route } from "react-router"; -import { ExtensionPoint } from "@scm-manager/ui-extensions"; - -import type { Links } from "@scm-manager/ui-types"; -import { Page, Navigation, NavLink, Section } from "@scm-manager/ui-components"; -import GlobalConfig from "./GlobalConfig"; -import type { History } from "history"; -import {connect} from "react-redux"; -import {compose} from "redux"; -import { getLinks } from "../../modules/indexResource"; - -type Props = { - links: Links, - - // context objects - t: string => string, - match: any, - history: History -}; - -class Config extends React.Component { - stripEndingSlash = (url: string) => { - if (url.endsWith("/")) { - return url.substring(0, url.length - 2); - } - return url; - }; - - matchedUrl = () => { - return this.stripEndingSlash(this.props.match.url); - }; - - render() { - const { links, t } = this.props; - - const url = this.matchedUrl(); - const extensionProps = { - links, - url - }; - - return ( - -
-
- - -
-
- -
- - -
-
-
-
-
- ); - } -} - -const mapStateToProps = (state: any) => { - const links = getLinks(state); - return { - links - }; -}; - -export default compose( - connect(mapStateToProps), - translate("config") -)(Config); - +// @flow +import React from "react"; +import { translate } from "react-i18next"; +import { Route } from "react-router"; +import { ExtensionPoint } from "@scm-manager/ui-extensions"; + +import type { Links } from "@scm-manager/ui-types"; +import { Page, Navigation, NavLink, Section } from "@scm-manager/ui-components"; +import GlobalConfig from "./GlobalConfig"; +import type { History } from "history"; +import { connect } from "react-redux"; +import { compose } from "redux"; +import { getLinks } from "../../modules/indexResource"; + +type Props = { + links: Links, + + // context objects + t: string => string, + match: any, + history: History +}; + +class Config extends React.Component { + stripEndingSlash = (url: string) => { + if (url.endsWith("/")) { + return url.substring(0, url.length - 2); + } + return url; + }; + + matchedUrl = () => { + return this.stripEndingSlash(this.props.match.url); + }; + + render() { + const { links, t } = this.props; + + const url = this.matchedUrl(); + const extensionProps = { + links, + url + }; + + return ( + +
+
+ + +
+
+ +
+ + +
+
+
+
+
+ ); + } +} + +const mapStateToProps = (state: any) => { + const links = getLinks(state); + return { + links + }; +}; + +export default compose( + connect(mapStateToProps), + translate("config") +)(Config); diff --git a/scm-ui/src/config/containers/GlobalConfig.js b/scm-ui/src/config/containers/GlobalConfig.js index 71be3fdd7f..eac8e27bee 100644 --- a/scm-ui/src/config/containers/GlobalConfig.js +++ b/scm-ui/src/config/containers/GlobalConfig.js @@ -78,8 +78,8 @@ class GlobalConfig extends React.Component { if (error) { return ( @@ -91,7 +91,7 @@ class GlobalConfig extends React.Component { return (
- + <Title title={t("config.title")} /> {this.renderConfigChangedNotification()} <ConfigForm submitForm={config => this.modifyConfig(config)} From f96a7b6ca509e81446f40a69ae7655bd776466a7 Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Fri, 18 Jan 2019 10:16:45 +0100 Subject: [PATCH 004/120] updated group navigation --- scm-ui/public/locales/en/groups.json | 10 ++++------ scm-ui/src/groups/containers/SingleGroup.js | 16 ++++------------ 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/scm-ui/public/locales/en/groups.json b/scm-ui/public/locales/en/groups.json index f1ebb95e18..66075be273 100644 --- a/scm-ui/public/locales/en/groups.json +++ b/scm-ui/public/locales/en/groups.json @@ -12,12 +12,10 @@ "subtitle": "Create, read, update and delete groups" }, "single-group": { - "error-title": "Error", - "error-subtitle": "Unknown group error", - "navigation-label": "Navigation", - "actions-label": "Actions", - "information-label": "Information", - "back-label": "Back" + "navigationLabel": "Group Navigation", + "informationNavLink": "Information", + "errorTitle": "Error", + "errorSubtitle": "Unknown group error" }, "add-group": { "title": "Create Group", diff --git a/scm-ui/src/groups/containers/SingleGroup.js b/scm-ui/src/groups/containers/SingleGroup.js index 1dd4aa569f..ec62333f6f 100644 --- a/scm-ui/src/groups/containers/SingleGroup.js +++ b/scm-ui/src/groups/containers/SingleGroup.js @@ -75,8 +75,8 @@ class SingleGroup extends React.Component<Props> { if (error) { return ( <ErrorPage - title={t("single-group.error-title")} - subtitle={t("single-group.error-subtitle")} + title={t("single-group.errorTitle")} + subtitle={t("single-group.errorSubtitle")} error={error} /> ); @@ -105,20 +105,12 @@ class SingleGroup extends React.Component<Props> { </div> <div className="column"> <Navigation> - <Section label={t("single-group.navigation-label")}> + <Section label={t("single-group.navigationLabel")}> <NavLink to={`${url}`} - label={t("single-group.information-label")} + label={t("single-group.informationNavLink")} /> </Section> - <Section label={t("single-group.actions-label")}> - <DeleteGroupNavLink - group={group} - deleteGroup={this.deleteGroup} - /> - <EditGroupNavLink group={group} editUrl={`${url}/edit`} /> - <NavLink to="/groups" label={t("single-group.back-label")} /> - </Section> </Navigation> </div> </div> From 03813b40af165195d9f8bda08458f3a95c91fbc3 Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Fri, 18 Jan 2019 10:25:35 +0100 Subject: [PATCH 005/120] updated profile navigation + some naming changes --- scm-ui/public/locales/en/commons.json | 7 +++---- scm-ui/public/locales/en/repos.json | 6 +++--- scm-ui/public/locales/en/users.json | 6 +++--- scm-ui/src/containers/Profile.js | 8 +++----- scm-ui/src/repos/containers/RepositoryRoot.js | 4 ++-- scm-ui/src/users/containers/SingleUser.js | 4 ++-- 6 files changed, 16 insertions(+), 19 deletions(-) diff --git a/scm-ui/public/locales/en/commons.json b/scm-ui/public/locales/en/commons.json index 3196f3a328..ca4a1c87bf 100644 --- a/scm-ui/public/locales/en/commons.json +++ b/scm-ui/public/locales/en/commons.json @@ -43,13 +43,12 @@ "previous": "Previous" }, "profile": { - "navigation-label": "Navigation", - "actions-label": "Actions", + "navigationLabel": "Profile Navigation", + "informationNavLink": "Information", + "changePasswordNavLink": "Change password", "username": "Username", "displayName": "Display Name", "mail": "E-Mail", - "information": "Information", - "change-password": "Change password", "error-title": "Error", "error-subtitle": "Cannot display profile", "error": "Error", diff --git a/scm-ui/public/locales/en/repos.json b/scm-ui/public/locales/en/repos.json index a1b006a1da..68f5b4a53b 100644 --- a/scm-ui/public/locales/en/repos.json +++ b/scm-ui/public/locales/en/repos.json @@ -17,13 +17,13 @@ "create-button": "Create" }, "repository-root": { - "error-title": "Error", - "error-subtitle": "Unknown repository error", "navigationLabel": "Repository Navigation", "historyNavLink": "Commits", "informationNavLink": "Information", "permissionsNavLink": "Permissions", - "sourcesNavLink": "Sources" + "sourcesNavLink": "Sources", + "errorTitle": "Error", + "errorSubtitle": "Unknown repository error" }, "create": { "title": "Create Repository", diff --git a/scm-ui/public/locales/en/users.json b/scm-ui/public/locales/en/users.json index e7a637bc2b..e388081504 100644 --- a/scm-ui/public/locales/en/users.json +++ b/scm-ui/public/locales/en/users.json @@ -34,12 +34,12 @@ "subtitle": "Create a new user" }, "single-user": { - "error-title": "Error", - "error-subtitle": "Unknown user error", "navigationLabel": "User Navigation", "informationNavLink": "Information", "editNavLink": "Edit", - "setPasswordNavLink": "Set password" + "setPasswordNavLink": "Set password", + "errorTitle": "Error", + "errorSubtitle": "Unknown user error" }, "validation": { "mail-invalid": "This email is invalid", diff --git a/scm-ui/src/containers/Profile.js b/scm-ui/src/containers/Profile.js index b40f5f3ee0..b28a8cd705 100644 --- a/scm-ui/src/containers/Profile.js +++ b/scm-ui/src/containers/Profile.js @@ -69,13 +69,11 @@ class Profile extends React.Component<Props, State> { </div> <div className="column"> <Navigation> - <Section label={t("profile.navigation-label")}> - <NavLink to={`${url}`} label={t("profile.information")} /> - </Section> - <Section label={t("profile.actions-label")}> + <Section label={t("profile.navigationLabel")}> + <NavLink to={`${url}`} label={t("profile.informationNavLink")} /> <NavLink to={`${url}/password`} - label={t("profile.change-password")} + label={t("profile.changePasswordNavLink")} /> </Section> </Navigation> diff --git a/scm-ui/src/repos/containers/RepositoryRoot.js b/scm-ui/src/repos/containers/RepositoryRoot.js index bdfa74d5d7..9fcae12c53 100644 --- a/scm-ui/src/repos/containers/RepositoryRoot.js +++ b/scm-ui/src/repos/containers/RepositoryRoot.js @@ -80,8 +80,8 @@ class RepositoryRoot extends React.Component<Props> { if (error) { return ( <ErrorPage - title={t("repository-root.error-title")} - subtitle={t("repository-root.error-subtitle")} + title={t("repository-root.errorTitle")} + subtitle={t("repository-root.errorSubtitle")} error={error} /> ); diff --git a/scm-ui/src/users/containers/SingleUser.js b/scm-ui/src/users/containers/SingleUser.js index c980585141..1662d213c5 100644 --- a/scm-ui/src/users/containers/SingleUser.js +++ b/scm-ui/src/users/containers/SingleUser.js @@ -80,8 +80,8 @@ class SingleUser extends React.Component<Props> { if (error) { return ( <ErrorPage - title={t("single-user.error-title")} - subtitle={t("single-user.error-subtitle")} + title={t("single-user.errorTitle")} + subtitle={t("single-user.errorSubtitle")} error={error} /> ); From 83321a195e63fb134ccfbfe0ccb2fe75f40b7d1b Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Fri, 18 Jan 2019 10:32:47 +0100 Subject: [PATCH 006/120] undo changes from switching from wrong branch --- .../src/repos/changesets/ChangesetRow.js | 19 +++++-------------- scm-ui/public/locales/en/commons.json | 2 +- 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetRow.js b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetRow.js index d74e3631d1..ef9de9bfe5 100644 --- a/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetRow.js +++ b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetRow.js @@ -3,16 +3,15 @@ import React from "react"; import type { Changeset, Repository, Tag } from "@scm-manager/ui-types"; import classNames from "classnames"; -import { Interpolate, translate } from "react-i18next"; +import {Interpolate, translate} from "react-i18next"; import ChangesetId from "./ChangesetId"; import injectSheet from "react-jss"; -import { DateFromNow } from "../.."; +import {DateFromNow} from "../.."; import ChangesetAuthor from "./ChangesetAuthor"; import ChangesetTag from "./ChangesetTag"; -import { parseDescription } from "./changesets"; -import { AvatarWrapper, AvatarImage } from "../../avatar"; -import { ExtensionPoint } from "@scm-manager/ui-extensions"; +import {parseDescription} from "./changesets"; +import {AvatarWrapper, AvatarImage} from "../../avatar"; const styles = { pointer: { @@ -65,15 +64,7 @@ class ChangesetRow extends React.Component<Props> { <div className={classNames("media-content", classes.withOverflow)}> <div className="content"> <p className="is-ellipsis-overflow"> - <strong> - <ExtensionPoint - name="changesets.changeset.description" - props={{ changeset, value: description.title }} - renderAll={true} - > - {description.title} - </ExtensionPoint> - </strong> + <strong>{description.title}</strong> <br /> <Interpolate i18nKey="changesets.changeset.summary" diff --git a/scm-ui/public/locales/en/commons.json b/scm-ui/public/locales/en/commons.json index ca4a1c87bf..3453ffc5ad 100644 --- a/scm-ui/public/locales/en/commons.json +++ b/scm-ui/public/locales/en/commons.json @@ -65,6 +65,6 @@ "passwordInvalid": "Password has to be between 6 and 32 characters", "passwordConfirmFailed": "Passwords have to be identical", "submit": "Submit", - "changedSuccessfully": "Pasword successfully changed" + "changedSuccessfully": "Password changed successfully" } } From 88afe18384b9c8d673fb9d3407f609cd3f0532c3 Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Fri, 18 Jan 2019 11:41:41 +0100 Subject: [PATCH 007/120] implemented subnav and added settings for profile --- .../src/navigation/SubNavigation.js | 51 +++++++++++++++++++ .../ui-components/src/navigation/index.js | 1 + scm-ui/public/locales/en/commons.json | 1 + scm-ui/src/containers/Profile.js | 17 +++++-- 4 files changed, 66 insertions(+), 4 deletions(-) create mode 100644 scm-ui-components/packages/ui-components/src/navigation/SubNavigation.js diff --git a/scm-ui-components/packages/ui-components/src/navigation/SubNavigation.js b/scm-ui-components/packages/ui-components/src/navigation/SubNavigation.js new file mode 100644 index 0000000000..b577955a34 --- /dev/null +++ b/scm-ui-components/packages/ui-components/src/navigation/SubNavigation.js @@ -0,0 +1,51 @@ +//@flow +import * as React from "react"; +import {Link, Route} from "react-router-dom"; + +type Props = { + to: string, + label: string, + activeOnlyWhenExact?: boolean, + activeWhenMatch?: (route: any) => boolean, + children?: React.Node +}; + +class SubNavigation extends React.Component<Props> { + static defaultProps = { + activeOnlyWhenExact: false + }; + + isActive(route: any) { + const { activeWhenMatch } = this.props; + return route.match || (activeWhenMatch && activeWhenMatch(route)); + } + + renderLink = (route: any) => { + const { to, label } = this.props; + + let children = null; + if(this.isActive(route)) { + children = ( + <ul>{this.props.children}</ul> + ); + } + + return ( + <li> + <Link className={this.isActive(route) ? "is-active" : ""} to={to}> + {label} + </Link> + {children} + </li> + ); + }; + + render() { + const { to, activeOnlyWhenExact } = this.props; + return ( + <Route path={to} exact={activeOnlyWhenExact} children={this.renderLink} /> + ); + } +} + +export default SubNavigation; diff --git a/scm-ui-components/packages/ui-components/src/navigation/index.js b/scm-ui-components/packages/ui-components/src/navigation/index.js index ca82073b56..b696f98328 100644 --- a/scm-ui-components/packages/ui-components/src/navigation/index.js +++ b/scm-ui-components/packages/ui-components/src/navigation/index.js @@ -3,6 +3,7 @@ export { default as NavAction } from "./NavAction.js"; export { default as NavLink } from "./NavLink.js"; export { default as Navigation } from "./Navigation.js"; +export { default as SubNavigation } from "./SubNavigation.js"; export { default as PrimaryNavigation } from "./PrimaryNavigation.js"; export { default as PrimaryNavigationLink } from "./PrimaryNavigationLink.js"; export { default as Section } from "./Section.js"; diff --git a/scm-ui/public/locales/en/commons.json b/scm-ui/public/locales/en/commons.json index 3453ffc5ad..a1a7ce7028 100644 --- a/scm-ui/public/locales/en/commons.json +++ b/scm-ui/public/locales/en/commons.json @@ -46,6 +46,7 @@ "navigationLabel": "Profile Navigation", "informationNavLink": "Information", "changePasswordNavLink": "Change password", + "settingsNavLink": "Settings", "username": "Username", "displayName": "Display Name", "mail": "E-Mail", diff --git a/scm-ui/src/containers/Profile.js b/scm-ui/src/containers/Profile.js index b28a8cd705..e603a565e8 100644 --- a/scm-ui/src/containers/Profile.js +++ b/scm-ui/src/containers/Profile.js @@ -12,6 +12,7 @@ import { ErrorPage, Page, Navigation, + SubNavigation, Section, NavLink } from "@scm-manager/ui-components"; @@ -63,18 +64,26 @@ class Profile extends React.Component<Props, State> { <div className="column is-three-quarters"> <Route path={url} exact render={() => <ProfileInfo me={me} />} /> <Route - path={`${url}/password`} + path={`${url}/settings/password`} render={() => <ChangeUserPassword me={me} />} /> </div> <div className="column"> <Navigation> <Section label={t("profile.navigationLabel")}> - <NavLink to={`${url}`} label={t("profile.informationNavLink")} /> <NavLink - to={`${url}/password`} - label={t("profile.changePasswordNavLink")} + to={`${url}`} + label={t("profile.informationNavLink")} /> + <SubNavigation + to={`${url}/settings/password`} + label={t("profile.settingsNavLink")} + > + <NavLink + to={`${url}/settings/password`} + label={t("profile.changePasswordNavLink")} + /> + </SubNavigation> </Section> </Navigation> </div> From 8f340ceab026776e095e52d79e95edb02e92acfc Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Fri, 18 Jan 2019 11:53:14 +0100 Subject: [PATCH 008/120] added settings for user --- scm-ui/public/locales/en/users.json | 1 + scm-ui/src/containers/Profile.js | 2 +- scm-ui/src/users/containers/SingleUser.js | 20 +++++++++++++------- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/scm-ui/public/locales/en/users.json b/scm-ui/public/locales/en/users.json index e388081504..cb08387ef2 100644 --- a/scm-ui/public/locales/en/users.json +++ b/scm-ui/public/locales/en/users.json @@ -36,6 +36,7 @@ "single-user": { "navigationLabel": "User Navigation", "informationNavLink": "Information", + "settingsNavLink": "Settings", "editNavLink": "Edit", "setPasswordNavLink": "Set password", "errorTitle": "Error", diff --git a/scm-ui/src/containers/Profile.js b/scm-ui/src/containers/Profile.js index e603a565e8..2ad78d3527 100644 --- a/scm-ui/src/containers/Profile.js +++ b/scm-ui/src/containers/Profile.js @@ -76,7 +76,7 @@ class Profile extends React.Component<Props, State> { label={t("profile.informationNavLink")} /> <SubNavigation - to={`${url}/settings/password`} + to={`${url}/settings`} label={t("profile.settingsNavLink")} > <NavLink diff --git a/scm-ui/src/users/containers/SingleUser.js b/scm-ui/src/users/containers/SingleUser.js index 1662d213c5..c8e3990f6d 100644 --- a/scm-ui/src/users/containers/SingleUser.js +++ b/scm-ui/src/users/containers/SingleUser.js @@ -5,6 +5,7 @@ import { Page, Loading, Navigation, + SubNavigation, Section, NavLink, ErrorPage @@ -99,11 +100,11 @@ class SingleUser extends React.Component<Props> { <div className="column is-three-quarters"> <Route path={url} exact component={() => <Details user={user} />} /> <Route - path={`${url}/edit`} + path={`${url}/settings/edit`} component={() => <EditUser user={user} />} /> <Route - path={`${url}/password`} + path={`${url}/settings/password`} component={() => <SetUserPassword user={user} />} /> </div> @@ -114,11 +115,16 @@ class SingleUser extends React.Component<Props> { to={`${url}`} label={t("single-user.informationNavLink")} /> - <EditUserNavLink user={user} editUrl={`${url}/edit`} /> - <SetPasswordNavLink - user={user} - passwordUrl={`${url}/password`} - /> + <SubNavigation + to={`${url}/settings`} + label={t("single-user.settingsNavLink")} + > + <EditUserNavLink user={user} editUrl={`${url}/settings/edit`} /> + <SetPasswordNavLink + user={user} + passwordUrl={`${url}/settings/password`} + /> + </SubNavigation> </Section> </Navigation> </div> From 71fc38dd1d6ae7d7915e5e8cb8ae458ccff34dfb Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Fri, 18 Jan 2019 13:42:11 +0100 Subject: [PATCH 009/120] first try on delete user --- scm-ui/public/locales/en/users.json | 3 ++- .../DeleteUserNavLink.js => DeleteUser.js} | 8 ++++---- ...eleteUserNavLink.test.js => DeleteUser.test.js} | 14 +++++++------- scm-ui/src/users/components/UserForm.js | 4 ++++ scm-ui/src/users/components/navLinks/index.js | 1 - scm-ui/src/users/containers/SingleUser.js | 1 - 6 files changed, 17 insertions(+), 14 deletions(-) rename scm-ui/src/users/components/{navLinks/DeleteUserNavLink.js => DeleteUser.js} (81%) rename scm-ui/src/users/components/{navLinks/DeleteUserNavLink.test.js => DeleteUser.test.js} (82%) diff --git a/scm-ui/public/locales/en/users.json b/scm-ui/public/locales/en/users.json index cb08387ef2..efcfab95b7 100644 --- a/scm-ui/public/locales/en/users.json +++ b/scm-ui/public/locales/en/users.json @@ -27,7 +27,8 @@ } }, "user-form": { - "submit": "Submit" + "submit": "Submit", + "deleteUser": "Delete User" }, "add-user": { "title": "Create User", diff --git a/scm-ui/src/users/components/navLinks/DeleteUserNavLink.js b/scm-ui/src/users/components/DeleteUser.js similarity index 81% rename from scm-ui/src/users/components/navLinks/DeleteUserNavLink.js rename to scm-ui/src/users/components/DeleteUser.js index 47fdae0f92..4037f5bed6 100644 --- a/scm-ui/src/users/components/navLinks/DeleteUserNavLink.js +++ b/scm-ui/src/users/components/DeleteUser.js @@ -2,7 +2,7 @@ import React from "react"; import { translate } from "react-i18next"; import type { User } from "@scm-manager/ui-types"; -import { NavAction, confirmAlert } from "@scm-manager/ui-components"; +import { DeleteButton, confirmAlert } from "@scm-manager/ui-components"; type Props = { user: User, @@ -11,7 +11,7 @@ type Props = { deleteUser: (user: User) => void }; -class DeleteUserNavLink extends React.Component<Props> { +class DeleteUser extends React.Component<Props> { static defaultProps = { confirmDialog: true }; @@ -49,8 +49,8 @@ class DeleteUserNavLink extends React.Component<Props> { if (!this.isDeletable()) { return null; } - return <NavAction label={t("delete-user-button.label")} action={action} />; + return <DeleteButton label={t("user-form.deleteUser")} action={action} />; } } -export default translate("users")(DeleteUserNavLink); +export default translate("users")(DeleteUser); diff --git a/scm-ui/src/users/components/navLinks/DeleteUserNavLink.test.js b/scm-ui/src/users/components/DeleteUser.test.js similarity index 82% rename from scm-ui/src/users/components/navLinks/DeleteUserNavLink.test.js rename to scm-ui/src/users/components/DeleteUser.test.js index 500235ab94..55c49c6648 100644 --- a/scm-ui/src/users/components/navLinks/DeleteUserNavLink.test.js +++ b/scm-ui/src/users/components/DeleteUser.test.js @@ -1,8 +1,8 @@ import React from "react"; import { mount, shallow } from "enzyme"; -import "../../../tests/enzyme"; +import "../../tests/enzyme"; import "../../../tests/i18n"; -import DeleteUserNavLink from "./DeleteUserNavLink"; +import DeleteUser from "../DeleteUser"; import { confirmAlert } from "@scm-manager/ui-components"; jest.mock("@scm-manager/ui-components", () => ({ @@ -10,14 +10,14 @@ jest.mock("@scm-manager/ui-components", () => ({ NavAction: require.requireActual("@scm-manager/ui-components").NavAction })); -describe("DeleteUserNavLink", () => { +describe("DeleteUser", () => { it("should render nothing, if the delete link is missing", () => { const user = { _links: {} }; const navLink = shallow( - <DeleteUserNavLink user={user} deleteUser={() => {}} /> + <DeleteUser user={user} deleteUser={() => {}} /> ); expect(navLink.text()).toBe(""); }); @@ -32,7 +32,7 @@ describe("DeleteUserNavLink", () => { }; const navLink = mount( - <DeleteUserNavLink user={user} deleteUser={() => {}} /> + <DeleteUser user={user} deleteUser={() => {}} /> ); expect(navLink.text()).not.toBe(""); }); @@ -47,7 +47,7 @@ describe("DeleteUserNavLink", () => { }; const navLink = mount( - <DeleteUserNavLink user={user} deleteUser={() => {}} /> + <DeleteUser user={user} deleteUser={() => {}} /> ); navLink.find("a").simulate("click"); @@ -69,7 +69,7 @@ describe("DeleteUserNavLink", () => { } const navLink = mount( - <DeleteUserNavLink + <DeleteUser user={user} confirmDialog={false} deleteUser={capture} diff --git a/scm-ui/src/users/components/UserForm.js b/scm-ui/src/users/components/UserForm.js index 2d3a519f15..dde5d9bf18 100644 --- a/scm-ui/src/users/components/UserForm.js +++ b/scm-ui/src/users/components/UserForm.js @@ -10,6 +10,7 @@ import { validation as validator } from "@scm-manager/ui-components"; import * as userValidator from "./userValidation"; +import DeleteUser from "./DeleteUser"; type Props = { submitForm: User => void, @@ -153,6 +154,9 @@ class UserForm extends React.Component<Props, State> { label={t("user-form.submit")} /> </div> + <div className="column"> + <DeleteUser user={user} /> + </div> </div> </form> ); diff --git a/scm-ui/src/users/components/navLinks/index.js b/scm-ui/src/users/components/navLinks/index.js index a6d8370c00..64f7536c4c 100644 --- a/scm-ui/src/users/components/navLinks/index.js +++ b/scm-ui/src/users/components/navLinks/index.js @@ -1,3 +1,2 @@ -export { default as DeleteUserNavLink } from "./DeleteUserNavLink"; export { default as EditUserNavLink } from "./EditUserNavLink"; export { default as SetPasswordNavLink } from "./SetPasswordNavLink"; diff --git a/scm-ui/src/users/containers/SingleUser.js b/scm-ui/src/users/containers/SingleUser.js index c8e3990f6d..6fb3ccfd37 100644 --- a/scm-ui/src/users/containers/SingleUser.js +++ b/scm-ui/src/users/containers/SingleUser.js @@ -26,7 +26,6 @@ import { } from "../modules/users"; import { - DeleteUserNavLink, EditUserNavLink, SetPasswordNavLink } from "./../components/navLinks"; From de381f767386348a9785f5cf302cbe2cb9fdf2c2 Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Fri, 18 Jan 2019 13:42:48 +0100 Subject: [PATCH 010/120] changed for submenu styling --- scm-ui/styles/scm.scss | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/scm-ui/styles/scm.scss b/scm-ui/styles/scm.scss index f2a373ff09..b3764749a3 100644 --- a/scm-ui/styles/scm.scss +++ b/scm-ui/styles/scm.scss @@ -256,9 +256,6 @@ $fa-font-path: "webfonts"; border-radius: 0; color: #333; padding: 1rem; - border-top: 1px solid #eee; - border-left: 1px solid #eee; - border-right: 1px solid #eee; &.is-active { color: $blue; @@ -277,10 +274,27 @@ $fa-font-path: "webfonts"; } } } - > li:first-child > a { + li { + ul { + margin: 0; + border-top: 1px solid #eee; + + li { + border-right: none; + } + li:last-child { + border-bottom: none; + } + } + + border-top: 1px solid #eee; + border-left: 1px solid #eee; + border-right: 1px solid #eee; + } + li:first-child { border-top: none; } - li:last-child > a { + li:last-child { border-bottom: 1px solid #eee; } } From c6b43ec460f448a5da39f556240dc82d56b0d00c Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Fri, 18 Jan 2019 14:32:35 +0100 Subject: [PATCH 011/120] fixed submenu navlinks --- .../ui-components/src/navigation/SubNavigation.js | 8 +++++++- scm-ui/src/containers/Profile.js | 2 +- scm-ui/src/users/containers/SingleUser.js | 2 +- scm-ui/styles/scm.scss | 2 +- 4 files changed, 10 insertions(+), 4 deletions(-) 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 b577955a34..0eef244f59 100644 --- a/scm-ui-components/packages/ui-components/src/navigation/SubNavigation.js +++ b/scm-ui-components/packages/ui-components/src/navigation/SubNavigation.js @@ -42,8 +42,14 @@ class SubNavigation extends React.Component<Props> { render() { const { to, activeOnlyWhenExact } = this.props; + + // removes last part of url + let parents = to.split("/"); + parents.splice(-1,1); + let parent = parents.join("/"); + return ( - <Route path={to} exact={activeOnlyWhenExact} children={this.renderLink} /> + <Route path={parent} exact={activeOnlyWhenExact} children={this.renderLink} /> ); } } diff --git a/scm-ui/src/containers/Profile.js b/scm-ui/src/containers/Profile.js index 2ad78d3527..e603a565e8 100644 --- a/scm-ui/src/containers/Profile.js +++ b/scm-ui/src/containers/Profile.js @@ -76,7 +76,7 @@ class Profile extends React.Component<Props, State> { label={t("profile.informationNavLink")} /> <SubNavigation - to={`${url}/settings`} + to={`${url}/settings/password`} label={t("profile.settingsNavLink")} > <NavLink diff --git a/scm-ui/src/users/containers/SingleUser.js b/scm-ui/src/users/containers/SingleUser.js index 6fb3ccfd37..dfc07d5942 100644 --- a/scm-ui/src/users/containers/SingleUser.js +++ b/scm-ui/src/users/containers/SingleUser.js @@ -115,7 +115,7 @@ class SingleUser extends React.Component<Props> { label={t("single-user.informationNavLink")} /> <SubNavigation - to={`${url}/settings`} + to={`${url}/settings/edit`} label={t("single-user.settingsNavLink")} > <EditUserNavLink user={user} editUrl={`${url}/settings/edit`} /> diff --git a/scm-ui/styles/scm.scss b/scm-ui/styles/scm.scss index b3764749a3..df32e8eb7d 100644 --- a/scm-ui/styles/scm.scss +++ b/scm-ui/styles/scm.scss @@ -253,7 +253,6 @@ $fa-font-path: "webfonts"; } .menu-list { a { - border-radius: 0; color: #333; padding: 1rem; @@ -287,6 +286,7 @@ $fa-font-path: "webfonts"; } } + border-radius: 0; border-top: 1px solid #eee; border-left: 1px solid #eee; border-right: 1px solid #eee; From b1b38276ab50cf8c5e7fcf7f5a327fe9477272ca Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Fri, 18 Jan 2019 15:26:55 +0100 Subject: [PATCH 012/120] moved funcs from singleuser to deleteuser + renamed edit to general --- scm-ui/public/locales/en/users.json | 4 +- scm-ui/src/users/components/DeleteUser.js | 54 ++++++++++++++++++- .../src/users/components/DeleteUser.test.js | 2 +- scm-ui/src/users/containers/SingleUser.js | 32 +++-------- 4 files changed, 63 insertions(+), 29 deletions(-) diff --git a/scm-ui/public/locales/en/users.json b/scm-ui/public/locales/en/users.json index efcfab95b7..957fb8f12c 100644 --- a/scm-ui/public/locales/en/users.json +++ b/scm-ui/public/locales/en/users.json @@ -38,8 +38,8 @@ "navigationLabel": "User Navigation", "informationNavLink": "Information", "settingsNavLink": "Settings", - "editNavLink": "Edit", - "setPasswordNavLink": "Set password", + "editNavLink": "General", + "setPasswordNavLink": "Password", "errorTitle": "Error", "errorSubtitle": "Unknown user error" }, diff --git a/scm-ui/src/users/components/DeleteUser.js b/scm-ui/src/users/components/DeleteUser.js index 4037f5bed6..775f243ac6 100644 --- a/scm-ui/src/users/components/DeleteUser.js +++ b/scm-ui/src/users/components/DeleteUser.js @@ -3,15 +3,37 @@ import React from "react"; import { translate } from "react-i18next"; import type { User } from "@scm-manager/ui-types"; import { DeleteButton, confirmAlert } from "@scm-manager/ui-components"; +import {connect} from "react-redux"; +import { + deleteUser, fetchUserByName, + getDeleteUserFailure, + getUserByName, + isDeleteUserPending, +} from "../modules/users"; +import type {History} from "history"; type Props = { user: User, confirmDialog?: boolean, + + // dispatcher functions + fetchUserByName: (string, string) => void, + deleteUser: (user: User, callback?: () => void) => void, + + // context objects t: string => string, - deleteUser: (user: User) => void + history: History }; class DeleteUser extends React.Component<Props> { + userDeleted = () => { + this.props.history.push("/users"); + }; + + deleteUser = (user: User) => { + this.props.deleteUser(user, this.userDeleted); + }; + static defaultProps = { confirmDialog: true }; @@ -53,4 +75,34 @@ class DeleteUser extends React.Component<Props> { } } +/* +const mapStateToProps = (state, ownProps) => { + const name = ownProps.match.params.name; + const user = getUserByName(state, name); + const loading = isDeleteUserPending(state, name); + const error = getDeleteUserFailure(state, name); + return { + name, + user, + loading, + error + }; +}; + +const mapDispatchToProps = dispatch => { + return { + fetchUserByName: (link: string, name: string) => { + dispatch(fetchUserByName(link, name)); + }, + deleteUser: (user: User, callback?: () => void) => { + dispatch(deleteUser(user, callback)); + } + }; +}; + +export default connect( + mapStateToProps, + mapDispatchToProps +)(translate("users")(DeleteUser)); +*/ export default translate("users")(DeleteUser); diff --git a/scm-ui/src/users/components/DeleteUser.test.js b/scm-ui/src/users/components/DeleteUser.test.js index 55c49c6648..e03aad5b49 100644 --- a/scm-ui/src/users/components/DeleteUser.test.js +++ b/scm-ui/src/users/components/DeleteUser.test.js @@ -2,7 +2,7 @@ import React from "react"; import { mount, shallow } from "enzyme"; import "../../tests/enzyme"; import "../../../tests/i18n"; -import DeleteUser from "../DeleteUser"; +import DeleteUser from "./DeleteUser"; import { confirmAlert } from "@scm-manager/ui-components"; jest.mock("@scm-manager/ui-components", () => ({ diff --git a/scm-ui/src/users/containers/SingleUser.js b/scm-ui/src/users/containers/SingleUser.js index dfc07d5942..b33e8dd906 100644 --- a/scm-ui/src/users/containers/SingleUser.js +++ b/scm-ui/src/users/containers/SingleUser.js @@ -17,14 +17,10 @@ import type { User } from "@scm-manager/ui-types"; import type { History } from "history"; import { fetchUserByName, - deleteUser, getUserByName, isFetchUserPending, - getFetchUserFailure, - isDeleteUserPending, - getDeleteUserFailure + getFetchUserFailure } from "../modules/users"; - import { EditUserNavLink, SetPasswordNavLink @@ -40,8 +36,7 @@ type Props = { error: Error, usersLink: string, - // dispatcher functions - deleteUser: (user: User, callback?: () => void) => void, + // dispatcher function fetchUserByName: (string, string) => void, // context objects @@ -55,14 +50,6 @@ class SingleUser extends React.Component<Props> { this.props.fetchUserByName(this.props.usersLink, this.props.name); } - userDeleted = () => { - this.props.history.push("/users"); - }; - - deleteUser = (user: User) => { - this.props.deleteUser(user, this.userDeleted); - }; - stripEndingSlash = (url: string) => { if (url.endsWith("/")) { return url.substring(0, url.length - 2); @@ -99,7 +86,7 @@ class SingleUser extends React.Component<Props> { <div className="column is-three-quarters"> <Route path={url} exact component={() => <Details user={user} />} /> <Route - path={`${url}/settings/edit`} + path={`${url}/settings/general`} component={() => <EditUser user={user} />} /> <Route @@ -115,10 +102,10 @@ class SingleUser extends React.Component<Props> { label={t("single-user.informationNavLink")} /> <SubNavigation - to={`${url}/settings/edit`} + to={`${url}/settings/general`} label={t("single-user.settingsNavLink")} > - <EditUserNavLink user={user} editUrl={`${url}/settings/edit`} /> + <EditUserNavLink user={user} editUrl={`${url}/settings/general`} /> <SetPasswordNavLink user={user} passwordUrl={`${url}/settings/password`} @@ -136,10 +123,8 @@ class SingleUser extends React.Component<Props> { const mapStateToProps = (state, ownProps) => { const name = ownProps.match.params.name; const user = getUserByName(state, name); - const loading = - isFetchUserPending(state, name) || isDeleteUserPending(state, name); - const error = - getFetchUserFailure(state, name) || getDeleteUserFailure(state, name); + const loading = isFetchUserPending(state, name); + const error = getFetchUserFailure(state, name); const usersLink = getUsersLink(state); return { usersLink, @@ -154,9 +139,6 @@ const mapDispatchToProps = dispatch => { return { fetchUserByName: (link: string, name: string) => { dispatch(fetchUserByName(link, name)); - }, - deleteUser: (user: User, callback?: () => void) => { - dispatch(deleteUser(user, callback)); } }; }; From df5434147c36593e3aa4479626cf39b1f91a467e Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Fri, 18 Jan 2019 15:52:34 +0100 Subject: [PATCH 013/120] refactoring + css fix + general config form changed --- scm-ui/public/locales/en/users.json | 46 ++++---- scm-ui/src/users/components/DeleteUser.js | 10 +- .../src/users/components/SetUserPassword.js | 4 +- scm-ui/src/users/components/UserForm.js | 106 ++++++++++-------- .../components/navLinks/EditUserNavLink.js | 2 +- .../components/navLinks/SetPasswordNavLink.js | 2 +- scm-ui/src/users/containers/SingleUser.js | 6 +- scm-ui/styles/scm.scss | 2 +- 8 files changed, 95 insertions(+), 83 deletions(-) diff --git a/scm-ui/public/locales/en/users.json b/scm-ui/public/locales/en/users.json index 957fb8f12c..0ec61c9870 100644 --- a/scm-ui/public/locales/en/users.json +++ b/scm-ui/public/locales/en/users.json @@ -17,29 +17,36 @@ "create-user-button": { "label": "Create" }, - "delete-user-button": { - "label": "Delete", - "confirm-alert": { - "title": "Delete user", - "message": "Do you really want to delete the user?", - "submit": "Yes", - "cancel": "No" - } - }, - "user-form": { - "submit": "Submit", - "deleteUser": "Delete User" - }, "add-user": { "title": "Create User", "subtitle": "Create a new user" }, "single-user": { - "navigationLabel": "User Navigation", - "informationNavLink": "Information", - "settingsNavLink": "Settings", - "editNavLink": "General", - "setPasswordNavLink": "Password", + "menu": { + "navigationLabel": "User Navigation", + "informationNavLink": "Information", + "settingsNavLink": "Settings", + "editNavLink": "General", + "setPasswordNavLink": "Password" + }, + "edit": { + "subtitle": "Edit User", + "button": "Submit" + }, + "delete": { + "subtitle": "Delete User", + "button": "Delete", + "confirm-alert": { + "title": "Delete user", + "message": "Do you really want to delete the user?", + "submit": "Yes", + "cancel": "No" + } + }, + "password": { + "button": "Set password", + "set-password-successful": "Password successfully set" + }, "errorTitle": "Error", "errorSubtitle": "Unknown user error" }, @@ -48,9 +55,6 @@ "name-invalid": "This name is invalid", "displayname-invalid": "This displayname is invalid" }, - "password": { - "set-password-successful": "Password successfully set" - }, "help": { "usernameHelpText": "Unique name of the user.", "displayNameHelpText": "Display name of the user.", diff --git a/scm-ui/src/users/components/DeleteUser.js b/scm-ui/src/users/components/DeleteUser.js index 775f243ac6..58d8d446dd 100644 --- a/scm-ui/src/users/components/DeleteUser.js +++ b/scm-ui/src/users/components/DeleteUser.js @@ -45,15 +45,15 @@ class DeleteUser extends React.Component<Props> { confirmDelete = () => { const { t } = this.props; confirmAlert({ - title: t("delete-user-button.confirm-alert.title"), - message: t("delete-user-button.confirm-alert.message"), + title: t("single-user.delete.confirm-alert.title"), + message: t("single-user.delete.confirm-alert.message"), buttons: [ { - label: t("delete-user-button.confirm-alert.submit"), + label: t("single-user.delete.confirm-alert.submit"), onClick: () => this.deleteUser() }, { - label: t("delete-user-button.confirm-alert.cancel"), + label: t("single-user.delete.confirm-alert.cancel"), onClick: () => null } ] @@ -71,7 +71,7 @@ class DeleteUser extends React.Component<Props> { if (!this.isDeletable()) { return null; } - return <DeleteButton label={t("user-form.deleteUser")} action={action} />; + return <DeleteButton label={t("single-user.delete.button")} action={action} />; } } diff --git a/scm-ui/src/users/components/SetUserPassword.js b/scm-ui/src/users/components/SetUserPassword.js index d318025f21..eadc2002c1 100644 --- a/scm-ui/src/users/components/SetUserPassword.js +++ b/scm-ui/src/users/components/SetUserPassword.js @@ -90,7 +90,7 @@ class SetUserPassword extends React.Component<Props, State> { message = ( <Notification type={"success"} - children={t("password.set-password-successful")} + children={t("single-user.password.set-password-successful")} onClose={() => this.onClose()} /> ); @@ -108,7 +108,7 @@ class SetUserPassword extends React.Component<Props, State> { <SubmitButton disabled={!this.state.passwordValid} loading={loading} - label={t("user-form.submit")} + label={t("single-user.password.button")} /> </form> ); diff --git a/scm-ui/src/users/components/UserForm.js b/scm-ui/src/users/components/UserForm.js index dde5d9bf18..eb8aa904ff 100644 --- a/scm-ui/src/users/components/UserForm.js +++ b/scm-ui/src/users/components/UserForm.js @@ -3,6 +3,7 @@ import React from "react"; import { translate } from "react-i18next"; import type { User } from "@scm-manager/ui-types"; import { + Subtitle, Checkbox, InputField, PasswordConfirmation, @@ -105,60 +106,67 @@ class UserForm extends React.Component<Props, State> { ); } return ( - <form onSubmit={this.submit}> + <> + <Subtitle subtitle={t("single-user.edit.subtitle")} /> + <form onSubmit={this.submit}> + <div className="columns"> + <div className="column is-half"> + {nameField} + <InputField + label={t("user.displayName")} + onChange={this.handleDisplayNameChange} + value={user ? user.displayName : ""} + validationError={this.state.displayNameValidationError} + errorMessage={t("validation.displayname-invalid")} + helpText={t("help.displayNameHelpText")} + /> + </div> + <div className="column is-half"> + <InputField + label={t("user.mail")} + onChange={this.handleEmailChange} + value={user ? user.mail : ""} + validationError={this.state.mailValidationError} + errorMessage={t("validation.mail-invalid")} + helpText={t("help.mailHelpText")} + /> + </div> + </div> + <div className="columns"> + <div className="column"> + {passwordChangeField} + <Checkbox + label={t("user.admin")} + onChange={this.handleAdminChange} + checked={user ? user.admin : false} + helpText={t("help.adminHelpText")} + /> + <Checkbox + label={t("user.active")} + onChange={this.handleActiveChange} + checked={user ? user.active : false} + helpText={t("help.activeHelpText")} + /> + </div> + </div> + <div className="columns"> + <div className="column"> + <SubmitButton + disabled={!this.isValid()} + loading={loading} + label={t("single-user.edit.button")} + /> + </div> + </div> + </form> + <hr /> + <Subtitle subtitle={t("single-user.delete.subtitle")} /> <div className="columns"> - <div className="column is-half"> - {nameField} - <InputField - label={t("user.displayName")} - onChange={this.handleDisplayNameChange} - value={user ? user.displayName : ""} - validationError={this.state.displayNameValidationError} - errorMessage={t("validation.displayname-invalid")} - helpText={t("help.displayNameHelpText")} - /> - </div> - <div className="column is-half"> - <InputField - label={t("user.mail")} - onChange={this.handleEmailChange} - value={user ? user.mail : ""} - validationError={this.state.mailValidationError} - errorMessage={t("validation.mail-invalid")} - helpText={t("help.mailHelpText")} - /> - </div> - </div> - <div className="columns"> - <div className="column"> - {passwordChangeField} - <Checkbox - label={t("user.admin")} - onChange={this.handleAdminChange} - checked={user ? user.admin : false} - helpText={t("help.adminHelpText")} - /> - <Checkbox - label={t("user.active")} - onChange={this.handleActiveChange} - checked={user ? user.active : false} - helpText={t("help.activeHelpText")} - /> - </div> - </div> - <div className="columns"> - <div className="column"> - <SubmitButton - disabled={!this.isValid()} - loading={loading} - label={t("user-form.submit")} - /> - </div> <div className="column"> <DeleteUser user={user} /> </div> </div> - </form> + </> ); } diff --git a/scm-ui/src/users/components/navLinks/EditUserNavLink.js b/scm-ui/src/users/components/navLinks/EditUserNavLink.js index 3689b445d8..b98a02c2db 100644 --- a/scm-ui/src/users/components/navLinks/EditUserNavLink.js +++ b/scm-ui/src/users/components/navLinks/EditUserNavLink.js @@ -17,7 +17,7 @@ class EditUserNavLink extends React.Component<Props> { if (!this.isEditable()) { return null; } - return <NavLink label={t("single-user.editNavLink")} to={editUrl} />; + return <NavLink label={t("single-user.menu.editNavLink")} to={editUrl} />; } isEditable = () => { diff --git a/scm-ui/src/users/components/navLinks/SetPasswordNavLink.js b/scm-ui/src/users/components/navLinks/SetPasswordNavLink.js index e9f28d841e..bea1bd2981 100644 --- a/scm-ui/src/users/components/navLinks/SetPasswordNavLink.js +++ b/scm-ui/src/users/components/navLinks/SetPasswordNavLink.js @@ -17,7 +17,7 @@ class ChangePasswordNavLink extends React.Component<Props> { if (!this.hasPermissionToSetPassword()) { return null; } - return <NavLink label={t("single-user.setPasswordNavLink")} to={passwordUrl} />; + return <NavLink label={t("single-user.menu.setPasswordNavLink")} to={passwordUrl} />; } hasPermissionToSetPassword = () => { diff --git a/scm-ui/src/users/containers/SingleUser.js b/scm-ui/src/users/containers/SingleUser.js index b33e8dd906..69c8a607e6 100644 --- a/scm-ui/src/users/containers/SingleUser.js +++ b/scm-ui/src/users/containers/SingleUser.js @@ -96,14 +96,14 @@ class SingleUser extends React.Component<Props> { </div> <div className="column"> <Navigation> - <Section label={t("single-user.navigationLabel")}> + <Section label={t("single-user.menu.navigationLabel")}> <NavLink to={`${url}`} - label={t("single-user.informationNavLink")} + label={t("single-user.menu.informationNavLink")} /> <SubNavigation to={`${url}/settings/general`} - label={t("single-user.settingsNavLink")} + label={t("single-user.menu.settingsNavLink")} > <EditUserNavLink user={user} editUrl={`${url}/settings/general`} /> <SetPasswordNavLink diff --git a/scm-ui/styles/scm.scss b/scm-ui/styles/scm.scss index df32e8eb7d..7ee8d427ba 100644 --- a/scm-ui/styles/scm.scss +++ b/scm-ui/styles/scm.scss @@ -291,7 +291,7 @@ $fa-font-path: "webfonts"; border-left: 1px solid #eee; border-right: 1px solid #eee; } - li:first-child { + > li:first-child { border-top: none; } li:last-child { From 9dc7882c54164b2f7ebe302cdfd6d2d2223e0a0d Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Fri, 18 Jan 2019 17:28:38 +0100 Subject: [PATCH 014/120] clarified langs, added repo settings menu, changed git config binding, fixed small css issue with div in navi --- .../scm-git-plugin/src/main/js/index.js | 2 +- scm-ui/public/locales/en/repos.json | 14 ++++-- .../src/repos/components/DeleteNavAction.js | 25 +++++++--- scm-ui/src/repos/components/EditNavLink.js | 2 +- .../repos/components/PermissionsNavLink.js | 2 +- .../components/PermissionsNavLink.test.js | 2 +- .../repos/components/form/RepositoryForm.js | 48 ++++++++++--------- scm-ui/src/repos/containers/Edit.js | 2 + scm-ui/src/repos/containers/RepositoryRoot.js | 38 ++++++++------- scm-ui/src/users/components/DeleteUser.js | 29 ++++++++--- scm-ui/src/users/components/UserForm.js | 8 ---- scm-ui/src/users/containers/EditUser.js | 3 ++ scm-ui/src/users/containers/SingleUser.js | 10 ++-- scm-ui/styles/scm.scss | 3 ++ 14 files changed, 116 insertions(+), 72 deletions(-) diff --git a/scm-plugins/scm-git-plugin/src/main/js/index.js b/scm-plugins/scm-git-plugin/src/main/js/index.js index a066247dde..5534d2061a 100644 --- a/scm-plugins/scm-git-plugin/src/main/js/index.js +++ b/scm-plugins/scm-git-plugin/src/main/js/index.js @@ -28,7 +28,7 @@ binder.bind( binder.bind("repos.repository-avatar", GitAvatar, gitPredicate); cfgBinder.bindRepository( - "/configuration", + "/settings/configuration", "scm-git-plugin.repo-config.link", "configuration", RepositoryConfig diff --git a/scm-ui/public/locales/en/repos.json b/scm-ui/public/locales/en/repos.json index 68f5b4a53b..4693dedfd6 100644 --- a/scm-ui/public/locales/en/repos.json +++ b/scm-ui/public/locales/en/repos.json @@ -17,11 +17,15 @@ "create-button": "Create" }, "repository-root": { - "navigationLabel": "Repository Navigation", - "historyNavLink": "Commits", - "informationNavLink": "Information", - "permissionsNavLink": "Permissions", - "sourcesNavLink": "Sources", + "menu": { + "navigationLabel": "Repository Navigation", + "informationNavLink": "Information", + "historyNavLink": "Commits", + "sourcesNavLink": "Sources", + "settingsNavLink": "Settings", + "editNavLink": "General", + "permissionsNavLink": "Permissions" + }, "errorTitle": "Error", "errorSubtitle": "Unknown repository error" }, diff --git a/scm-ui/src/repos/components/DeleteNavAction.js b/scm-ui/src/repos/components/DeleteNavAction.js index c2369a5bfb..a81cb17f66 100644 --- a/scm-ui/src/repos/components/DeleteNavAction.js +++ b/scm-ui/src/repos/components/DeleteNavAction.js @@ -1,7 +1,7 @@ //@flow import React from "react"; import { translate } from "react-i18next"; -import { NavAction, confirmAlert } from "@scm-manager/ui-components"; +import { Subtitle, DeleteButton, confirmAlert } from "@scm-manager/ui-components"; import type { Repository } from "@scm-manager/ui-types"; type Props = { @@ -25,15 +25,15 @@ class DeleteNavAction extends React.Component<Props> { confirmDelete = () => { const { t } = this.props; confirmAlert({ - title: t("delete-nav-action.confirm-alert.title"), - message: t("delete-nav-action.confirm-alert.message"), + title: t("repository.delete.confirm-alert.title"), + message: t("repository.delete.confirm-alert.message"), buttons: [ { - label: t("delete-nav-action.confirm-alert.submit"), + label: t("repository.delete.confirm-alert.submit"), onClick: () => this.delete() }, { - label: t("delete-nav-action.confirm-alert.cancel"), + label: t("repository.delete.confirm-alert.cancel"), onClick: () => null } ] @@ -51,7 +51,20 @@ class DeleteNavAction extends React.Component<Props> { if (!this.isDeletable()) { return null; } - return <NavAction label={t("delete-nav-action.label")} action={action} />; + + return ( + <> + <Subtitle subtitle={t("repository.delete.subtitle")} /> + <div className="columns"> + <div className="column"> + <DeleteButton + label={t("repository.delete.button")} + action={action} + /> + </div> + </div> + </> + ); } } diff --git a/scm-ui/src/repos/components/EditNavLink.js b/scm-ui/src/repos/components/EditNavLink.js index 1a49fdee81..2163624e4d 100644 --- a/scm-ui/src/repos/components/EditNavLink.js +++ b/scm-ui/src/repos/components/EditNavLink.js @@ -15,7 +15,7 @@ class EditNavLink extends React.Component<Props> { return null; } const { editUrl, t } = this.props; - return <NavLink to={editUrl} label={t("edit-nav-link.label")} />; + return <NavLink to={editUrl} label={t("repository-root.menu.editNavLink")} />; } } diff --git a/scm-ui/src/repos/components/PermissionsNavLink.js b/scm-ui/src/repos/components/PermissionsNavLink.js index 937980fd3d..364c274f6b 100644 --- a/scm-ui/src/repos/components/PermissionsNavLink.js +++ b/scm-ui/src/repos/components/PermissionsNavLink.js @@ -20,7 +20,7 @@ class PermissionsNavLink extends React.Component<Props> { } const { permissionUrl, t } = this.props; return ( - <NavLink to={permissionUrl} label={t("repository-root.permissionsNavLink")} /> + <NavLink to={permissionUrl} label={t("repository-root.menu.permissionsNavLink")} /> ); } } diff --git a/scm-ui/src/repos/components/PermissionsNavLink.test.js b/scm-ui/src/repos/components/PermissionsNavLink.test.js index 450c7f49e6..5dddfe0cf4 100644 --- a/scm-ui/src/repos/components/PermissionsNavLink.test.js +++ b/scm-ui/src/repos/components/PermissionsNavLink.test.js @@ -33,6 +33,6 @@ describe("PermissionsNavLink", () => { <PermissionsNavLink repository={repository} permissionUrl="" />, options.get() ); - expect(navLink.text()).toBe("repository-root.permissions"); + expect(navLink.text()).toBe("repository-root.menu.permissions"); }); }); diff --git a/scm-ui/src/repos/components/form/RepositoryForm.js b/scm-ui/src/repos/components/form/RepositoryForm.js index 8f5d932778..cc0b409175 100644 --- a/scm-ui/src/repos/components/form/RepositoryForm.js +++ b/scm-ui/src/repos/components/form/RepositoryForm.js @@ -2,6 +2,7 @@ import React from "react"; import { translate } from "react-i18next"; import { + Subtitle, InputField, Select, SubmitButton, @@ -82,29 +83,32 @@ class RepositoryForm extends React.Component<Props, State> { const repository = this.state.repository; return ( - <form onSubmit={this.submit}> - {this.renderCreateOnlyFields()} - <InputField - label={t("repository.contact")} - onChange={this.handleContactChange} - value={repository ? repository.contact : ""} - validationError={this.state.contactValidationError} - errorMessage={t("validation.contact-invalid")} - helpText={t("help.contactHelpText")} - /> + <> + <Subtitle subtitle={t("repository.edit.subtitle")} /> + <form onSubmit={this.submit}> + {this.renderCreateOnlyFields()} + <InputField + label={t("repository.contact")} + onChange={this.handleContactChange} + value={repository ? repository.contact : ""} + validationError={this.state.contactValidationError} + errorMessage={t("validation.contact-invalid")} + helpText={t("help.contactHelpText")} + /> - <Textarea - label={t("repository.description")} - onChange={this.handleDescriptionChange} - value={repository ? repository.description : ""} - helpText={t("help.descriptionHelpText")} - /> - <SubmitButton - disabled={!this.isValid()} - loading={loading} - label={t("repository-form.submit")} - /> - </form> + <Textarea + label={t("repository.description")} + onChange={this.handleDescriptionChange} + value={repository ? repository.description : ""} + helpText={t("help.descriptionHelpText")} + /> + <SubmitButton + disabled={!this.isValid()} + loading={loading} + label={t("repository-form.submit")} + /> + </form> + </> ); } diff --git a/scm-ui/src/repos/containers/Edit.js b/scm-ui/src/repos/containers/Edit.js index 816dae8de9..7341bcbd3e 100644 --- a/scm-ui/src/repos/containers/Edit.js +++ b/scm-ui/src/repos/containers/Edit.js @@ -48,6 +48,8 @@ class Edit extends React.Component<Props> { this.props.modifyRepo(repo, this.repoModified); }} /> + <hr /> + <p>TODO: DeleteRepo hier einbinden. Aktuell heißt es noch DeleteNavAction</p> </div> ); } diff --git a/scm-ui/src/repos/containers/RepositoryRoot.js b/scm-ui/src/repos/containers/RepositoryRoot.js index 9fcae12c53..1a7ca7eaea 100644 --- a/scm-ui/src/repos/containers/RepositoryRoot.js +++ b/scm-ui/src/repos/containers/RepositoryRoot.js @@ -6,7 +6,7 @@ import {connect} from "react-redux"; import {Route, Switch} from "react-router-dom"; import type {Repository} from "@scm-manager/ui-types"; -import {ErrorPage, Loading, Navigation, NavLink, Page, Section} from "@scm-manager/ui-components"; +import {ErrorPage, Loading, Navigation, SubNavigation, NavLink, Page, Section} from "@scm-manager/ui-components"; import {translate} from "react-i18next"; import RepositoryDetails from "../components/RepositoryDetails"; import DeleteNavAction from "../components/DeleteNavAction"; @@ -109,11 +109,11 @@ class RepositoryRoot extends React.Component<Props> { component={() => <RepositoryDetails repository={repository} />} /> <Route - path={`${url}/edit`} + path={`${url}/settings/general`} component={() => <Edit repository={repository} />} /> <Route - path={`${url}/permissions`} + path={`${url}/settings/permissions`} render={() => ( <Permissions namespace={this.props.repository.namespace} @@ -168,13 +168,13 @@ class RepositoryRoot extends React.Component<Props> { </div> <div className="column"> <Navigation> - <Section label={t("repository-root.navigationLabel")}> - <NavLink to={url} label={t("repository-root.informationNavLink")} /> + <Section label={t("repository-root.menu.navigationLabel")}> + <NavLink to={url} label={t("repository-root.menu.informationNavLink")} /> <RepositoryNavLink repository={repository} linkName="changesets" to={`${url}/changesets/`} - label={t("repository-root.historyNavLink")} + label={t("repository-root.menu.historyNavLink")} activeWhenMatch={this.matches} activeOnlyWhenExact={false} /> @@ -182,18 +182,24 @@ class RepositoryRoot extends React.Component<Props> { repository={repository} linkName="sources" to={`${url}/sources`} - label={t("repository-root.sourcesNavLink")} + label={t("repository-root.menu.sourcesNavLink")} activeOnlyWhenExact={false} /> - <PermissionsNavLink - permissionUrl={`${url}/permissions`} - repository={repository} - /> - <ExtensionPoint - name="repository.navigation" - props={extensionProps} - renderAll={true} - /> + <SubNavigation + to={`${url}/settings/general`} + label={t("repository-root.menu.settingsNavLink")} + > + <EditNavLink repository={repository} editUrl={`${url}/settings/general`} /> + <PermissionsNavLink + permissionUrl={`${url}/settings/permissions`} + repository={repository} + /> + <ExtensionPoint + name="repository.navigation" + props={extensionProps} + renderAll={true} + /> + </SubNavigation> </Section> </Navigation> </div> diff --git a/scm-ui/src/users/components/DeleteUser.js b/scm-ui/src/users/components/DeleteUser.js index 58d8d446dd..1a250fa58d 100644 --- a/scm-ui/src/users/components/DeleteUser.js +++ b/scm-ui/src/users/components/DeleteUser.js @@ -2,15 +2,20 @@ import React from "react"; import { translate } from "react-i18next"; import type { User } from "@scm-manager/ui-types"; -import { DeleteButton, confirmAlert } from "@scm-manager/ui-components"; -import {connect} from "react-redux"; import { - deleteUser, fetchUserByName, + Subtitle, + DeleteButton, + confirmAlert +} from "@scm-manager/ui-components"; +import { connect } from "react-redux"; +import { + deleteUser, + fetchUserByName, getDeleteUserFailure, getUserByName, - isDeleteUserPending, + isDeleteUserPending } from "../modules/users"; -import type {History} from "history"; +import type { History } from "history"; type Props = { user: User, @@ -71,7 +76,19 @@ class DeleteUser extends React.Component<Props> { if (!this.isDeletable()) { return null; } - return <DeleteButton label={t("single-user.delete.button")} action={action} />; + return ( + <> + <Subtitle subtitle={t("single-user.delete.subtitle")} /> + <div className="columns"> + <div className="column"> + <DeleteButton + label={t("single-user.delete.button")} + action={action} + /> + </div> + </div> + </> + ); } } diff --git a/scm-ui/src/users/components/UserForm.js b/scm-ui/src/users/components/UserForm.js index eb8aa904ff..0f6119ef94 100644 --- a/scm-ui/src/users/components/UserForm.js +++ b/scm-ui/src/users/components/UserForm.js @@ -11,7 +11,6 @@ import { validation as validator } from "@scm-manager/ui-components"; import * as userValidator from "./userValidation"; -import DeleteUser from "./DeleteUser"; type Props = { submitForm: User => void, @@ -159,13 +158,6 @@ class UserForm extends React.Component<Props, State> { </div> </div> </form> - <hr /> - <Subtitle subtitle={t("single-user.delete.subtitle")} /> - <div className="columns"> - <div className="column"> - <DeleteUser user={user} /> - </div> - </div> </> ); } diff --git a/scm-ui/src/users/containers/EditUser.js b/scm-ui/src/users/containers/EditUser.js index 55062ecb5b..a8f3276471 100644 --- a/scm-ui/src/users/containers/EditUser.js +++ b/scm-ui/src/users/containers/EditUser.js @@ -3,6 +3,7 @@ import React from "react"; import { connect } from "react-redux"; import { withRouter } from "react-router-dom"; import UserForm from "./../components/UserForm"; +import DeleteUser from "./../components/DeleteUser"; import type { User } from "@scm-manager/ui-types"; import { modifyUser, @@ -49,6 +50,8 @@ class EditUser extends React.Component<Props> { user={user} loading={loading} /> + <hr /> + <DeleteUser user={user} /> </div> ); } diff --git a/scm-ui/src/users/containers/SingleUser.js b/scm-ui/src/users/containers/SingleUser.js index 69c8a607e6..321e117976 100644 --- a/scm-ui/src/users/containers/SingleUser.js +++ b/scm-ui/src/users/containers/SingleUser.js @@ -21,10 +21,7 @@ import { isFetchUserPending, getFetchUserFailure } from "../modules/users"; -import { - EditUserNavLink, - SetPasswordNavLink -} from "./../components/navLinks"; +import { EditUserNavLink, SetPasswordNavLink } from "./../components/navLinks"; import { translate } from "react-i18next"; import { getUsersLink } from "../../modules/indexResource"; import SetUserPassword from "../components/SetUserPassword"; @@ -105,7 +102,10 @@ class SingleUser extends React.Component<Props> { to={`${url}/settings/general`} label={t("single-user.menu.settingsNavLink")} > - <EditUserNavLink user={user} editUrl={`${url}/settings/general`} /> + <EditUserNavLink + user={user} + editUrl={`${url}/settings/general`} + /> <SetPasswordNavLink user={user} passwordUrl={`${url}/settings/password`} diff --git a/scm-ui/styles/scm.scss b/scm-ui/styles/scm.scss index 7ee8d427ba..3ceab48f9f 100644 --- a/scm-ui/styles/scm.scss +++ b/scm-ui/styles/scm.scss @@ -297,4 +297,7 @@ $fa-font-path: "webfonts"; li:last-child { border-bottom: 1px solid #eee; } + div { + margin-bottom: 0; + } } From ac28d7d01974535681db93a209336351fa72caf4 Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Wed, 23 Jan 2019 09:10:43 +0100 Subject: [PATCH 015/120] undo changesetdetails changes --- .../components/changesets/ChangesetDetails.js | 41 ++++++++----------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/scm-ui/src/repos/components/changesets/ChangesetDetails.js b/scm-ui/src/repos/components/changesets/ChangesetDetails.js index 034ee36263..eb9d0992ee 100644 --- a/scm-ui/src/repos/components/changesets/ChangesetDetails.js +++ b/scm-ui/src/repos/components/changesets/ChangesetDetails.js @@ -46,15 +46,7 @@ class ChangesetDetails extends React.Component<Props> { return ( <div> <div className="content"> - <h4> - <ExtensionPoint - name="changesets.changeset.description" - props={{ changeset, value: description.title }} - renderAll={true} - > - {description.title} - </ExtensionPoint> - </h4> + <h4>{description.title}</h4> <article className="media"> <AvatarWrapper> <p className={classNames("image", "is-64x64", classes.spacing)}> @@ -75,23 +67,22 @@ class ChangesetDetails extends React.Component<Props> { </div> <div className="media-right">{this.renderTags()}</div> </article> - - <p> - {description.message.split("\n").map((item, key) => { - return ( - <span key={key}> - <ExtensionPoint - name="changesets.changeset.description" - props={{ changeset, value: item }} - renderAll={true} - > + <ExtensionPoint + name="changesets.changeset.description" + props={{ changeset, description }} + renderAll={true} + > + <p> + {description.message.split("\n").map((item, key) => { + return ( + <span key={key}> {item} - </ExtensionPoint> - <br /> - </span> - ); - })} - </p> + <br /> + </span> + ); + })} + </p> + </ExtensionPoint> </div> <div> <ChangesetDiff changeset={changeset} /> From 904f5851a7acbbf7fb67a589a295863ca3dbf002 Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Wed, 23 Jan 2019 09:17:53 +0100 Subject: [PATCH 016/120] renamed edituser to generaluser --- .../{EditUserNavLink.js => GeneralUserNavLink.js} | 4 ++-- ...EditUserNavLink.test.js => GeneralUserNavLink.test.js} | 6 +++--- scm-ui/src/users/components/navLinks/index.js | 2 +- .../src/users/containers/{EditUser.js => GeneralUser.js} | 4 ++-- scm-ui/src/users/containers/SingleUser.js | 8 ++++---- 5 files changed, 12 insertions(+), 12 deletions(-) rename scm-ui/src/users/components/navLinks/{EditUserNavLink.js => GeneralUserNavLink.js} (82%) rename scm-ui/src/users/components/navLinks/{EditUserNavLink.test.js => GeneralUserNavLink.test.js} (67%) rename scm-ui/src/users/containers/{EditUser.js => GeneralUser.js} (96%) diff --git a/scm-ui/src/users/components/navLinks/EditUserNavLink.js b/scm-ui/src/users/components/navLinks/GeneralUserNavLink.js similarity index 82% rename from scm-ui/src/users/components/navLinks/EditUserNavLink.js rename to scm-ui/src/users/components/navLinks/GeneralUserNavLink.js index b98a02c2db..a6f1201152 100644 --- a/scm-ui/src/users/components/navLinks/EditUserNavLink.js +++ b/scm-ui/src/users/components/navLinks/GeneralUserNavLink.js @@ -10,7 +10,7 @@ type Props = { editUrl: String }; -class EditUserNavLink extends React.Component<Props> { +class GeneralUserNavLink extends React.Component<Props> { render() { const { t, editUrl } = this.props; @@ -25,4 +25,4 @@ class EditUserNavLink extends React.Component<Props> { }; } -export default translate("users")(EditUserNavLink); +export default translate("users")(GeneralUserNavLink); diff --git a/scm-ui/src/users/components/navLinks/EditUserNavLink.test.js b/scm-ui/src/users/components/navLinks/GeneralUserNavLink.test.js similarity index 67% rename from scm-ui/src/users/components/navLinks/EditUserNavLink.test.js rename to scm-ui/src/users/components/navLinks/GeneralUserNavLink.test.js index ec46c531a1..018e98f47e 100644 --- a/scm-ui/src/users/components/navLinks/EditUserNavLink.test.js +++ b/scm-ui/src/users/components/navLinks/GeneralUserNavLink.test.js @@ -2,14 +2,14 @@ import React from "react"; import { shallow } from "enzyme"; import "../../../tests/enzyme"; import "../../../tests/i18n"; -import EditUserNavLink from "./EditUserNavLink"; +import GeneralUserNavLink from "./GeneralUserNavLink"; it("should render nothing, if the edit link is missing", () => { const user = { _links: {} }; - const navLink = shallow(<EditUserNavLink user={user} editUrl='/user/edit'/>); + const navLink = shallow(<GeneralUserNavLink user={user} editUrl='/user/edit'/>); expect(navLink.text()).toBe(""); }); @@ -22,6 +22,6 @@ it("should render the navLink", () => { } }; - const navLink = shallow(<EditUserNavLink user={user} editUrl='/user/edit'/>); + const navLink = shallow(<GeneralUserNavLink user={user} editUrl='/user/edit'/>); expect(navLink.text()).not.toBe(""); }); diff --git a/scm-ui/src/users/components/navLinks/index.js b/scm-ui/src/users/components/navLinks/index.js index 64f7536c4c..64a266f923 100644 --- a/scm-ui/src/users/components/navLinks/index.js +++ b/scm-ui/src/users/components/navLinks/index.js @@ -1,2 +1,2 @@ -export { default as EditUserNavLink } from "./EditUserNavLink"; +export { default as GeneralUserNavLink } from "./GeneralUserNavLink"; export { default as SetPasswordNavLink } from "./SetPasswordNavLink"; diff --git a/scm-ui/src/users/containers/EditUser.js b/scm-ui/src/users/containers/GeneralUser.js similarity index 96% rename from scm-ui/src/users/containers/EditUser.js rename to scm-ui/src/users/containers/GeneralUser.js index a8f3276471..ac94b10148 100644 --- a/scm-ui/src/users/containers/EditUser.js +++ b/scm-ui/src/users/containers/GeneralUser.js @@ -27,7 +27,7 @@ type Props = { history: History }; -class EditUser extends React.Component<Props> { +class GeneralUser extends React.Component<Props> { componentDidMount() { const { modifyUserReset, user } = this.props; modifyUserReset(user); @@ -80,4 +80,4 @@ const mapStateToProps = (state, ownProps) => { export default connect( mapStateToProps, mapDispatchToProps -)(withRouter(EditUser)); +)(withRouter(GeneralUser)); diff --git a/scm-ui/src/users/containers/SingleUser.js b/scm-ui/src/users/containers/SingleUser.js index 321e117976..060c55bf70 100644 --- a/scm-ui/src/users/containers/SingleUser.js +++ b/scm-ui/src/users/containers/SingleUser.js @@ -12,7 +12,7 @@ import { } from "@scm-manager/ui-components"; import { Route } from "react-router"; import { Details } from "./../components/table"; -import EditUser from "./EditUser"; +import GeneralUser from "./GeneralUser"; import type { User } from "@scm-manager/ui-types"; import type { History } from "history"; import { @@ -21,7 +21,7 @@ import { isFetchUserPending, getFetchUserFailure } from "../modules/users"; -import { EditUserNavLink, SetPasswordNavLink } from "./../components/navLinks"; +import { GeneralUserNavLink, SetPasswordNavLink } from "./../components/navLinks"; import { translate } from "react-i18next"; import { getUsersLink } from "../../modules/indexResource"; import SetUserPassword from "../components/SetUserPassword"; @@ -84,7 +84,7 @@ class SingleUser extends React.Component<Props> { <Route path={url} exact component={() => <Details user={user} />} /> <Route path={`${url}/settings/general`} - component={() => <EditUser user={user} />} + component={() => <GeneralUser user={user} />} /> <Route path={`${url}/settings/password`} @@ -102,7 +102,7 @@ class SingleUser extends React.Component<Props> { to={`${url}/settings/general`} label={t("single-user.menu.settingsNavLink")} > - <EditUserNavLink + <GeneralUserNavLink user={user} editUrl={`${url}/settings/general`} /> From cb5e74e7915b10ec3bc6f450d8525192c5084f71 Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Wed, 23 Jan 2019 10:08:15 +0100 Subject: [PATCH 017/120] renamed navlink to simple delete --- .../{DeleteNavAction.js => DeleteRepo.js} | 7 +++--- ...teNavAction.test.js => DeleteRepo.test.js} | 12 +++++----- .../containers/{Edit.js => GeneralRepo.js} | 20 ++++++++--------- scm-ui/src/repos/containers/RepositoryRoot.js | 8 +++---- scm-ui/src/users/components/DeleteUser.js | 22 +++++-------------- scm-ui/src/users/containers/GeneralUser.js | 22 +++++++++---------- 6 files changed, 38 insertions(+), 53 deletions(-) rename scm-ui/src/repos/components/{DeleteNavAction.js => DeleteRepo.js} (88%) rename scm-ui/src/repos/components/{DeleteNavAction.test.js => DeleteRepo.test.js} (83%) rename scm-ui/src/repos/containers/{Edit.js => GeneralRepo.js} (86%) diff --git a/scm-ui/src/repos/components/DeleteNavAction.js b/scm-ui/src/repos/components/DeleteRepo.js similarity index 88% rename from scm-ui/src/repos/components/DeleteNavAction.js rename to scm-ui/src/repos/components/DeleteRepo.js index a81cb17f66..512ec9a2f2 100644 --- a/scm-ui/src/repos/components/DeleteNavAction.js +++ b/scm-ui/src/repos/components/DeleteRepo.js @@ -7,19 +7,18 @@ import type { Repository } from "@scm-manager/ui-types"; type Props = { repository: Repository, confirmDialog?: boolean, - delete: Repository => void, // context props t: string => string }; -class DeleteNavAction extends React.Component<Props> { +class DeleteRepo extends React.Component<Props> { static defaultProps = { confirmDialog: true }; delete = () => { - this.props.delete(this.props.repository); + //this.props.delete(this.props.repository); }; confirmDelete = () => { @@ -68,4 +67,4 @@ class DeleteNavAction extends React.Component<Props> { } } -export default translate("repos")(DeleteNavAction); +export default translate("repos")(DeleteRepo); diff --git a/scm-ui/src/repos/components/DeleteNavAction.test.js b/scm-ui/src/repos/components/DeleteRepo.test.js similarity index 83% rename from scm-ui/src/repos/components/DeleteNavAction.test.js rename to scm-ui/src/repos/components/DeleteRepo.test.js index 7c2191864a..7985e02ef2 100644 --- a/scm-ui/src/repos/components/DeleteNavAction.test.js +++ b/scm-ui/src/repos/components/DeleteRepo.test.js @@ -2,7 +2,7 @@ import React from "react"; import { mount, shallow } from "enzyme"; import "../../tests/enzyme"; import "../../tests/i18n"; -import DeleteNavAction from "./DeleteNavAction"; +import DeleteRepo from "./DeleteRepo"; import { confirmAlert } from "@scm-manager/ui-components"; jest.mock("@scm-manager/ui-components", () => ({ @@ -10,14 +10,14 @@ jest.mock("@scm-manager/ui-components", () => ({ NavAction: require.requireActual("@scm-manager/ui-components").NavAction })); -describe("DeleteNavAction", () => { +describe("DeleteRepo", () => { it("should render nothing, if the delete link is missing", () => { const repository = { _links: {} }; const navLink = shallow( - <DeleteNavAction repository={repository} delete={() => {}} /> + <DeleteRepo repository={repository} delete={() => {}} /> ); expect(navLink.text()).toBe(""); }); @@ -32,7 +32,7 @@ describe("DeleteNavAction", () => { }; const navLink = mount( - <DeleteNavAction repository={repository} delete={() => {}} /> + <DeleteRepo repository={repository} delete={() => {}} /> ); expect(navLink.text()).not.toBe(""); }); @@ -47,7 +47,7 @@ describe("DeleteNavAction", () => { }; const navLink = mount( - <DeleteNavAction repository={repository} delete={() => {}} /> + <DeleteRepo repository={repository} delete={() => {}} /> ); navLink.find("a").simulate("click"); @@ -69,7 +69,7 @@ describe("DeleteNavAction", () => { } const navLink = mount( - <DeleteNavAction + <DeleteRepo repository={repository} confirmDialog={false} delete={capture} diff --git a/scm-ui/src/repos/containers/Edit.js b/scm-ui/src/repos/containers/GeneralRepo.js similarity index 86% rename from scm-ui/src/repos/containers/Edit.js rename to scm-ui/src/repos/containers/GeneralRepo.js index 7341bcbd3e..70cc9f9adc 100644 --- a/scm-ui/src/repos/containers/Edit.js +++ b/scm-ui/src/repos/containers/GeneralRepo.js @@ -1,8 +1,9 @@ // @flow import React from "react"; import { connect } from "react-redux"; -import { translate } from "react-i18next"; +import { withRouter } from "react-router-dom"; import RepositoryForm from "../components/form"; +import DeleteRepo from "../components/DeleteRepo"; import type { Repository } from "@scm-manager/ui-types"; import { modifyRepo, @@ -10,23 +11,22 @@ import { getModifyRepoFailure, modifyRepoReset } from "../modules/repos"; -import { withRouter } from "react-router-dom"; import type { History } from "history"; import { ErrorNotification } from "@scm-manager/ui-components"; type Props = { - repository: Repository, - modifyRepo: (Repository, () => void) => void, - modifyRepoReset: Repository => void, loading: boolean, error: Error, + modifyRepo: (Repository, () => void) => void, + modifyRepoReset: Repository => void, + // context props - t: string => string, + repository: Repository, history: History }; -class Edit extends React.Component<Props> { +class GeneralRepo extends React.Component<Props> { componentDidMount() { const { modifyRepoReset, repository } = this.props; modifyRepoReset(repository); @@ -37,7 +37,7 @@ class Edit extends React.Component<Props> { }; render() { - const { loading, error } = this.props; + const { loading, error, repository } = this.props; return ( <div> <ErrorNotification error={error} /> @@ -49,7 +49,7 @@ class Edit extends React.Component<Props> { }} /> <hr /> - <p>TODO: DeleteRepo hier einbinden. Aktuell heißt es noch DeleteNavAction</p> + <DeleteRepo repository={repository} /> </div> ); } @@ -79,4 +79,4 @@ const mapDispatchToProps = dispatch => { export default connect( mapStateToProps, mapDispatchToProps -)(translate("repos")(withRouter(Edit))); +)(withRouter(GeneralRepo)); diff --git a/scm-ui/src/repos/containers/RepositoryRoot.js b/scm-ui/src/repos/containers/RepositoryRoot.js index 1a7ca7eaea..1de1a9dd89 100644 --- a/scm-ui/src/repos/containers/RepositoryRoot.js +++ b/scm-ui/src/repos/containers/RepositoryRoot.js @@ -9,12 +9,10 @@ import type {Repository} from "@scm-manager/ui-types"; import {ErrorPage, Loading, Navigation, SubNavigation, NavLink, Page, Section} from "@scm-manager/ui-components"; import {translate} from "react-i18next"; import RepositoryDetails from "../components/RepositoryDetails"; -import DeleteNavAction from "../components/DeleteNavAction"; -import Edit from "../containers/Edit"; +import GeneralRepo from "./GeneralRepo"; import Permissions from "../permissions/containers/Permissions"; import type {History} from "history"; -import EditNavLink from "../components/EditNavLink"; import BranchRoot from "./ChangesetsRoot"; import ChangesetView from "./ChangesetView"; @@ -110,7 +108,7 @@ class RepositoryRoot extends React.Component<Props> { /> <Route path={`${url}/settings/general`} - component={() => <Edit repository={repository} />} + component={() => <GeneralRepo repository={repository} />} /> <Route path={`${url}/settings/permissions`} @@ -189,7 +187,7 @@ class RepositoryRoot extends React.Component<Props> { to={`${url}/settings/general`} label={t("repository-root.menu.settingsNavLink")} > - <EditNavLink repository={repository} editUrl={`${url}/settings/general`} /> + <NavLink repository={repository} editUrl={`${url}/settings/general`} /> <PermissionsNavLink permissionUrl={`${url}/settings/permissions`} repository={repository} diff --git a/scm-ui/src/users/components/DeleteUser.js b/scm-ui/src/users/components/DeleteUser.js index 1a250fa58d..95fbe0aa1d 100644 --- a/scm-ui/src/users/components/DeleteUser.js +++ b/scm-ui/src/users/components/DeleteUser.js @@ -1,20 +1,8 @@ // @flow import React from "react"; import { translate } from "react-i18next"; +import { Subtitle, DeleteButton, confirmAlert } from "@scm-manager/ui-components"; import type { User } from "@scm-manager/ui-types"; -import { - Subtitle, - DeleteButton, - confirmAlert -} from "@scm-manager/ui-components"; -import { connect } from "react-redux"; -import { - deleteUser, - fetchUserByName, - getDeleteUserFailure, - getUserByName, - isDeleteUserPending -} from "../modules/users"; import type { History } from "history"; type Props = { @@ -31,6 +19,10 @@ type Props = { }; class DeleteUser extends React.Component<Props> { + static defaultProps = { + confirmDialog: true + }; + userDeleted = () => { this.props.history.push("/users"); }; @@ -39,10 +31,6 @@ class DeleteUser extends React.Component<Props> { this.props.deleteUser(user, this.userDeleted); }; - static defaultProps = { - confirmDialog: true - }; - deleteUser = () => { this.props.deleteUser(this.props.user); }; diff --git a/scm-ui/src/users/containers/GeneralUser.js b/scm-ui/src/users/containers/GeneralUser.js index ac94b10148..f386994460 100644 --- a/scm-ui/src/users/containers/GeneralUser.js +++ b/scm-ui/src/users/containers/GeneralUser.js @@ -2,8 +2,8 @@ import React from "react"; import { connect } from "react-redux"; import { withRouter } from "react-router-dom"; -import UserForm from "./../components/UserForm"; -import DeleteUser from "./../components/DeleteUser"; +import UserForm from "../components/UserForm"; +import DeleteUser from "../components/DeleteUser"; import type { User } from "@scm-manager/ui-types"; import { modifyUser, @@ -57,6 +57,15 @@ class GeneralUser extends React.Component<Props> { } } +const mapStateToProps = (state, ownProps) => { + const loading = isModifyUserPending(state, ownProps.user.name); + const error = getModifyUserFailure(state, ownProps.user.name); + return { + loading, + error + }; +}; + const mapDispatchToProps = dispatch => { return { modifyUser: (user: User, callback?: () => void) => { @@ -68,15 +77,6 @@ const mapDispatchToProps = dispatch => { }; }; -const mapStateToProps = (state, ownProps) => { - const loading = isModifyUserPending(state, ownProps.user.name); - const error = getModifyUserFailure(state, ownProps.user.name); - return { - loading, - error - }; -}; - export default connect( mapStateToProps, mapDispatchToProps From ff3044c365f2196332277c60bf3a2b2cd95ed863 Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Wed, 23 Jan 2019 11:14:30 +0100 Subject: [PATCH 018/120] lang file anpassen + refactoring --- .../src/repos/changesets/ChangesetDiff.js | 2 +- scm-ui/public/locales/en/repos.json | 160 +++++----- scm-ui/public/locales/en/users.json | 78 +++-- scm-ui/src/repos/components/DeleteRepo.js | 12 +- scm-ui/src/repos/components/EditNavLink.js | 2 +- .../src/repos/components/EditNavLink.test.js | 2 +- .../repos/components/PermissionsNavLink.js | 2 +- .../repos/components/form/RepositoryForm.js | 47 ++- scm-ui/src/repos/containers/ChangesetView.js | 4 +- scm-ui/src/repos/containers/ChangesetsRoot.js | 2 +- scm-ui/src/repos/containers/Overview.js | 2 +- scm-ui/src/repos/containers/RepositoryRoot.js | 24 +- .../src/repos/sources/containers/Sources.js | 2 +- scm-ui/src/users/components/DeleteUser.js | 12 +- .../src/users/components/SetUserPassword.js | 4 +- scm-ui/src/users/components/UserForm.js | 4 +- .../components/buttons/CreateUserButton.js | 20 -- .../components/navLinks/GeneralUserNavLink.js | 2 +- .../components/navLinks/SetPasswordNavLink.js | 2 +- scm-ui/src/users/containers/AddUser.js | 4 +- scm-ui/src/users/containers/SingleUser.js | 10 +- scm-ui/src/users/containers/Users.js | 286 +++++++++--------- 22 files changed, 326 insertions(+), 357 deletions(-) delete mode 100644 scm-ui/src/users/components/buttons/CreateUserButton.js diff --git a/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetDiff.js b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetDiff.js index 857ff8c827..232b0e3577 100644 --- a/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetDiff.js +++ b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetDiff.js @@ -25,7 +25,7 @@ class ChangesetDiff extends React.Component<Props> { render() { const { changeset, t } = this.props; if (!this.isDiffSupported(changeset)) { - return <Notification type="danger">{t("changesets.diff.not-supported")}</Notification>; + return <Notification type="danger">{t("changesets.changeset.diffNotSupported")}</Notification>; } else { const url = this.createUrl(changeset); return <LoadingDiff url={url} />; diff --git a/scm-ui/public/locales/en/repos.json b/scm-ui/public/locales/en/repos.json index 4693dedfd6..a7a7d8449f 100644 --- a/scm-ui/public/locales/en/repos.json +++ b/scm-ui/public/locales/en/repos.json @@ -11,12 +11,15 @@ "name-invalid": "The repository name is invalid", "contact-invalid": "Contact must be a valid mail address" }, - "overview": { - "title": "Repositories", - "subtitle": "Overview of available repositories", - "create-button": "Create" + "help": { + "nameHelpText": "The name of the repository. This name will be part of the repository url.", + "typeHelpText": "The type of the repository (e.g. Mercurial, Git or Subversion).", + "contactHelpText": "Email address of the person who is responsible for this repository.", + "descriptionHelpText": "A short description of the repository." }, - "repository-root": { + "repositoryRoot": { + "errorTitle": "Error", + "errorSubtitle": "Unknown repository error", "menu": { "navigationLabel": "Repository Navigation", "informationNavLink": "Information", @@ -25,29 +28,37 @@ "settingsNavLink": "Settings", "editNavLink": "General", "permissionsNavLink": "Permissions" - }, - "errorTitle": "Error", - "errorSubtitle": "Unknown repository error" + } + }, + "overview": { + "title": "Repositories", + "subtitle": "Overview of available repositories", + "createButton": "Create" }, "create": { "title": "Create Repository", "subtitle": "Create a new repository" }, - "repository-form": { - "submit": "Save" - }, - "edit-nav-link": { - "label": "Edit" - }, - "delete-nav-action": { - "label": "Delete", - "confirm-alert": { - "title": "Delete repository", - "message": "Do you really want to delete the repository?", - "submit": "Yes", - "cancel": "No" + "changesets": { + "errorTitle": "Error", + "errorSubtitle": "Could not fetch changesets", + "branchSelectorLabel": "Branches", + "changeset": { + "description": "Description", + "summary": "Changeset {{id}} was committed {{time}}", + "diffNotSupported": "Diff of changesets is not supported by the type of repository", + "id": "ID", + "contact": "Contact", + "date": "Date" + }, + "author": { + "name": "Author", + "mail": "Mail" } }, + "repositoryForm": { + "submit": "Save" + }, "sources": { "file-tree": { "name": "Name", @@ -67,71 +78,54 @@ "size": "Size" } }, - "changesets": { - "diff": { - "not-supported": "Diff of changesets is not supported by the type of repository" - }, + "permission": { + "user": "User", + "group": "Group", "error-title": "Error", - "error-subtitle": "Could not fetch changesets", - "changeset": { - "id": "ID", - "description": "Description", - "contact": "Contact", - "date": "Date", - "summary": "Changeset {{id}} was committed {{time}}" + "error-subtitle": "Unknown permissions error", + "name": "User or Group", + "type": "Type", + "group-permission": "Group Permission", + "user-permission": "User Permission", + "edit-permission": { + "delete-button": "Delete", + "save-button": "Save Changes" }, - "author": { - "name": "Author", - "mail": "Mail" + "delete-permission-button": { + "label": "Delete", + "confirm-alert": { + "title": "Delete permission", + "message": "Do you really want to delete the permission?", + "submit": "Yes", + "cancel": "No" + } + }, + "add-permission": { + "add-permission-heading": "Add new Permission", + "submit-button": "Submit", + "name-input-invalid": "Permission is not allowed to be empty! If it is not empty, your input name is invalid or it already exists!" + }, + "help": { + "groupPermissionHelpText": "States if a permission is a group permission.", + "nameHelpText": "Manage permissions for a specific user or group", + "typeHelpText": "READ = read; WRITE = read and write; OWNER = read, write and also the ability to manage the properties and permissions" + }, + "autocomplete": { + "no-group-options": "No group suggestion available", + "group-placeholder": "Enter group", + "no-user-options": "No user suggestion available", + "user-placeholder": "Enter user", + "loading": "Loading..." } }, - "branch-selector": { - "label": "Branches" - }, - "permission": { - "user": "User", - "group": "Group", - "error-title": "Error", - "error-subtitle": "Unknown permissions error", - "name": "User or Group", - "type": "Type", - "group-permission": "Group Permission", - "user-permission": "User Permission", - "edit-permission": { - "delete-button": "Delete", - "save-button": "Save Changes" - }, - "delete-permission-button": { - "label": "Delete", - "confirm-alert": { - "title": "Delete permission", - "message": "Do you really want to delete the permission?", - "submit": "Yes", - "cancel": "No" - } - }, - "add-permission": { - "add-permission-heading": "Add new Permission", - "submit-button": "Submit", - "name-input-invalid": "Permission is not allowed to be empty! If it is not empty, your input name is invalid or it already exists!" - }, - "help": { - "groupPermissionHelpText": "States if a permission is a group permission.", - "nameHelpText": "Manage permissions for a specific user or group", - "typeHelpText": "READ = read; WRITE = read and write; OWNER = read, write and also the ability to manage the properties and permissions" - }, - "autocomplete": { - "no-group-options": "No group suggestion available", - "group-placeholder": "Enter group", - "no-user-options": "No user suggestion available", - "user-placeholder": "Enter user", - "loading": "Loading..." - } - }, - "help": { - "nameHelpText": "The name of the repository. This name will be part of the repository url.", - "typeHelpText": "The type of the repository (e.g. Mercurial, Git or Subversion).", - "contactHelpText": "Email address of the person who is responsible for this repository.", - "descriptionHelpText": "A short description of the repository." + "delete": { + "subtitle": "Delete Repository", + "button": "Delete", + "confirmAlert": { + "title": "Delete repository", + "message": "Do you really want to delete the repository?", + "submit": "Yes", + "cancel": "No" + } } } diff --git a/scm-ui/public/locales/en/users.json b/scm-ui/public/locales/en/users.json index 0ec61c9870..394d23dbb0 100644 --- a/scm-ui/public/locales/en/users.json +++ b/scm-ui/public/locales/en/users.json @@ -10,46 +10,6 @@ "creationDate": "Creation Date", "lastModified": "Last Modified" }, - "users": { - "title": "Users", - "subtitle": "Create, read, update and delete users" - }, - "create-user-button": { - "label": "Create" - }, - "add-user": { - "title": "Create User", - "subtitle": "Create a new user" - }, - "single-user": { - "menu": { - "navigationLabel": "User Navigation", - "informationNavLink": "Information", - "settingsNavLink": "Settings", - "editNavLink": "General", - "setPasswordNavLink": "Password" - }, - "edit": { - "subtitle": "Edit User", - "button": "Submit" - }, - "delete": { - "subtitle": "Delete User", - "button": "Delete", - "confirm-alert": { - "title": "Delete user", - "message": "Do you really want to delete the user?", - "submit": "Yes", - "cancel": "No" - } - }, - "password": { - "button": "Set password", - "set-password-successful": "Password successfully set" - }, - "errorTitle": "Error", - "errorSubtitle": "Unknown user error" - }, "validation": { "mail-invalid": "This email is invalid", "name-invalid": "This name is invalid", @@ -61,5 +21,43 @@ "mailHelpText": "Email address of the user.", "adminHelpText": "An administrator is able to create, modify and delete repositories, groups and users.", "activeHelpText": "Activate or deactive the user." + }, + "users": { + "title": "Users", + "subtitle": "Create, read, update and delete users", + "createButton": "Create" + }, + "singleUser": { + "errorTitle": "Error", + "errorSubtitle": "Unknown user error", + "menu": { + "navigationLabel": "User Navigation", + "informationNavLink": "Information", + "settingsNavLink": "Settings", + "editNavLink": "General", + "setPasswordNavLink": "Password" + } + }, + "addUser": { + "title": "Create User", + "subtitle": "Create a new user" + }, + "delete": { + "subtitle": "Delete User", + "button": "Delete", + "confirm-alert": { + "title": "Delete user", + "message": "Do you really want to delete the user?", + "submit": "Yes", + "cancel": "No" + } + }, + "singleUserPassword": { + "button": "Set password", + "setPasswordSuccessful": "Password successfully set" + }, + "userForm": { + "subtitle": "Edit User", + "button": "Submit" } } diff --git a/scm-ui/src/repos/components/DeleteRepo.js b/scm-ui/src/repos/components/DeleteRepo.js index 512ec9a2f2..d1908f9daf 100644 --- a/scm-ui/src/repos/components/DeleteRepo.js +++ b/scm-ui/src/repos/components/DeleteRepo.js @@ -24,15 +24,15 @@ class DeleteRepo extends React.Component<Props> { confirmDelete = () => { const { t } = this.props; confirmAlert({ - title: t("repository.delete.confirm-alert.title"), - message: t("repository.delete.confirm-alert.message"), + title: t("delete.confirmAlert.title"), + message: t("delete.confirmAlert.message"), buttons: [ { - label: t("repository.delete.confirm-alert.submit"), + label: t("delete.confirmAlert.submit"), onClick: () => this.delete() }, { - label: t("repository.delete.confirm-alert.cancel"), + label: t("delete.confirmAlert.cancel"), onClick: () => null } ] @@ -53,11 +53,11 @@ class DeleteRepo extends React.Component<Props> { return ( <> - <Subtitle subtitle={t("repository.delete.subtitle")} /> + <Subtitle subtitle={t("delete.subtitle")} /> <div className="columns"> <div className="column"> <DeleteButton - label={t("repository.delete.button")} + label={t("delete.button")} action={action} /> </div> diff --git a/scm-ui/src/repos/components/EditNavLink.js b/scm-ui/src/repos/components/EditNavLink.js index 2163624e4d..b06cd8d96e 100644 --- a/scm-ui/src/repos/components/EditNavLink.js +++ b/scm-ui/src/repos/components/EditNavLink.js @@ -15,7 +15,7 @@ class EditNavLink extends React.Component<Props> { return null; } const { editUrl, t } = this.props; - return <NavLink to={editUrl} label={t("repository-root.menu.editNavLink")} />; + return <NavLink to={editUrl} label={t("repositoryRoot.menu.editNavLink")} />; } } diff --git a/scm-ui/src/repos/components/EditNavLink.test.js b/scm-ui/src/repos/components/EditNavLink.test.js index 935b7cf928..080440cc88 100644 --- a/scm-ui/src/repos/components/EditNavLink.test.js +++ b/scm-ui/src/repos/components/EditNavLink.test.js @@ -33,6 +33,6 @@ describe("EditNavLink", () => { <EditNavLink repository={repository} editUrl="" />, options.get() ); - expect(navLink.text()).toBe("edit-nav-link.label"); + expect(navLink.text()).toBe("repositoryRoot.menu.editNavLink"); }); }); diff --git a/scm-ui/src/repos/components/PermissionsNavLink.js b/scm-ui/src/repos/components/PermissionsNavLink.js index 364c274f6b..773ad94246 100644 --- a/scm-ui/src/repos/components/PermissionsNavLink.js +++ b/scm-ui/src/repos/components/PermissionsNavLink.js @@ -20,7 +20,7 @@ class PermissionsNavLink extends React.Component<Props> { } const { permissionUrl, t } = this.props; return ( - <NavLink to={permissionUrl} label={t("repository-root.menu.permissionsNavLink")} /> + <NavLink to={permissionUrl} label={t("repositoryRoot.menu.permissionsNavLink")} /> ); } } diff --git a/scm-ui/src/repos/components/form/RepositoryForm.js b/scm-ui/src/repos/components/form/RepositoryForm.js index cc0b409175..a97c76af8b 100644 --- a/scm-ui/src/repos/components/form/RepositoryForm.js +++ b/scm-ui/src/repos/components/form/RepositoryForm.js @@ -83,32 +83,29 @@ class RepositoryForm extends React.Component<Props, State> { const repository = this.state.repository; return ( - <> - <Subtitle subtitle={t("repository.edit.subtitle")} /> - <form onSubmit={this.submit}> - {this.renderCreateOnlyFields()} - <InputField - label={t("repository.contact")} - onChange={this.handleContactChange} - value={repository ? repository.contact : ""} - validationError={this.state.contactValidationError} - errorMessage={t("validation.contact-invalid")} - helpText={t("help.contactHelpText")} - /> + <form onSubmit={this.submit}> + {this.renderCreateOnlyFields()} + <InputField + label={t("repository.contact")} + onChange={this.handleContactChange} + value={repository ? repository.contact : ""} + validationError={this.state.contactValidationError} + errorMessage={t("validation.contact-invalid")} + helpText={t("help.contactHelpText")} + /> - <Textarea - label={t("repository.description")} - onChange={this.handleDescriptionChange} - value={repository ? repository.description : ""} - helpText={t("help.descriptionHelpText")} - /> - <SubmitButton - disabled={!this.isValid()} - loading={loading} - label={t("repository-form.submit")} - /> - </form> - </> + <Textarea + label={t("repository.description")} + onChange={this.handleDescriptionChange} + value={repository ? repository.description : ""} + helpText={t("help.descriptionHelpText")} + /> + <SubmitButton + disabled={!this.isValid()} + loading={loading} + label={t("repositoryForm.submit")} + /> + </form> ); } diff --git a/scm-ui/src/repos/containers/ChangesetView.js b/scm-ui/src/repos/containers/ChangesetView.js index 80ab0b71d6..dc53b5d798 100644 --- a/scm-ui/src/repos/containers/ChangesetView.js +++ b/scm-ui/src/repos/containers/ChangesetView.js @@ -37,8 +37,8 @@ class ChangesetView extends React.Component<Props> { if (error) { return ( <ErrorPage - title={t("changeset-error.title")} - subtitle={t("changeset-error.subtitle")} + title={t("changesets.errorTitle")} + subtitle={t("changesets.errorSubtitle")} error={error} /> ); diff --git a/scm-ui/src/repos/containers/ChangesetsRoot.js b/scm-ui/src/repos/containers/ChangesetsRoot.js index 2eea64b8e1..c49904762a 100644 --- a/scm-ui/src/repos/containers/ChangesetsRoot.js +++ b/scm-ui/src/repos/containers/ChangesetsRoot.js @@ -101,7 +101,7 @@ class BranchRoot extends React.Component<Props> { if (repository._links.branches) { return ( <BranchSelector - label={t("branch-selector.label")} + label={t("changesets.branchSelectorLabel")} branches={branches} selectedBranch={selected} selected={(b: Branch) => { diff --git a/scm-ui/src/repos/containers/Overview.js b/scm-ui/src/repos/containers/Overview.js index bbafe14539..598b6c94f2 100644 --- a/scm-ui/src/repos/containers/Overview.js +++ b/scm-ui/src/repos/containers/Overview.js @@ -90,7 +90,7 @@ class Overview extends React.Component<Props> { if (showCreateButton) { return ( <CreateButton - label={t("overview.create-button")} + label={t("overview.createButton")} link="/repos/create" /> ); diff --git a/scm-ui/src/repos/containers/RepositoryRoot.js b/scm-ui/src/repos/containers/RepositoryRoot.js index 1de1a9dd89..fcbe5dfee1 100644 --- a/scm-ui/src/repos/containers/RepositoryRoot.js +++ b/scm-ui/src/repos/containers/RepositoryRoot.js @@ -78,8 +78,8 @@ class RepositoryRoot extends React.Component<Props> { if (error) { return ( <ErrorPage - title={t("repository-root.errorTitle")} - subtitle={t("repository-root.errorSubtitle")} + title={t("repositoryRoot.errorTitle")} + subtitle={t("repositoryRoot.errorSubtitle")} error={error} /> ); @@ -166,13 +166,13 @@ class RepositoryRoot extends React.Component<Props> { </div> <div className="column"> <Navigation> - <Section label={t("repository-root.menu.navigationLabel")}> - <NavLink to={url} label={t("repository-root.menu.informationNavLink")} /> + <Section label={t("repositoryRoot.menu.navigationLabel")}> + <NavLink to={url} label={t("repositoryRoot.menu.informationNavLink")} /> <RepositoryNavLink repository={repository} linkName="changesets" to={`${url}/changesets/`} - label={t("repository-root.menu.historyNavLink")} + label={t("repositoryRoot.menu.historyNavLink")} activeWhenMatch={this.matches} activeOnlyWhenExact={false} /> @@ -180,23 +180,23 @@ class RepositoryRoot extends React.Component<Props> { repository={repository} linkName="sources" to={`${url}/sources`} - label={t("repository-root.menu.sourcesNavLink")} + label={t("repositoryRoot.menu.sourcesNavLink")} activeOnlyWhenExact={false} /> + <ExtensionPoint + name="repository.navigation" + props={extensionProps} + renderAll={true} + /> <SubNavigation to={`${url}/settings/general`} - label={t("repository-root.menu.settingsNavLink")} + label={t("repositoryRoot.menu.settingsNavLink")} > <NavLink repository={repository} editUrl={`${url}/settings/general`} /> <PermissionsNavLink permissionUrl={`${url}/settings/permissions`} repository={repository} /> - <ExtensionPoint - name="repository.navigation" - props={extensionProps} - renderAll={true} - /> </SubNavigation> </Section> </Navigation> diff --git a/scm-ui/src/repos/sources/containers/Sources.js b/scm-ui/src/repos/sources/containers/Sources.js index 810e2309ee..66c1b7bdf5 100644 --- a/scm-ui/src/repos/sources/containers/Sources.js +++ b/scm-ui/src/repos/sources/containers/Sources.js @@ -118,7 +118,7 @@ class Sources extends React.Component<Props> { <BranchSelector branches={branches} selectedBranch={revision} - label={t("branch-selector.label")} + label={t("changesets.branchSelectorLabel")} selected={(b: Branch) => { this.branchSelected(b); }} diff --git a/scm-ui/src/users/components/DeleteUser.js b/scm-ui/src/users/components/DeleteUser.js index 95fbe0aa1d..ec3bf57a15 100644 --- a/scm-ui/src/users/components/DeleteUser.js +++ b/scm-ui/src/users/components/DeleteUser.js @@ -38,15 +38,15 @@ class DeleteUser extends React.Component<Props> { confirmDelete = () => { const { t } = this.props; confirmAlert({ - title: t("single-user.delete.confirm-alert.title"), - message: t("single-user.delete.confirm-alert.message"), + title: t("delete.confirm-alert.title"), + message: t("delete.confirm-alert.message"), buttons: [ { - label: t("single-user.delete.confirm-alert.submit"), + label: t("delete.confirm-alert.submit"), onClick: () => this.deleteUser() }, { - label: t("single-user.delete.confirm-alert.cancel"), + label: t("delete.confirm-alert.cancel"), onClick: () => null } ] @@ -66,11 +66,11 @@ class DeleteUser extends React.Component<Props> { } return ( <> - <Subtitle subtitle={t("single-user.delete.subtitle")} /> + <Subtitle subtitle={t("delete.subtitle")} /> <div className="columns"> <div className="column"> <DeleteButton - label={t("single-user.delete.button")} + label={t("delete.button")} action={action} /> </div> diff --git a/scm-ui/src/users/components/SetUserPassword.js b/scm-ui/src/users/components/SetUserPassword.js index eadc2002c1..a0fe844b0c 100644 --- a/scm-ui/src/users/components/SetUserPassword.js +++ b/scm-ui/src/users/components/SetUserPassword.js @@ -90,7 +90,7 @@ class SetUserPassword extends React.Component<Props, State> { message = ( <Notification type={"success"} - children={t("single-user.password.set-password-successful")} + children={t("singleUserPassword.setPasswordSuccessful")} onClose={() => this.onClose()} /> ); @@ -108,7 +108,7 @@ class SetUserPassword extends React.Component<Props, State> { <SubmitButton disabled={!this.state.passwordValid} loading={loading} - label={t("single-user.password.button")} + label={t("singleUserPassword.button")} /> </form> ); diff --git a/scm-ui/src/users/components/UserForm.js b/scm-ui/src/users/components/UserForm.js index 0f6119ef94..d01838f08a 100644 --- a/scm-ui/src/users/components/UserForm.js +++ b/scm-ui/src/users/components/UserForm.js @@ -106,7 +106,7 @@ class UserForm extends React.Component<Props, State> { } return ( <> - <Subtitle subtitle={t("single-user.edit.subtitle")} /> + <Subtitle subtitle={t("userForm.subtitle")} /> <form onSubmit={this.submit}> <div className="columns"> <div className="column is-half"> @@ -153,7 +153,7 @@ class UserForm extends React.Component<Props, State> { <SubmitButton disabled={!this.isValid()} loading={loading} - label={t("single-user.edit.button")} + label={t("userForm.button")} /> </div> </div> diff --git a/scm-ui/src/users/components/buttons/CreateUserButton.js b/scm-ui/src/users/components/buttons/CreateUserButton.js deleted file mode 100644 index f34820cd0d..0000000000 --- a/scm-ui/src/users/components/buttons/CreateUserButton.js +++ /dev/null @@ -1,20 +0,0 @@ -//@flow -import React from "react"; -import { translate } from "react-i18next"; -import { CreateButton } from "@scm-manager/ui-components"; - -// TODO remove -type Props = { - t: string => string -}; - -class CreateUserButton extends React.Component<Props> { - render() { - const { t } = this.props; - return ( - <CreateButton label={t("create-user-button.label")} link="/users/add" /> - ); - } -} - -export default translate("users")(CreateUserButton); diff --git a/scm-ui/src/users/components/navLinks/GeneralUserNavLink.js b/scm-ui/src/users/components/navLinks/GeneralUserNavLink.js index a6f1201152..d4bce5b8b5 100644 --- a/scm-ui/src/users/components/navLinks/GeneralUserNavLink.js +++ b/scm-ui/src/users/components/navLinks/GeneralUserNavLink.js @@ -17,7 +17,7 @@ class GeneralUserNavLink extends React.Component<Props> { if (!this.isEditable()) { return null; } - return <NavLink label={t("single-user.menu.editNavLink")} to={editUrl} />; + return <NavLink label={t("singleUser.menu.editNavLink")} to={editUrl} />; } isEditable = () => { diff --git a/scm-ui/src/users/components/navLinks/SetPasswordNavLink.js b/scm-ui/src/users/components/navLinks/SetPasswordNavLink.js index bea1bd2981..7dfd807a93 100644 --- a/scm-ui/src/users/components/navLinks/SetPasswordNavLink.js +++ b/scm-ui/src/users/components/navLinks/SetPasswordNavLink.js @@ -17,7 +17,7 @@ class ChangePasswordNavLink extends React.Component<Props> { if (!this.hasPermissionToSetPassword()) { return null; } - return <NavLink label={t("single-user.menu.setPasswordNavLink")} to={passwordUrl} />; + return <NavLink label={t("singleUser.menu.setPasswordNavLink")} to={passwordUrl} />; } hasPermissionToSetPassword = () => { diff --git a/scm-ui/src/users/containers/AddUser.js b/scm-ui/src/users/containers/AddUser.js index 1ee6fc759d..1946f7849a 100644 --- a/scm-ui/src/users/containers/AddUser.js +++ b/scm-ui/src/users/containers/AddUser.js @@ -47,8 +47,8 @@ class AddUser extends React.Component<Props> { return ( <Page - title={t("add-user.title")} - subtitle={t("add-user.subtitle")} + title={t("addUser.title")} + subtitle={t("addUser.subtitle")} error={error} showContentOnError={true} > diff --git a/scm-ui/src/users/containers/SingleUser.js b/scm-ui/src/users/containers/SingleUser.js index 060c55bf70..65d94a7395 100644 --- a/scm-ui/src/users/containers/SingleUser.js +++ b/scm-ui/src/users/containers/SingleUser.js @@ -64,8 +64,8 @@ class SingleUser extends React.Component<Props> { if (error) { return ( <ErrorPage - title={t("single-user.errorTitle")} - subtitle={t("single-user.errorSubtitle")} + title={t("singleUser.errorTitle")} + subtitle={t("singleUser.errorSubtitle")} error={error} /> ); @@ -93,14 +93,14 @@ class SingleUser extends React.Component<Props> { </div> <div className="column"> <Navigation> - <Section label={t("single-user.menu.navigationLabel")}> + <Section label={t("singleUser.menu.navigationLabel")}> <NavLink to={`${url}`} - label={t("single-user.menu.informationNavLink")} + label={t("singleUser.menu.informationNavLink")} /> <SubNavigation to={`${url}/settings/general`} - label={t("single-user.menu.settingsNavLink")} + label={t("singleUser.menu.settingsNavLink")} > <GeneralUserNavLink user={user} diff --git a/scm-ui/src/users/containers/Users.js b/scm-ui/src/users/containers/Users.js index cbb33dc68b..041ac226b4 100644 --- a/scm-ui/src/users/containers/Users.js +++ b/scm-ui/src/users/containers/Users.js @@ -1,143 +1,143 @@ -// @flow -import React from "react"; -import type { History } from "history"; -import { connect } from "react-redux"; -import { translate } from "react-i18next"; - -import { - fetchUsersByPage, - fetchUsersByLink, - getUsersFromState, - selectListAsCollection, - isPermittedToCreateUsers, - isFetchUsersPending, - getFetchUsersFailure -} from "../modules/users"; - -import { Page, Paginator } from "@scm-manager/ui-components"; -import { UserTable } from "./../components/table"; -import type { User, PagedCollection } from "@scm-manager/ui-types"; -import CreateUserButton from "../components/buttons/CreateUserButton"; -import { getUsersLink } from "../../modules/indexResource"; - -type Props = { - users: User[], - loading: boolean, - error: Error, - canAddUsers: boolean, - list: PagedCollection, - page: number, - usersLink: string, - - // context objects - t: string => string, - history: History, - - // dispatch functions - fetchUsersByPage: (link: string, page: number) => void, - fetchUsersByLink: (link: string) => void -}; - -class Users extends React.Component<Props> { - componentDidMount() { - this.props.fetchUsersByPage(this.props.usersLink, this.props.page); - } - - onPageChange = (link: string) => { - this.props.fetchUsersByLink(link); - }; - - /** - * reflect page transitions in the uri - */ - componentDidUpdate() { - const { page, list } = this.props; - if (list && (list.page || list.page === 0)) { - // backend starts paging by 0 - const statePage: number = list.page + 1; - if (page !== statePage) { - this.props.history.push(`/users/${statePage}`); - } - } - } - - render() { - const { users, loading, error, t } = this.props; - return ( - <Page - title={t("users.title")} - subtitle={t("users.subtitle")} - loading={loading || !users} - error={error} - > - <UserTable users={users} /> - {this.renderPaginator()} - {this.renderCreateButton()} - </Page> - ); - } - - renderPaginator() { - const { list } = this.props; - if (list) { - return <Paginator collection={list} onPageChange={this.onPageChange} />; - } - return null; - } - - renderCreateButton() { - if (this.props.canAddUsers) { - return <CreateUserButton />; - } else { - return; - } - } -} - -const getPageFromProps = props => { - let page = props.match.params.page; - if (page) { - page = parseInt(page, 10); - } else { - page = 1; - } - return page; -}; - -const mapStateToProps = (state, ownProps) => { - const users = getUsersFromState(state); - const loading = isFetchUsersPending(state); - const error = getFetchUsersFailure(state); - - const usersLink = getUsersLink(state); - - const page = getPageFromProps(ownProps); - const canAddUsers = isPermittedToCreateUsers(state); - const list = selectListAsCollection(state); - - return { - users, - loading, - error, - canAddUsers, - list, - page, - usersLink - }; -}; - -const mapDispatchToProps = dispatch => { - return { - fetchUsersByPage: (link: string, page: number) => { - dispatch(fetchUsersByPage(link, page)); - }, - fetchUsersByLink: (link: string) => { - dispatch(fetchUsersByLink(link)); - } - }; -}; - -export default connect( - mapStateToProps, - mapDispatchToProps -)(translate("users")(Users)); +// @flow +import React from "react"; +import type { History } from "history"; +import { connect } from "react-redux"; +import { translate } from "react-i18next"; + +import { + fetchUsersByPage, + fetchUsersByLink, + getUsersFromState, + selectListAsCollection, + isPermittedToCreateUsers, + isFetchUsersPending, + getFetchUsersFailure +} from "../modules/users"; + +import { Page, CreateButton, Paginator } from "@scm-manager/ui-components"; +import { UserTable } from "./../components/table"; +import type { User, PagedCollection } from "@scm-manager/ui-types"; +import { getUsersLink } from "../../modules/indexResource"; + +type Props = { + users: User[], + loading: boolean, + error: Error, + canAddUsers: boolean, + list: PagedCollection, + page: number, + usersLink: string, + + // context objects + t: string => string, + history: History, + + // dispatch functions + fetchUsersByPage: (link: string, page: number) => void, + fetchUsersByLink: (link: string) => void +}; + +class Users extends React.Component<Props> { + componentDidMount() { + this.props.fetchUsersByPage(this.props.usersLink, this.props.page); + } + + onPageChange = (link: string) => { + this.props.fetchUsersByLink(link); + }; + + /** + * reflect page transitions in the uri + */ + componentDidUpdate() { + const { page, list } = this.props; + if (list && (list.page || list.page === 0)) { + // backend starts paging by 0 + const statePage: number = list.page + 1; + if (page !== statePage) { + this.props.history.push(`/users/${statePage}`); + } + } + } + + render() { + const { users, loading, error, t } = this.props; + return ( + <Page + title={t("users.title")} + subtitle={t("users.subtitle")} + loading={loading || !users} + error={error} + > + <UserTable users={users} /> + {this.renderPaginator()} + {this.renderCreateButton()} + </Page> + ); + } + + renderPaginator() { + const { list } = this.props; + if (list) { + return <Paginator collection={list} onPageChange={this.onPageChange} />; + } + return null; + } + + renderCreateButton() { + const { t } = this.props; + if (this.props.canAddUsers) { + return <CreateButton label={t("users.createButton")} link="/users/add" />; + } else { + return; + } + } +} + +const getPageFromProps = props => { + let page = props.match.params.page; + if (page) { + page = parseInt(page, 10); + } else { + page = 1; + } + return page; +}; + +const mapStateToProps = (state, ownProps) => { + const users = getUsersFromState(state); + const loading = isFetchUsersPending(state); + const error = getFetchUsersFailure(state); + + const usersLink = getUsersLink(state); + + const page = getPageFromProps(ownProps); + const canAddUsers = isPermittedToCreateUsers(state); + const list = selectListAsCollection(state); + + return { + users, + loading, + error, + canAddUsers, + list, + page, + usersLink + }; +}; + +const mapDispatchToProps = dispatch => { + return { + fetchUsersByPage: (link: string, page: number) => { + dispatch(fetchUsersByPage(link, page)); + }, + fetchUsersByLink: (link: string) => { + dispatch(fetchUsersByLink(link)); + } + }; +}; + +export default connect( + mapStateToProps, + mapDispatchToProps +)(translate("users")(Users)); From 17cf42caf071a984e9b1360090df72a6cba16319 Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Wed, 23 Jan 2019 11:55:57 +0100 Subject: [PATCH 019/120] added deleteuser functionality --- scm-ui/src/users/components/DeleteUser.js | 45 +--------------------- scm-ui/src/users/containers/GeneralUser.js | 24 ++++++++++-- 2 files changed, 22 insertions(+), 47 deletions(-) diff --git a/scm-ui/src/users/components/DeleteUser.js b/scm-ui/src/users/components/DeleteUser.js index ec3bf57a15..7932e14558 100644 --- a/scm-ui/src/users/components/DeleteUser.js +++ b/scm-ui/src/users/components/DeleteUser.js @@ -3,19 +3,16 @@ import React from "react"; import { translate } from "react-i18next"; import { Subtitle, DeleteButton, confirmAlert } from "@scm-manager/ui-components"; import type { User } from "@scm-manager/ui-types"; -import type { History } from "history"; type Props = { user: User, confirmDialog?: boolean, // dispatcher functions - fetchUserByName: (string, string) => void, - deleteUser: (user: User, callback?: () => void) => void, + deleteUser: (user: User) => void, // context objects - t: string => string, - history: History + t: string => string }; class DeleteUser extends React.Component<Props> { @@ -23,14 +20,6 @@ class DeleteUser extends React.Component<Props> { confirmDialog: true }; - userDeleted = () => { - this.props.history.push("/users"); - }; - - deleteUser = (user: User) => { - this.props.deleteUser(user, this.userDeleted); - }; - deleteUser = () => { this.props.deleteUser(this.props.user); }; @@ -80,34 +69,4 @@ class DeleteUser extends React.Component<Props> { } } -/* -const mapStateToProps = (state, ownProps) => { - const name = ownProps.match.params.name; - const user = getUserByName(state, name); - const loading = isDeleteUserPending(state, name); - const error = getDeleteUserFailure(state, name); - return { - name, - user, - loading, - error - }; -}; - -const mapDispatchToProps = dispatch => { - return { - fetchUserByName: (link: string, name: string) => { - dispatch(fetchUserByName(link, name)); - }, - deleteUser: (user: User, callback?: () => void) => { - dispatch(deleteUser(user, callback)); - } - }; -}; - -export default connect( - mapStateToProps, - mapDispatchToProps -)(translate("users")(DeleteUser)); -*/ export default translate("users")(DeleteUser); diff --git a/scm-ui/src/users/containers/GeneralUser.js b/scm-ui/src/users/containers/GeneralUser.js index f386994460..cb578d6f1e 100644 --- a/scm-ui/src/users/containers/GeneralUser.js +++ b/scm-ui/src/users/containers/GeneralUser.js @@ -7,9 +7,12 @@ import DeleteUser from "../components/DeleteUser"; import type { User } from "@scm-manager/ui-types"; import { modifyUser, + deleteUser, isModifyUserPending, getModifyUserFailure, - modifyUserReset + modifyUserReset, + isDeleteUserPending, + getDeleteUserFailure } from "../modules/users"; import type { History } from "history"; import { ErrorNotification } from "@scm-manager/ui-components"; @@ -21,6 +24,7 @@ type Props = { // dispatch functions modifyUser: (user: User, callback?: () => void) => void, modifyUserReset: User => void, + deleteUser: (user: User, callback?: () => void) => void, // context objects user: User, @@ -32,6 +36,7 @@ class GeneralUser extends React.Component<Props> { const { modifyUserReset, user } = this.props; modifyUserReset(user); } + userModified = (user: User) => () => { this.props.history.push(`/user/${user.name}`); }; @@ -40,6 +45,14 @@ class GeneralUser extends React.Component<Props> { this.props.modifyUser(user, this.userModified(user)); }; + userDeleted = () => { + this.props.history.push("/users"); + }; + + deleteUser = (user: User) => { + this.props.deleteUser(user, this.userDeleted); + }; + render() { const { user, loading, error } = this.props; return ( @@ -51,15 +64,15 @@ class GeneralUser extends React.Component<Props> { loading={loading} /> <hr /> - <DeleteUser user={user} /> + <DeleteUser user={user} deleteUser={this.deleteUser} /> </div> ); } } const mapStateToProps = (state, ownProps) => { - const loading = isModifyUserPending(state, ownProps.user.name); - const error = getModifyUserFailure(state, ownProps.user.name); + const loading = isModifyUserPending(state, ownProps.user.name) || isDeleteUserPending(state, ownProps.user.name); + const error = getModifyUserFailure(state, ownProps.user.name) || getDeleteUserFailure(state, ownProps.user.name); return { loading, error @@ -73,6 +86,9 @@ const mapDispatchToProps = dispatch => { }, modifyUserReset: (user: User) => { dispatch(modifyUserReset(user)); + }, + deleteUser: (user: User, callback?: () => void) => { + dispatch(deleteUser(user, callback)); } }; }; From 27c71ec6a393d2a9574c9a605dcb9315e5583d5f Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Wed, 23 Jan 2019 12:07:28 +0100 Subject: [PATCH 020/120] added deleterepo functionality --- scm-ui/src/repos/components/DeleteRepo.js | 5 ++++- scm-ui/src/repos/containers/GeneralRepo.js | 16 +++++++++++++++- scm-ui/src/users/components/DeleteUser.js | 1 + 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/scm-ui/src/repos/components/DeleteRepo.js b/scm-ui/src/repos/components/DeleteRepo.js index d1908f9daf..57ee868586 100644 --- a/scm-ui/src/repos/components/DeleteRepo.js +++ b/scm-ui/src/repos/components/DeleteRepo.js @@ -8,6 +8,9 @@ type Props = { repository: Repository, confirmDialog?: boolean, + // dispatcher functions + delete: Repository => void, + // context props t: string => string }; @@ -18,7 +21,7 @@ class DeleteRepo extends React.Component<Props> { }; delete = () => { - //this.props.delete(this.props.repository); + this.props.delete(this.props.repository); }; confirmDelete = () => { diff --git a/scm-ui/src/repos/containers/GeneralRepo.js b/scm-ui/src/repos/containers/GeneralRepo.js index 70cc9f9adc..83c5d47b52 100644 --- a/scm-ui/src/repos/containers/GeneralRepo.js +++ b/scm-ui/src/repos/containers/GeneralRepo.js @@ -7,6 +7,7 @@ import DeleteRepo from "../components/DeleteRepo"; import type { Repository } from "@scm-manager/ui-types"; import { modifyRepo, + deleteRepo, isModifyRepoPending, getModifyRepoFailure, modifyRepoReset @@ -20,6 +21,7 @@ type Props = { modifyRepo: (Repository, () => void) => void, modifyRepoReset: Repository => void, + deleteRepo: (Repository, () => void) => void, // context props repository: Repository, @@ -31,11 +33,20 @@ class GeneralRepo extends React.Component<Props> { const { modifyRepoReset, repository } = this.props; modifyRepoReset(repository); } + repoModified = () => { const { history, repository } = this.props; history.push(`/repo/${repository.namespace}/${repository.name}`); }; + deleted = () => { + this.props.history.push("/repos"); + }; + + delete = (repository: Repository) => { + this.props.deleteRepo(repository, this.deleted); + }; + render() { const { loading, error, repository } = this.props; return ( @@ -49,7 +60,7 @@ class GeneralRepo extends React.Component<Props> { }} /> <hr /> - <DeleteRepo repository={repository} /> + <DeleteRepo repository={repository} delete={this.delete} /> </div> ); } @@ -72,6 +83,9 @@ const mapDispatchToProps = dispatch => { }, modifyRepoReset: (repo: Repository) => { dispatch(modifyRepoReset(repo)); + }, + deleteRepo: (repo: Repository, callback: () => void) => { + dispatch(deleteRepo(repo, callback)); } }; }; diff --git a/scm-ui/src/users/components/DeleteUser.js b/scm-ui/src/users/components/DeleteUser.js index 7932e14558..c699d23dd1 100644 --- a/scm-ui/src/users/components/DeleteUser.js +++ b/scm-ui/src/users/components/DeleteUser.js @@ -53,6 +53,7 @@ class DeleteUser extends React.Component<Props> { if (!this.isDeletable()) { return null; } + return ( <> <Subtitle subtitle={t("delete.subtitle")} /> From 7160aa98f9018c506fc3fde984e95f4e11b09a2d Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Wed, 23 Jan 2019 12:44:02 +0100 Subject: [PATCH 021/120] removed unnecessary stuff in reporoot --- scm-ui/src/repos/containers/RepositoryRoot.js | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/scm-ui/src/repos/containers/RepositoryRoot.js b/scm-ui/src/repos/containers/RepositoryRoot.js index fcbe5dfee1..5791622b9a 100644 --- a/scm-ui/src/repos/containers/RepositoryRoot.js +++ b/scm-ui/src/repos/containers/RepositoryRoot.js @@ -1,6 +1,6 @@ //@flow import React from "react"; -import {deleteRepo, fetchRepoByName, getFetchRepoFailure, getRepository, isFetchRepoPending} from "../modules/repos"; +import {fetchRepoByName, getFetchRepoFailure, getRepository, isFetchRepoPending} from "../modules/repos"; import {connect} from "react-redux"; import {Route, Switch} from "react-router-dom"; @@ -32,7 +32,6 @@ type Props = { // dispatch functions fetchRepoByName: (link: string, namespace: string, name: string) => void, - deleteRepo: (repository: Repository, () => void) => void, // context props t: string => string, @@ -58,14 +57,6 @@ class RepositoryRoot extends React.Component<Props> { return this.stripEndingSlash(this.props.match.url); }; - deleted = () => { - this.props.history.push("/repos"); - }; - - delete = (repository: Repository) => { - this.props.deleteRepo(repository, this.deleted); - }; - matches = (route: any) => { const url = this.matchedUrl(); const regex = new RegExp(`${url}(/branches)?/?[^/]*/changesets?.*`); @@ -227,9 +218,6 @@ const mapDispatchToProps = dispatch => { return { fetchRepoByName: (link: string, namespace: string, name: string) => { dispatch(fetchRepoByName(link, namespace, name)); - }, - deleteRepo: (repository: Repository, callback: () => void) => { - dispatch(deleteRepo(repository, callback)); } }; }; From 5f7432c7584b432b1faca527d209b76c0c3f12bc Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Wed, 23 Jan 2019 13:20:57 +0100 Subject: [PATCH 022/120] unified langs + added deletegroup component + renamed editgroup to general --- scm-ui/public/locales/en/groups.json | 20 +++++--- scm-ui/public/locales/en/repos.json | 2 +- scm-ui/public/locales/en/users.json | 4 +- .../DeleteGroupNavLink.js => DeleteGroup.js} | 33 +++++++++---- ...oupNavLink.test.js => DeleteGroup.test.js} | 12 ++--- ...GroupNavLink.js => GeneralGroupNavLink.js} | 6 +-- ...nk.test.js => GeneralGroupNavLink.test.js} | 6 +-- .../src/groups/components/navLinks/index.js | 3 +- scm-ui/src/groups/containers/SingleGroup.js | 47 +++++++++---------- scm-ui/src/repos/components/DeleteRepo.js | 20 ++++---- scm-ui/src/users/components/DeleteUser.js | 14 +++--- 11 files changed, 90 insertions(+), 77 deletions(-) rename scm-ui/src/groups/components/{navLinks/DeleteGroupNavLink.js => DeleteGroup.js} (51%) rename scm-ui/src/groups/components/{navLinks/DeleteGroupNavLink.test.js => DeleteGroup.test.js} (83%) rename scm-ui/src/groups/components/navLinks/{EditGroupNavLink.js => GeneralGroupNavLink.js} (69%) rename scm-ui/src/groups/components/navLinks/{editGroupNavLink.test.js => GeneralGroupNavLink.test.js} (66%) diff --git a/scm-ui/public/locales/en/groups.json b/scm-ui/public/locales/en/groups.json index 66075be273..b14d570ee2 100644 --- a/scm-ui/public/locales/en/groups.json +++ b/scm-ui/public/locales/en/groups.json @@ -11,11 +11,16 @@ "title": "Groups", "subtitle": "Create, read, update and delete groups" }, - "single-group": { - "navigationLabel": "Group Navigation", - "informationNavLink": "Information", + "singleGroup": { "errorTitle": "Error", - "errorSubtitle": "Unknown group error" + "errorSubtitle": "Unknown group error", + "menu": { + "navigationLabel": "Group Navigation", + "informationNavLink": "Information", + "settingsNavLink": "Settings", + "editNavLink": "General", + "permissionsNavLink": "Permissions" + } }, "add-group": { "title": "Create Group", @@ -53,9 +58,10 @@ "memberHelpText": "Usernames of the group members" } }, - "delete-group-button": { - "label": "Delete", - "confirm-alert": { + "deleteGroup": { + "subtitle": "Delete Group", + "button": "Delete", + "confirmAlert": { "title": "Delete Group", "message": "Do you really want to delete the group?", "submit": "Yes", diff --git a/scm-ui/public/locales/en/repos.json b/scm-ui/public/locales/en/repos.json index a7a7d8449f..564748c9eb 100644 --- a/scm-ui/public/locales/en/repos.json +++ b/scm-ui/public/locales/en/repos.json @@ -118,7 +118,7 @@ "loading": "Loading..." } }, - "delete": { + "deleteRepo": { "subtitle": "Delete Repository", "button": "Delete", "confirmAlert": { diff --git a/scm-ui/public/locales/en/users.json b/scm-ui/public/locales/en/users.json index 394d23dbb0..e7f0661fdf 100644 --- a/scm-ui/public/locales/en/users.json +++ b/scm-ui/public/locales/en/users.json @@ -42,10 +42,10 @@ "title": "Create User", "subtitle": "Create a new user" }, - "delete": { + "deleteUser": { "subtitle": "Delete User", "button": "Delete", - "confirm-alert": { + "confirmAlert": { "title": "Delete user", "message": "Do you really want to delete the user?", "submit": "Yes", diff --git a/scm-ui/src/groups/components/navLinks/DeleteGroupNavLink.js b/scm-ui/src/groups/components/DeleteGroup.js similarity index 51% rename from scm-ui/src/groups/components/navLinks/DeleteGroupNavLink.js rename to scm-ui/src/groups/components/DeleteGroup.js index 45bbdd3026..c32983ff94 100644 --- a/scm-ui/src/groups/components/navLinks/DeleteGroupNavLink.js +++ b/scm-ui/src/groups/components/DeleteGroup.js @@ -2,16 +2,16 @@ import React from "react"; import { translate } from "react-i18next"; import type { Group } from "@scm-manager/ui-types"; -import { NavAction, confirmAlert } from "@scm-manager/ui-components"; +import { Subtitle, DeleteButton, confirmAlert } from "@scm-manager/ui-components"; type Props = { group: Group, confirmDialog?: boolean, - t: string => string, - deleteGroup: (group: Group) => void + deleteGroup: (group: Group) => void, + t: string => string }; -export class DeleteGroupNavLink extends React.Component<Props> { +export class DeleteGroup extends React.Component<Props> { static defaultProps = { confirmDialog: true }; @@ -23,15 +23,15 @@ export class DeleteGroupNavLink extends React.Component<Props> { confirmDelete = () => { const { t } = this.props; confirmAlert({ - title: t("delete-group-button.confirm-alert.title"), - message: t("delete-group-button.confirm-alert.message"), + title: t("deleteGroup.confirmAlert.title"), + message: t("deleteGroup.confirmAlert.message"), buttons: [ { - label: t("delete-group-button.confirm-alert.submit"), + label: t("deleteGroup.confirmAlert.submit"), onClick: () => this.deleteGroup() }, { - label: t("delete-group-button.confirm-alert.cancel"), + label: t("deleteGroup.confirmAlert.cancel"), onClick: () => null } ] @@ -49,8 +49,21 @@ export class DeleteGroupNavLink extends React.Component<Props> { if (!this.isDeletable()) { return null; } - return <NavAction label={t("delete-group-button.label")} action={action} />; + + return ( + <> + <Subtitle subtitle={t("deleteGroup.subtitle")} /> + <div className="columns"> + <div className="column"> + <DeleteButton + label={t("deleteGroup.button")} + action={action} + /> + </div> + </div> + </> + ); } } -export default translate("groups")(DeleteGroupNavLink); +export default translate("groups")(DeleteGroup); diff --git a/scm-ui/src/groups/components/navLinks/DeleteGroupNavLink.test.js b/scm-ui/src/groups/components/DeleteGroup.test.js similarity index 83% rename from scm-ui/src/groups/components/navLinks/DeleteGroupNavLink.test.js rename to scm-ui/src/groups/components/DeleteGroup.test.js index 49f8d95c63..2364a196e8 100644 --- a/scm-ui/src/groups/components/navLinks/DeleteGroupNavLink.test.js +++ b/scm-ui/src/groups/components/DeleteGroup.test.js @@ -1,8 +1,8 @@ import React from "react"; import { mount, shallow } from "enzyme"; -import "../../../tests/enzyme"; +import "../../tests/enzyme"; import "../../../tests/i18n"; -import DeleteGroupNavLink from "./DeleteGroupNavLink"; +import DeleteGroup from "./DeleteGroup"; import { confirmAlert } from "@scm-manager/ui-components"; jest.mock("@scm-manager/ui-components", () => ({ @@ -17,7 +17,7 @@ describe("DeleteGroupNavLink", () => { }; const navLink = shallow( - <DeleteGroupNavLink group={group} deleteGroup={() => {}} /> + <DeleteGroup group={group} deleteGroup={() => {}} /> ); expect(navLink.text()).toBe(""); }); @@ -32,7 +32,7 @@ describe("DeleteGroupNavLink", () => { }; const navLink = mount( - <DeleteGroupNavLink group={group} deleteGroup={() => {}} /> + <DeleteGroup group={group} deleteGroup={() => {}} /> ); expect(navLink.text()).not.toBe(""); }); @@ -47,7 +47,7 @@ describe("DeleteGroupNavLink", () => { }; const navLink = mount( - <DeleteGroupNavLink group={group} deleteGroup={() => {}} /> + <DeleteGroup group={group} deleteGroup={() => {}} /> ); navLink.find("a").simulate("click"); @@ -69,7 +69,7 @@ describe("DeleteGroupNavLink", () => { } const navLink = mount( - <DeleteGroupNavLink + <DeleteGroup group={group} confirmDialog={false} deleteGroup={capture} diff --git a/scm-ui/src/groups/components/navLinks/EditGroupNavLink.js b/scm-ui/src/groups/components/navLinks/GeneralGroupNavLink.js similarity index 69% rename from scm-ui/src/groups/components/navLinks/EditGroupNavLink.js rename to scm-ui/src/groups/components/navLinks/GeneralGroupNavLink.js index a0e36bc8d7..fe59fa41a3 100644 --- a/scm-ui/src/groups/components/navLinks/EditGroupNavLink.js +++ b/scm-ui/src/groups/components/navLinks/GeneralGroupNavLink.js @@ -12,13 +12,13 @@ type Props = { type State = {}; -class EditGroupNavLink extends React.Component<Props, State> { +class GeneralGroupNavLink extends React.Component<Props, State> { render() { const { t, editUrl } = this.props; if (!this.isEditable()) { return null; } - return <NavLink label={t("edit-group-button.label")} to={editUrl} />; + return <NavLink label={t("singleGroup.menu.editNavLink")} to={editUrl} />; } isEditable = () => { @@ -26,4 +26,4 @@ class EditGroupNavLink extends React.Component<Props, State> { }; } -export default translate("groups")(EditGroupNavLink); +export default translate("groups")(GeneralGroupNavLink); diff --git a/scm-ui/src/groups/components/navLinks/editGroupNavLink.test.js b/scm-ui/src/groups/components/navLinks/GeneralGroupNavLink.test.js similarity index 66% rename from scm-ui/src/groups/components/navLinks/editGroupNavLink.test.js rename to scm-ui/src/groups/components/navLinks/GeneralGroupNavLink.test.js index 7399f4f714..1120dab2fd 100644 --- a/scm-ui/src/groups/components/navLinks/editGroupNavLink.test.js +++ b/scm-ui/src/groups/components/navLinks/GeneralGroupNavLink.test.js @@ -4,14 +4,14 @@ import React from "react"; import { shallow } from "enzyme"; import "../../../tests/enzyme"; import "../../../tests/i18n"; -import EditGroupNavLink from "./EditGroupNavLink"; +import GeneralGroupNavLink from "./GeneralGroupNavLink"; it("should render nothing, if the edit link is missing", () => { const group = { _links: {} }; - const navLink = shallow(<EditGroupNavLink group={group} editUrl='/group/edit'/>); + const navLink = shallow(<GeneralGroupNavLink group={group} editUrl='/group/edit'/>); expect(navLink.text()).toBe(""); }); @@ -24,6 +24,6 @@ it("should render the navLink", () => { } }; - const navLink = shallow(<EditGroupNavLink group={group} editUrl='/group/edit'/>); + const navLink = shallow(<GeneralGroupNavLink group={group} editUrl='/group/edit'/>); expect(navLink.text()).not.toBe(""); }); diff --git a/scm-ui/src/groups/components/navLinks/index.js b/scm-ui/src/groups/components/navLinks/index.js index 30fdd34b6d..46dc704d5b 100644 --- a/scm-ui/src/groups/components/navLinks/index.js +++ b/scm-ui/src/groups/components/navLinks/index.js @@ -1,2 +1 @@ -export { default as DeleteGroupNavLink } from "./DeleteGroupNavLink"; -export { default as EditGroupNavLink } from "./EditGroupNavLink"; +export { default as GeneralGroupNavLink } from "./GeneralGroupNavLink"; diff --git a/scm-ui/src/groups/containers/SingleGroup.js b/scm-ui/src/groups/containers/SingleGroup.js index ec62333f6f..5a00b7a965 100644 --- a/scm-ui/src/groups/containers/SingleGroup.js +++ b/scm-ui/src/groups/containers/SingleGroup.js @@ -6,22 +6,22 @@ import { ErrorPage, Loading, Navigation, + SubNavigation, Section, NavLink } from "@scm-manager/ui-components"; import { Route } from "react-router"; import { Details } from "./../components/table"; -import { DeleteGroupNavLink, EditGroupNavLink } from "./../components/navLinks"; +import { + GeneralGroupNavLink +} from "./../components/navLinks"; import type { Group } from "@scm-manager/ui-types"; import type { History } from "history"; import { - deleteGroup, fetchGroupByName, getGroupByName, isFetchGroupPending, - getFetchGroupFailure, - getDeleteGroupFailure, - isDeleteGroupPending + getFetchGroupFailure } from "../modules/groups"; import { translate } from "react-i18next"; @@ -36,7 +36,6 @@ type Props = { groupLink: string, // dispatcher functions - deleteGroup: (group: Group, callback?: () => void) => void, fetchGroupByName: (string, string) => void, // context objects @@ -57,14 +56,6 @@ class SingleGroup extends React.Component<Props> { return url; }; - deleteGroup = (group: Group) => { - this.props.deleteGroup(group, this.groupDeleted); - }; - - groupDeleted = () => { - this.props.history.push("/groups"); - }; - matchedUrl = () => { return this.stripEndingSlash(this.props.match.url); }; @@ -75,8 +66,8 @@ class SingleGroup extends React.Component<Props> { if (error) { return ( <ErrorPage - title={t("single-group.errorTitle")} - subtitle={t("single-group.errorSubtitle")} + title={t("singleGroup.errorTitle")} + subtitle={t("singleGroup.errorSubtitle")} error={error} /> ); @@ -98,18 +89,27 @@ class SingleGroup extends React.Component<Props> { component={() => <Details group={group} />} /> <Route - path={`${url}/edit`} + path={`${url}/settings/general`} exact component={() => <EditGroup group={group} />} /> </div> <div className="column"> <Navigation> - <Section label={t("single-group.navigationLabel")}> + <Section label={t("singleGroup.menu.navigationLabel")}> <NavLink to={`${url}`} - label={t("single-group.informationNavLink")} + label={t("singleGroup.menu.informationNavLink")} /> + <SubNavigation + to={`${url}/settings/general`} + label={t("singleGroup.menu.settingsNavLink")} + > + <GeneralGroupNavLink + group={group} + editUrl={`${url}/settings/general`} + /> + </SubNavigation> </Section> </Navigation> </div> @@ -122,10 +122,8 @@ class SingleGroup extends React.Component<Props> { const mapStateToProps = (state, ownProps) => { const name = ownProps.match.params.name; const group = getGroupByName(state, name); - const loading = - isFetchGroupPending(state, name) || isDeleteGroupPending(state, name); - const error = - getFetchGroupFailure(state, name) || getDeleteGroupFailure(state, name); + const loading = isFetchGroupPending(state, name); + const error = getFetchGroupFailure(state, name); const groupLink = getGroupsLink(state); return { @@ -141,9 +139,6 @@ const mapDispatchToProps = dispatch => { return { fetchGroupByName: (link: string, name: string) => { dispatch(fetchGroupByName(link, name)); - }, - deleteGroup: (group: Group, callback?: () => void) => { - dispatch(deleteGroup(group, callback)); } }; }; diff --git a/scm-ui/src/repos/components/DeleteRepo.js b/scm-ui/src/repos/components/DeleteRepo.js index 57ee868586..3ba7b08d48 100644 --- a/scm-ui/src/repos/components/DeleteRepo.js +++ b/scm-ui/src/repos/components/DeleteRepo.js @@ -1,8 +1,8 @@ //@flow import React from "react"; import { translate } from "react-i18next"; -import { Subtitle, DeleteButton, confirmAlert } from "@scm-manager/ui-components"; import type { Repository } from "@scm-manager/ui-types"; +import { Subtitle, DeleteButton, confirmAlert } from "@scm-manager/ui-components"; type Props = { repository: Repository, @@ -20,22 +20,22 @@ class DeleteRepo extends React.Component<Props> { confirmDialog: true }; - delete = () => { + deleteRepo = () => { this.props.delete(this.props.repository); }; confirmDelete = () => { const { t } = this.props; confirmAlert({ - title: t("delete.confirmAlert.title"), - message: t("delete.confirmAlert.message"), + title: t("deleteRepo.confirmAlert.title"), + message: t("deleteRepo.confirmAlert.message"), buttons: [ { - label: t("delete.confirmAlert.submit"), - onClick: () => this.delete() + label: t("deleteRepo.confirmAlert.submit"), + onClick: () => this.deleteRepo() }, { - label: t("delete.confirmAlert.cancel"), + label: t("deleteRepo.confirmAlert.cancel"), onClick: () => null } ] @@ -48,7 +48,7 @@ class DeleteRepo extends React.Component<Props> { render() { const { confirmDialog, t } = this.props; - const action = confirmDialog ? this.confirmDelete : this.delete(); + const action = confirmDialog ? this.confirmDelete : this.deleteRepo; if (!this.isDeletable()) { return null; @@ -56,11 +56,11 @@ class DeleteRepo extends React.Component<Props> { return ( <> - <Subtitle subtitle={t("delete.subtitle")} /> + <Subtitle subtitle={t("deleteRepo.subtitle")} /> <div className="columns"> <div className="column"> <DeleteButton - label={t("delete.button")} + label={t("deleteRepo.button")} action={action} /> </div> diff --git a/scm-ui/src/users/components/DeleteUser.js b/scm-ui/src/users/components/DeleteUser.js index c699d23dd1..b8523a375e 100644 --- a/scm-ui/src/users/components/DeleteUser.js +++ b/scm-ui/src/users/components/DeleteUser.js @@ -1,8 +1,8 @@ // @flow import React from "react"; import { translate } from "react-i18next"; -import { Subtitle, DeleteButton, confirmAlert } from "@scm-manager/ui-components"; import type { User } from "@scm-manager/ui-types"; +import { Subtitle, DeleteButton, confirmAlert } from "@scm-manager/ui-components"; type Props = { user: User, @@ -27,15 +27,15 @@ class DeleteUser extends React.Component<Props> { confirmDelete = () => { const { t } = this.props; confirmAlert({ - title: t("delete.confirm-alert.title"), - message: t("delete.confirm-alert.message"), + title: t("deleteUser.confirmAlert.title"), + message: t("deleteUser.confirmAlert.message"), buttons: [ { - label: t("delete.confirm-alert.submit"), + label: t("deleteUser.confirmAlert.submit"), onClick: () => this.deleteUser() }, { - label: t("delete.confirm-alert.cancel"), + label: t("deleteUser.confirmAlert.cancel"), onClick: () => null } ] @@ -56,11 +56,11 @@ class DeleteUser extends React.Component<Props> { return ( <> - <Subtitle subtitle={t("delete.subtitle")} /> + <Subtitle subtitle={t("deleteUser.subtitle")} /> <div className="columns"> <div className="column"> <DeleteButton - label={t("delete.button")} + label={t("deleteUser.button")} action={action} /> </div> From 0b4c7148e98c6788fc6f17ee03940501720fa613 Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Wed, 23 Jan 2019 14:28:57 +0100 Subject: [PATCH 023/120] lang file + fixed edit group subtitle, paths + remaned editgroup to general --- scm-ui/public/locales/en/groups.json | 10 +++--- scm-ui/public/locales/en/users.json | 4 +-- scm-ui/src/groups/components/GroupForm.js | 23 +++++++------ .../{EditGroup.js => GeneralGroup.js} | 32 +++++++++++++++---- scm-ui/src/groups/containers/SingleGroup.js | 8 ++--- 5 files changed, 51 insertions(+), 26 deletions(-) rename scm-ui/src/groups/containers/{EditGroup.js => GeneralGroup.js} (76%) diff --git a/scm-ui/public/locales/en/groups.json b/scm-ui/public/locales/en/groups.json index 51b3c32c4e..654ab2a2db 100644 --- a/scm-ui/public/locales/en/groups.json +++ b/scm-ui/public/locales/en/groups.json @@ -46,12 +46,12 @@ "placeholder": "Enter member", "loading": "Loading...", "no-options": "No suggestion available" - }, - -"group-form": { + }, + "groupForm": { + "subtitle": "Edit Group", "submit": "Submit", - "name-error": "Group name is invalid", - "description-error": "Description is invalid", + "nameError": "Group name is invalid", + "descriptionError": "Description is invalid", "help": { "nameHelpText": "Unique name of the group", "descriptionHelpText": "A short description of the group", diff --git a/scm-ui/public/locales/en/users.json b/scm-ui/public/locales/en/users.json index f151786183..731b59e563 100644 --- a/scm-ui/public/locales/en/users.json +++ b/scm-ui/public/locales/en/users.json @@ -35,8 +35,8 @@ "informationNavLink": "Information", "settingsNavLink": "Settings", "editNavLink": "General", - "setPasswordNavLink": "Set Password", - "setPermissionsNavLink": "Set Permissions" + "setPasswordNavLink": "Password", + "setPermissionsNavLink": "Permissions" } }, "addUser": { diff --git a/scm-ui/src/groups/components/GroupForm.js b/scm-ui/src/groups/components/GroupForm.js index 7cc2ee5d24..75f6e89579 100644 --- a/scm-ui/src/groups/components/GroupForm.js +++ b/scm-ui/src/groups/components/GroupForm.js @@ -2,6 +2,7 @@ import React from "react"; import { translate } from "react-i18next"; import { + Subtitle, AutocompleteAddEntryToTableField, LabelWithHelpIcon, MemberNameTable, @@ -73,34 +74,38 @@ class GroupForm extends React.Component<Props, State> { render() { const { t, loading } = this.props; const { group } = this.state; - let nameField = null; + let firstField = null; if (!this.props.group) { - nameField = ( + // create new group + firstField = ( <InputField label={t("group.name")} - errorMessage={t("group-form.name-error")} + errorMessage={t("groupForm.nameError")} onChange={this.handleGroupNameChange} value={group.name} validationError={this.state.nameValidationError} - helpText={t("group-form.help.nameHelpText")} + helpText={t("groupForm.help.nameHelpText")} /> ); + } else { + // edit existing group + firstField = <Subtitle subtitle={t("groupForm.subtitle")} />; } return ( <form onSubmit={this.submit}> - {nameField} + {firstField} <Textarea label={t("group.description")} - errorMessage={t("group-form.description-error")} + errorMessage={t("groupForm.descriptionError")} onChange={this.handleDescriptionChange} value={group.description} validationError={false} - helpText={t("group-form.help.descriptionHelpText")} + helpText={t("groupForm.help.descriptionHelpText")} /> <LabelWithHelpIcon label={t("group.members")} - helpText={t("group-form.help.memberHelpText")} + helpText={t("groupForm.help.memberHelpText")} /> <MemberNameTable members={group.members} @@ -120,7 +125,7 @@ class GroupForm extends React.Component<Props, State> { /> <SubmitButton disabled={!this.isValid()} - label={t("group-form.submit")} + label={t("groupForm.submit")} loading={loading} /> </form> diff --git a/scm-ui/src/groups/containers/EditGroup.js b/scm-ui/src/groups/containers/GeneralGroup.js similarity index 76% rename from scm-ui/src/groups/containers/EditGroup.js rename to scm-ui/src/groups/containers/GeneralGroup.js index 223ea1eef6..a89491568f 100644 --- a/scm-ui/src/groups/containers/EditGroup.js +++ b/scm-ui/src/groups/containers/GeneralGroup.js @@ -3,9 +3,12 @@ import React from "react"; import { connect } from "react-redux"; import GroupForm from "../components/GroupForm"; import { + modifyGroup, + deleteGroup, getModifyGroupFailure, isModifyGroupPending, - modifyGroup, + getDeleteGroupFailure, + isDeleteGroupPending, modifyGroupReset } from "../modules/groups"; import type { History } from "history"; @@ -13,19 +16,21 @@ import { withRouter } from "react-router-dom"; import type { Group } from "@scm-manager/ui-types"; import { ErrorNotification } from "@scm-manager/ui-components"; import { getUserAutoCompleteLink } from "../../modules/indexResource"; +import DeleteGroup from "../components/DeleteGroup"; type Props = { group: Group, + fetchGroup: (name: string) => void, modifyGroup: (group: Group, callback?: () => void) => void, modifyGroupReset: Group => void, - fetchGroup: (name: string) => void, + deleteGroup: (group: Group, callback?: () => void) => void, autocompleteLink: string, history: History, loading?: boolean, error: Error }; -class EditGroup extends React.Component<Props> { +class GeneralGroup extends React.Component<Props> { componentDidMount() { const { group, modifyGroupReset } = this.props; modifyGroupReset(group); @@ -39,6 +44,14 @@ class EditGroup extends React.Component<Props> { this.props.modifyGroup(group, this.groupModified(group)); }; + deleteGroup = (group: Group) => { + this.props.deleteGroup(group, this.groupDeleted); + }; + + groupDeleted = () => { + this.props.history.push("/groups"); + }; + loadUserAutocompletion = (inputValue: string) => { const url = this.props.autocompleteLink + "?q="; return fetch(url + inputValue) @@ -66,14 +79,18 @@ class EditGroup extends React.Component<Props> { loading={loading} loadUserSuggestions={this.loadUserAutocompletion} /> + <hr /> + <DeleteGroup + group={group} + deleteGroup={this.deleteGroup} /> </div> ); } } const mapStateToProps = (state, ownProps) => { - const loading = isModifyGroupPending(state, ownProps.group.name); - const error = getModifyGroupFailure(state, ownProps.group.name); + const loading = isModifyGroupPending(state, ownProps.group.name) || isDeleteGroupPending(state, ownProps.group.name); + const error = getModifyGroupFailure(state, ownProps.group.name) || getDeleteGroupFailure(state, ownProps.group.name); const autocompleteLink = getUserAutoCompleteLink(state); return { loading, @@ -89,6 +106,9 @@ const mapDispatchToProps = dispatch => { }, modifyGroupReset: (group: Group) => { dispatch(modifyGroupReset(group)); + }, + deleteGroup: (group: Group, callback?: () => void) => { + dispatch(deleteGroup(group, callback)); } }; }; @@ -96,4 +116,4 @@ const mapDispatchToProps = dispatch => { export default connect( mapStateToProps, mapDispatchToProps -)(withRouter(EditGroup)); +)(withRouter(GeneralGroup)); diff --git a/scm-ui/src/groups/containers/SingleGroup.js b/scm-ui/src/groups/containers/SingleGroup.js index 29a188c964..aefe73fe5d 100644 --- a/scm-ui/src/groups/containers/SingleGroup.js +++ b/scm-ui/src/groups/containers/SingleGroup.js @@ -26,7 +26,7 @@ import { } from "../modules/groups"; import { translate } from "react-i18next"; -import EditGroup from "./EditGroup"; +import GeneralGroup from "./GeneralGroup"; import { getGroupsLink } from "../../modules/indexResource"; import SetPermissions from "../../permissions/components/SetPermissions"; import {ExtensionPoint} from "@scm-manager/ui-extensions"; @@ -99,10 +99,10 @@ class SingleGroup extends React.Component<Props> { <Route path={`${url}/settings/general`} exact - component={() => <EditGroup group={group} />} + component={() => <GeneralGroup group={group} />} /> <Route - path={`${url}/permissions`} + path={`${url}/settings/permissions`} exact component={() => ( <SetPermissions selectedPermissionsLink={group._links.permissions} /> @@ -136,7 +136,7 @@ class SingleGroup extends React.Component<Props> { /> <SetPermissionsNavLink group={group} - permissionsUrl={`${url}/permissions`} + permissionsUrl={`${url}/settings/permissions`} /> </SubNavigation> </Section> From 4b24c8db068cce2022fe2d812a96082aec65f93c Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Wed, 23 Jan 2019 14:48:29 +0100 Subject: [PATCH 024/120] corrected subtitle in create/edit --- scm-ui/src/groups/components/GroupForm.js | 82 ++++++++++++----------- scm-ui/src/users/components/UserForm.js | 7 +- scm-ui/src/users/containers/SingleUser.js | 2 +- 3 files changed, 50 insertions(+), 41 deletions(-) diff --git a/scm-ui/src/groups/components/GroupForm.js b/scm-ui/src/groups/components/GroupForm.js index 75f6e89579..8693cb9b47 100644 --- a/scm-ui/src/groups/components/GroupForm.js +++ b/scm-ui/src/groups/components/GroupForm.js @@ -72,12 +72,13 @@ class GroupForm extends React.Component<Props, State> { }; render() { - const { t, loading } = this.props; + const { loading, t } = this.props; const { group } = this.state; - let firstField = null; + let nameField = null; + let subtitle = null; if (!this.props.group) { // create new group - firstField = ( + nameField = ( <InputField label={t("group.name")} errorMessage={t("groupForm.nameError")} @@ -89,46 +90,49 @@ class GroupForm extends React.Component<Props, State> { ); } else { // edit existing group - firstField = <Subtitle subtitle={t("groupForm.subtitle")} />; + subtitle = <Subtitle subtitle={t("groupForm.subtitle")} />; } return ( - <form onSubmit={this.submit}> - {firstField} - <Textarea - label={t("group.description")} - errorMessage={t("groupForm.descriptionError")} - onChange={this.handleDescriptionChange} - value={group.description} - validationError={false} - helpText={t("groupForm.help.descriptionHelpText")} - /> - <LabelWithHelpIcon - label={t("group.members")} - helpText={t("groupForm.help.memberHelpText")} - /> - <MemberNameTable - members={group.members} - memberListChanged={this.memberListChanged} - /> + <> + {subtitle} + <form onSubmit={this.submit}> + {nameField} + <Textarea + label={t("group.description")} + errorMessage={t("groupForm.descriptionError")} + onChange={this.handleDescriptionChange} + value={group.description} + validationError={false} + helpText={t("groupForm.help.descriptionHelpText")} + /> + <LabelWithHelpIcon + label={t("group.members")} + helpText={t("groupForm.help.memberHelpText")} + /> + <MemberNameTable + members={group.members} + memberListChanged={this.memberListChanged} + /> - <AutocompleteAddEntryToTableField - addEntry={this.addMember} - disabled={false} - buttonLabel={t("add-member-button.label")} - fieldLabel={t("add-member-textfield.label")} - errorMessage={t("add-member-textfield.error")} - loadSuggestions={this.props.loadUserSuggestions} - placeholder={t("add-member-autocomplete.placeholder")} - loadingMessage={t("add-member-autocomplete.loading")} - noOptionsMessage={t("add-member-autocomplete.no-options")} - /> - <SubmitButton - disabled={!this.isValid()} - label={t("groupForm.submit")} - loading={loading} - /> - </form> + <AutocompleteAddEntryToTableField + addEntry={this.addMember} + disabled={false} + buttonLabel={t("add-member-button.label")} + fieldLabel={t("add-member-textfield.label")} + errorMessage={t("add-member-textfield.error")} + loadSuggestions={this.props.loadUserSuggestions} + placeholder={t("add-member-autocomplete.placeholder")} + loadingMessage={t("add-member-autocomplete.loading")} + noOptionsMessage={t("add-member-autocomplete.no-options")} + /> + <SubmitButton + disabled={!this.isValid()} + label={t("groupForm.submit")} + loading={loading} + /> + </form> + </> ); } diff --git a/scm-ui/src/users/components/UserForm.js b/scm-ui/src/users/components/UserForm.js index d01838f08a..bb368666d9 100644 --- a/scm-ui/src/users/components/UserForm.js +++ b/scm-ui/src/users/components/UserForm.js @@ -88,7 +88,9 @@ class UserForm extends React.Component<Props, State> { let nameField = null; let passwordChangeField = null; + let subtitle = null; if (!this.props.user) { + // create new user nameField = ( <InputField label={t("user.name")} @@ -103,10 +105,13 @@ class UserForm extends React.Component<Props, State> { passwordChangeField = ( <PasswordConfirmation passwordChanged={this.handlePasswordChange} /> ); + } else { + // edit existing user + subtitle = <Subtitle subtitle={t("userForm.subtitle")} />; } return ( <> - <Subtitle subtitle={t("userForm.subtitle")} /> + {subtitle} <form onSubmit={this.submit}> <div className="columns"> <div className="column is-half"> diff --git a/scm-ui/src/users/containers/SingleUser.js b/scm-ui/src/users/containers/SingleUser.js index 734d7cb209..ad95aa81ec 100644 --- a/scm-ui/src/users/containers/SingleUser.js +++ b/scm-ui/src/users/containers/SingleUser.js @@ -92,7 +92,7 @@ class SingleUser extends React.Component<Props> { component={() => <SetUserPassword user={user} />} /> <Route - path={`${url}/permissions`} + path={`${url}/settings/permissions`} component={() => ( <SetPermissions selectedPermissionsLink={user._links.permissions} From b90588a6f466e1f9c1ebcd5376a53ff9a4bfee7c Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Wed, 23 Jan 2019 15:08:28 +0100 Subject: [PATCH 025/120] renamed lang + component --- scm-ui/public/locales/en/groups.json | 2 +- scm-ui/public/locales/en/repos.json | 2 +- scm-ui/public/locales/en/users.json | 2 +- .../groups/components/navLinks/GeneralGroupNavLink.js | 2 +- .../{EditNavLink.js => GeneralRepoNavLink.js} | 6 +++--- ...{EditNavLink.test.js => GeneralRepoNavLink.test.js} | 10 +++++----- scm-ui/src/repos/containers/RepositoryRoot.js | 3 ++- .../users/components/navLinks/GeneralUserNavLink.js | 2 +- 8 files changed, 15 insertions(+), 14 deletions(-) rename scm-ui/src/repos/components/{EditNavLink.js => GeneralRepoNavLink.js} (69%) rename scm-ui/src/repos/components/{EditNavLink.test.js => GeneralRepoNavLink.test.js} (70%) diff --git a/scm-ui/public/locales/en/groups.json b/scm-ui/public/locales/en/groups.json index 654ab2a2db..83dcf18fe5 100644 --- a/scm-ui/public/locales/en/groups.json +++ b/scm-ui/public/locales/en/groups.json @@ -18,7 +18,7 @@ "navigationLabel": "Group Navigation", "informationNavLink": "Information", "settingsNavLink": "Settings", - "editNavLink": "General", + "generalNavLink": "General", "setPermissionsNavLink": "Permissions" } }, diff --git a/scm-ui/public/locales/en/repos.json b/scm-ui/public/locales/en/repos.json index 564748c9eb..577386572e 100644 --- a/scm-ui/public/locales/en/repos.json +++ b/scm-ui/public/locales/en/repos.json @@ -26,7 +26,7 @@ "historyNavLink": "Commits", "sourcesNavLink": "Sources", "settingsNavLink": "Settings", - "editNavLink": "General", + "generalNavLink": "General", "permissionsNavLink": "Permissions" } }, diff --git a/scm-ui/public/locales/en/users.json b/scm-ui/public/locales/en/users.json index 731b59e563..e907f88f4c 100644 --- a/scm-ui/public/locales/en/users.json +++ b/scm-ui/public/locales/en/users.json @@ -34,7 +34,7 @@ "navigationLabel": "User Navigation", "informationNavLink": "Information", "settingsNavLink": "Settings", - "editNavLink": "General", + "generalNavLink": "General", "setPasswordNavLink": "Password", "setPermissionsNavLink": "Permissions" } diff --git a/scm-ui/src/groups/components/navLinks/GeneralGroupNavLink.js b/scm-ui/src/groups/components/navLinks/GeneralGroupNavLink.js index fe59fa41a3..5b0528ba3a 100644 --- a/scm-ui/src/groups/components/navLinks/GeneralGroupNavLink.js +++ b/scm-ui/src/groups/components/navLinks/GeneralGroupNavLink.js @@ -18,7 +18,7 @@ class GeneralGroupNavLink extends React.Component<Props, State> { if (!this.isEditable()) { return null; } - return <NavLink label={t("singleGroup.menu.editNavLink")} to={editUrl} />; + return <NavLink label={t("singleGroup.menu.generalNavLink")} to={editUrl} />; } isEditable = () => { diff --git a/scm-ui/src/repos/components/EditNavLink.js b/scm-ui/src/repos/components/GeneralRepoNavLink.js similarity index 69% rename from scm-ui/src/repos/components/EditNavLink.js rename to scm-ui/src/repos/components/GeneralRepoNavLink.js index b06cd8d96e..a76231529b 100644 --- a/scm-ui/src/repos/components/EditNavLink.js +++ b/scm-ui/src/repos/components/GeneralRepoNavLink.js @@ -6,7 +6,7 @@ import type { Repository } from "@scm-manager/ui-types"; type Props = { editUrl: string, t: string => string, repository: Repository }; -class EditNavLink extends React.Component<Props> { +class GeneralRepoNavLink extends React.Component<Props> { isEditable = () => { return this.props.repository._links.update; }; @@ -15,8 +15,8 @@ class EditNavLink extends React.Component<Props> { return null; } const { editUrl, t } = this.props; - return <NavLink to={editUrl} label={t("repositoryRoot.menu.editNavLink")} />; + return <NavLink to={editUrl} label={t("repositoryRoot.menu.generalNavLink")} />; } } -export default translate("repos")(EditNavLink); +export default translate("repos")(GeneralRepoNavLink); diff --git a/scm-ui/src/repos/components/EditNavLink.test.js b/scm-ui/src/repos/components/GeneralRepoNavLink.test.js similarity index 70% rename from scm-ui/src/repos/components/EditNavLink.test.js rename to scm-ui/src/repos/components/GeneralRepoNavLink.test.js index 080440cc88..2ce050f69c 100644 --- a/scm-ui/src/repos/components/EditNavLink.test.js +++ b/scm-ui/src/repos/components/GeneralRepoNavLink.test.js @@ -3,9 +3,9 @@ import { shallow, mount } from "enzyme"; import "../../tests/enzyme"; import "../../tests/i18n"; import ReactRouterEnzymeContext from "react-router-enzyme-context"; -import EditNavLink from "./EditNavLink"; +import GeneralRepoNavLink from "./GeneralRepoNavLink"; -describe("EditNavLink", () => { +describe("GeneralNavLink", () => { const options = new ReactRouterEnzymeContext(); it("should render nothing, if the modify link is missing", () => { @@ -14,7 +14,7 @@ describe("EditNavLink", () => { }; const navLink = shallow( - <EditNavLink repository={repository} editUrl="" />, + <GeneralRepoNavLink repository={repository} editUrl="" />, options.get() ); expect(navLink.text()).toBe(""); @@ -30,9 +30,9 @@ describe("EditNavLink", () => { }; const navLink = mount( - <EditNavLink repository={repository} editUrl="" />, + <GeneralRepoNavLink repository={repository} editUrl="" />, options.get() ); - expect(navLink.text()).toBe("repositoryRoot.menu.editNavLink"); + expect(navLink.text()).toBe("repositoryRoot.menu.generalNavLink"); }); }); diff --git a/scm-ui/src/repos/containers/RepositoryRoot.js b/scm-ui/src/repos/containers/RepositoryRoot.js index 5791622b9a..dcad647d4e 100644 --- a/scm-ui/src/repos/containers/RepositoryRoot.js +++ b/scm-ui/src/repos/containers/RepositoryRoot.js @@ -13,6 +13,7 @@ import GeneralRepo from "./GeneralRepo"; import Permissions from "../permissions/containers/Permissions"; import type {History} from "history"; +import GeneralRepoNavLink from "../components/GeneralRepoNavLink"; import BranchRoot from "./ChangesetsRoot"; import ChangesetView from "./ChangesetView"; @@ -183,7 +184,7 @@ class RepositoryRoot extends React.Component<Props> { to={`${url}/settings/general`} label={t("repositoryRoot.menu.settingsNavLink")} > - <NavLink repository={repository} editUrl={`${url}/settings/general`} /> + <GeneralRepoNavLink repository={repository} editUrl={`${url}/settings/general`} /> <PermissionsNavLink permissionUrl={`${url}/settings/permissions`} repository={repository} diff --git a/scm-ui/src/users/components/navLinks/GeneralUserNavLink.js b/scm-ui/src/users/components/navLinks/GeneralUserNavLink.js index d4bce5b8b5..417c9c76f6 100644 --- a/scm-ui/src/users/components/navLinks/GeneralUserNavLink.js +++ b/scm-ui/src/users/components/navLinks/GeneralUserNavLink.js @@ -17,7 +17,7 @@ class GeneralUserNavLink extends React.Component<Props> { if (!this.isEditable()) { return null; } - return <NavLink label={t("singleUser.menu.editNavLink")} to={editUrl} />; + return <NavLink label={t("singleUser.menu.generalNavLink")} to={editUrl} />; } isEditable = () => { From 2e3fcbd86029dbf88202c4199cc62f432b5cbdcf Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Wed, 23 Jan 2019 15:16:36 +0100 Subject: [PATCH 026/120] unified navlink components --- .../navLinks/GeneralGroupNavLink.js | 27 +++++++++---------- .../repos/components/GeneralRepoNavLink.js | 12 ++++++--- .../components/navLinks/GeneralUserNavLink.js | 16 +++++------ 3 files changed, 30 insertions(+), 25 deletions(-) diff --git a/scm-ui/src/groups/components/navLinks/GeneralGroupNavLink.js b/scm-ui/src/groups/components/navLinks/GeneralGroupNavLink.js index 5b0528ba3a..b04e572937 100644 --- a/scm-ui/src/groups/components/navLinks/GeneralGroupNavLink.js +++ b/scm-ui/src/groups/components/navLinks/GeneralGroupNavLink.js @@ -1,29 +1,28 @@ //@flow import React from "react"; +import type { Group } from "@scm-manager/ui-types"; import { NavLink } from "@scm-manager/ui-components"; import { translate } from "react-i18next"; -import type { Group } from "@scm-manager/ui-types"; type Props = { - t: string => string, + group: Group, editUrl: string, - group: Group + t: string => string }; -type State = {}; - -class GeneralGroupNavLink extends React.Component<Props, State> { - render() { - const { t, editUrl } = this.props; - if (!this.isEditable()) { - return null; - } - return <NavLink label={t("singleGroup.menu.generalNavLink")} to={editUrl} />; - } - +class GeneralGroupNavLink extends React.Component<Props> { isEditable = () => { return this.props.group._links.update; }; + + render() { + const { t, editUrl } = this.props; + + if (!this.isEditable()) { + return null; + } + return <NavLink to={editUrl} label={t("singleGroup.menu.generalNavLink")} />; + } } export default translate("groups")(GeneralGroupNavLink); diff --git a/scm-ui/src/repos/components/GeneralRepoNavLink.js b/scm-ui/src/repos/components/GeneralRepoNavLink.js index a76231529b..650cf0f62c 100644 --- a/scm-ui/src/repos/components/GeneralRepoNavLink.js +++ b/scm-ui/src/repos/components/GeneralRepoNavLink.js @@ -1,20 +1,26 @@ //@flow import React from "react"; +import type { Repository } from "@scm-manager/ui-types"; import { NavLink } from "@scm-manager/ui-components"; import { translate } from "react-i18next"; -import type { Repository } from "@scm-manager/ui-types"; -type Props = { editUrl: string, t: string => string, repository: Repository }; +type Props = { + repository: Repository, + editUrl: string, + t: string => string +}; class GeneralRepoNavLink extends React.Component<Props> { isEditable = () => { return this.props.repository._links.update; }; + render() { + const { editUrl, t } = this.props; + if (!this.isEditable()) { return null; } - const { editUrl, t } = this.props; return <NavLink to={editUrl} label={t("repositoryRoot.menu.generalNavLink")} />; } } diff --git a/scm-ui/src/users/components/navLinks/GeneralUserNavLink.js b/scm-ui/src/users/components/navLinks/GeneralUserNavLink.js index 417c9c76f6..e222a7bec2 100644 --- a/scm-ui/src/users/components/navLinks/GeneralUserNavLink.js +++ b/scm-ui/src/users/components/navLinks/GeneralUserNavLink.js @@ -1,28 +1,28 @@ //@flow import React from "react"; -import { translate } from "react-i18next"; import type { User } from "@scm-manager/ui-types"; import { NavLink } from "@scm-manager/ui-components"; +import { translate } from "react-i18next"; type Props = { - t: string => string, user: User, - editUrl: String + editUrl: String, + t: string => string }; class GeneralUserNavLink extends React.Component<Props> { + isEditable = () => { + return this.props.user._links.update; + }; + render() { const { t, editUrl } = this.props; if (!this.isEditable()) { return null; } - return <NavLink label={t("singleUser.menu.generalNavLink")} to={editUrl} />; + return <NavLink to={editUrl} label={t("singleUser.menu.generalNavLink")} />; } - - isEditable = () => { - return this.props.user._links.update; - }; } export default translate("users")(GeneralUserNavLink); From 7d0570e7eed36361d57b53b7dc9410d147cbd1c8 Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Wed, 23 Jan 2019 15:42:15 +0100 Subject: [PATCH 027/120] added extensionpoints + rehang gitplugin config --- .../src/config/ConfigurationBinder.js | 2 +- scm-ui/public/locales/en/repos.json | 1 + scm-ui/src/containers/Profile.js | 11 ++++ scm-ui/src/groups/containers/SingleGroup.js | 1 + .../repos/components/form/RepositoryForm.js | 55 +++++++++++-------- scm-ui/src/repos/containers/RepositoryRoot.js | 46 ++++++++++++---- scm-ui/src/users/containers/SingleUser.js | 11 ++++ 7 files changed, 92 insertions(+), 35 deletions(-) diff --git a/scm-ui-components/packages/ui-components/src/config/ConfigurationBinder.js b/scm-ui-components/packages/ui-components/src/config/ConfigurationBinder.js index 1b2b37bb19..146bbd371f 100644 --- a/scm-ui-components/packages/ui-components/src/config/ConfigurationBinder.js +++ b/scm-ui-components/packages/ui-components/src/config/ConfigurationBinder.js @@ -59,7 +59,7 @@ class ConfigurationBinder { }); // bind navigation link to extension point - binder.bind("repository.navigation", RepoNavLink, repoPredicate); + binder.bind("repository.subnavigation", RepoNavLink, repoPredicate); // route for global configuration, passes the current repository to component diff --git a/scm-ui/public/locales/en/repos.json b/scm-ui/public/locales/en/repos.json index 577386572e..510c64cfb7 100644 --- a/scm-ui/public/locales/en/repos.json +++ b/scm-ui/public/locales/en/repos.json @@ -57,6 +57,7 @@ } }, "repositoryForm": { + "subtitle": "Edit Repository", "submit": "Save" }, "sources": { diff --git a/scm-ui/src/containers/Profile.js b/scm-ui/src/containers/Profile.js index e603a565e8..40392d151e 100644 --- a/scm-ui/src/containers/Profile.js +++ b/scm-ui/src/containers/Profile.js @@ -18,6 +18,7 @@ import { } from "@scm-manager/ui-components"; import ChangeUserPassword from "./ChangeUserPassword"; import ProfileInfo from "./ProfileInfo"; +import {ExtensionPoint} from "@scm-manager/ui-extensions"; type Props = { me: Me, @@ -58,6 +59,11 @@ class Profile extends React.Component<Props, State> { ); } + const extensionProps = { + me, + url + }; + return ( <Page title={me.displayName}> <div className="columns"> @@ -83,6 +89,11 @@ class Profile extends React.Component<Props, State> { to={`${url}/settings/password`} label={t("profile.changePasswordNavLink")} /> + <ExtensionPoint + name="profile.subnavigation" + props={extensionProps} + renderAll={true} + /> </SubNavigation> </Section> </Navigation> diff --git a/scm-ui/src/groups/containers/SingleGroup.js b/scm-ui/src/groups/containers/SingleGroup.js index aefe73fe5d..72d6946220 100644 --- a/scm-ui/src/groups/containers/SingleGroup.js +++ b/scm-ui/src/groups/containers/SingleGroup.js @@ -138,6 +138,7 @@ class SingleGroup extends React.Component<Props> { group={group} permissionsUrl={`${url}/settings/permissions`} /> + <ExtensionPoint name="group.subnavigation" props={extensionProps} renderAll={true} /> </SubNavigation> </Section> </Navigation> diff --git a/scm-ui/src/repos/components/form/RepositoryForm.js b/scm-ui/src/repos/components/form/RepositoryForm.js index a97c76af8b..fcd88f0417 100644 --- a/scm-ui/src/repos/components/form/RepositoryForm.js +++ b/scm-ui/src/repos/components/form/RepositoryForm.js @@ -82,30 +82,39 @@ class RepositoryForm extends React.Component<Props, State> { const { loading, t } = this.props; const repository = this.state.repository; - return ( - <form onSubmit={this.submit}> - {this.renderCreateOnlyFields()} - <InputField - label={t("repository.contact")} - onChange={this.handleContactChange} - value={repository ? repository.contact : ""} - validationError={this.state.contactValidationError} - errorMessage={t("validation.contact-invalid")} - helpText={t("help.contactHelpText")} - /> + let subtitle = null; + if (this.props.repository) { + // edit existing repo + subtitle = <Subtitle subtitle={t("repositoryForm.subtitle")} />; + } - <Textarea - label={t("repository.description")} - onChange={this.handleDescriptionChange} - value={repository ? repository.description : ""} - helpText={t("help.descriptionHelpText")} - /> - <SubmitButton - disabled={!this.isValid()} - loading={loading} - label={t("repositoryForm.submit")} - /> - </form> + return ( + <> + {subtitle} + <form onSubmit={this.submit}> + {this.renderCreateOnlyFields()} + <InputField + label={t("repository.contact")} + onChange={this.handleContactChange} + value={repository ? repository.contact : ""} + validationError={this.state.contactValidationError} + errorMessage={t("validation.contact-invalid")} + helpText={t("help.contactHelpText")} + /> + + <Textarea + label={t("repository.description")} + onChange={this.handleDescriptionChange} + value={repository ? repository.description : ""} + helpText={t("help.descriptionHelpText")} + /> + <SubmitButton + disabled={!this.isValid()} + loading={loading} + label={t("repositoryForm.submit")} + /> + </form> + </> ); } diff --git a/scm-ui/src/repos/containers/RepositoryRoot.js b/scm-ui/src/repos/containers/RepositoryRoot.js index dcad647d4e..52c0a7ed83 100644 --- a/scm-ui/src/repos/containers/RepositoryRoot.js +++ b/scm-ui/src/repos/containers/RepositoryRoot.js @@ -1,18 +1,31 @@ //@flow import React from "react"; -import {fetchRepoByName, getFetchRepoFailure, getRepository, isFetchRepoPending} from "../modules/repos"; +import { + fetchRepoByName, + getFetchRepoFailure, + getRepository, + isFetchRepoPending +} from "../modules/repos"; -import {connect} from "react-redux"; -import {Route, Switch} from "react-router-dom"; -import type {Repository} from "@scm-manager/ui-types"; +import { connect } from "react-redux"; +import { Route, Switch } from "react-router-dom"; +import type { Repository } from "@scm-manager/ui-types"; -import {ErrorPage, Loading, Navigation, SubNavigation, NavLink, Page, Section} from "@scm-manager/ui-components"; -import {translate} from "react-i18next"; +import { + ErrorPage, + Loading, + Navigation, + SubNavigation, + NavLink, + Page, + Section +} from "@scm-manager/ui-components"; +import { translate } from "react-i18next"; import RepositoryDetails from "../components/RepositoryDetails"; import GeneralRepo from "./GeneralRepo"; import Permissions from "../permissions/containers/Permissions"; -import type {History} from "history"; +import type { History } from "history"; import GeneralRepoNavLink from "../components/GeneralRepoNavLink"; import BranchRoot from "./ChangesetsRoot"; @@ -20,8 +33,8 @@ import ChangesetView from "./ChangesetView"; import PermissionsNavLink from "../components/PermissionsNavLink"; import Sources from "../sources/containers/Sources"; import RepositoryNavLink from "../components/RepositoryNavLink"; -import {getRepositoriesLink} from "../../modules/indexResource"; -import {ExtensionPoint} from "@scm-manager/ui-extensions"; +import { getRepositoriesLink } from "../../modules/indexResource"; +import { ExtensionPoint } from "@scm-manager/ui-extensions"; type Props = { namespace: string, @@ -159,7 +172,10 @@ class RepositoryRoot extends React.Component<Props> { <div className="column"> <Navigation> <Section label={t("repositoryRoot.menu.navigationLabel")}> - <NavLink to={url} label={t("repositoryRoot.menu.informationNavLink")} /> + <NavLink + to={url} + label={t("repositoryRoot.menu.informationNavLink")} + /> <RepositoryNavLink repository={repository} linkName="changesets" @@ -184,11 +200,19 @@ class RepositoryRoot extends React.Component<Props> { to={`${url}/settings/general`} label={t("repositoryRoot.menu.settingsNavLink")} > - <GeneralRepoNavLink repository={repository} editUrl={`${url}/settings/general`} /> + <GeneralRepoNavLink + repository={repository} + editUrl={`${url}/settings/general`} + /> <PermissionsNavLink permissionUrl={`${url}/settings/permissions`} repository={repository} /> + <ExtensionPoint + name="repository.subnavigation" + props={extensionProps} + renderAll={true} + /> </SubNavigation> </Section> </Navigation> diff --git a/scm-ui/src/users/containers/SingleUser.js b/scm-ui/src/users/containers/SingleUser.js index ad95aa81ec..188de0aaee 100644 --- a/scm-ui/src/users/containers/SingleUser.js +++ b/scm-ui/src/users/containers/SingleUser.js @@ -26,6 +26,7 @@ import { translate } from "react-i18next"; import { getUsersLink } from "../../modules/indexResource"; import SetUserPassword from "../components/SetUserPassword"; import SetPermissions from "../../permissions/components/SetPermissions"; +import {ExtensionPoint} from "@scm-manager/ui-extensions"; type Props = { name: string, @@ -78,6 +79,11 @@ class SingleUser extends React.Component<Props> { const url = this.matchedUrl(); + const extensionProps = { + user, + url + }; + return ( <Page title={user.displayName}> <div className="columns"> @@ -123,6 +129,11 @@ class SingleUser extends React.Component<Props> { user={user} permissionsUrl={`${url}/settings/permissions`} /> + <ExtensionPoint + name="user.subnavigation" + props={extensionProps} + renderAll={true} + /> </SubNavigation> </Section> </Navigation> From f8bb3e0b2b985253cbaee76164373963c0c78a71 Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Wed, 23 Jan 2019 16:27:39 +0100 Subject: [PATCH 028/120] fixed tests --- .../src/groups/components/DeleteGroup.test.js | 22 +++++++++++++------ .../src/repos/components/DeleteRepo.test.js | 20 ++++++++++++----- .../components/PermissionsNavLink.test.js | 2 +- .../src/users/components/DeleteUser.test.js | 22 +++++++++++++------ 4 files changed, 45 insertions(+), 21 deletions(-) diff --git a/scm-ui/src/groups/components/DeleteGroup.test.js b/scm-ui/src/groups/components/DeleteGroup.test.js index 2364a196e8..043724d5f7 100644 --- a/scm-ui/src/groups/components/DeleteGroup.test.js +++ b/scm-ui/src/groups/components/DeleteGroup.test.js @@ -1,15 +1,20 @@ import React from "react"; import { mount, shallow } from "enzyme"; +import ReactRouterEnzymeContext from "react-router-enzyme-context"; + import "../../tests/enzyme"; -import "../../../tests/i18n"; +import "../../tests/i18n"; import DeleteGroup from "./DeleteGroup"; import { confirmAlert } from "@scm-manager/ui-components"; jest.mock("@scm-manager/ui-components", () => ({ confirmAlert: jest.fn(), - NavAction: require.requireActual("@scm-manager/ui-components").NavAction + Subtitle: require.requireActual("@scm-manager/ui-components").Subtitle, + DeleteButton: require.requireActual("@scm-manager/ui-components").DeleteButton })); +const options = new ReactRouterEnzymeContext(); + describe("DeleteGroupNavLink", () => { it("should render nothing, if the delete link is missing", () => { const group = { @@ -32,7 +37,8 @@ describe("DeleteGroupNavLink", () => { }; const navLink = mount( - <DeleteGroup group={group} deleteGroup={() => {}} /> + <DeleteGroup group={group} deleteGroup={() => {}} />, + options.get() ); expect(navLink.text()).not.toBe(""); }); @@ -47,9 +53,10 @@ describe("DeleteGroupNavLink", () => { }; const navLink = mount( - <DeleteGroup group={group} deleteGroup={() => {}} /> + <DeleteGroup group={group} deleteGroup={() => {}} />, + options.get() ); - navLink.find("a").simulate("click"); + navLink.find("button").simulate("click"); expect(confirmAlert.mock.calls.length).toBe(1); }); @@ -73,9 +80,10 @@ describe("DeleteGroupNavLink", () => { group={group} confirmDialog={false} deleteGroup={capture} - /> + />, + options.get() ); - navLink.find("a").simulate("click"); + navLink.find("button").simulate("click"); expect(calledUrl).toBe("/groups"); }); diff --git a/scm-ui/src/repos/components/DeleteRepo.test.js b/scm-ui/src/repos/components/DeleteRepo.test.js index 7985e02ef2..136cec6c03 100644 --- a/scm-ui/src/repos/components/DeleteRepo.test.js +++ b/scm-ui/src/repos/components/DeleteRepo.test.js @@ -1,5 +1,7 @@ import React from "react"; import { mount, shallow } from "enzyme"; +import ReactRouterEnzymeContext from "react-router-enzyme-context"; + import "../../tests/enzyme"; import "../../tests/i18n"; import DeleteRepo from "./DeleteRepo"; @@ -7,9 +9,12 @@ import DeleteRepo from "./DeleteRepo"; import { confirmAlert } from "@scm-manager/ui-components"; jest.mock("@scm-manager/ui-components", () => ({ confirmAlert: jest.fn(), - NavAction: require.requireActual("@scm-manager/ui-components").NavAction + Subtitle: require.requireActual("@scm-manager/ui-components").Subtitle, + DeleteButton: require.requireActual("@scm-manager/ui-components").DeleteButton })); +const options = new ReactRouterEnzymeContext(); + describe("DeleteRepo", () => { it("should render nothing, if the delete link is missing", () => { const repository = { @@ -32,7 +37,8 @@ describe("DeleteRepo", () => { }; const navLink = mount( - <DeleteRepo repository={repository} delete={() => {}} /> + <DeleteRepo repository={repository} delete={() => {}} />, + options.get() ); expect(navLink.text()).not.toBe(""); }); @@ -47,9 +53,10 @@ describe("DeleteRepo", () => { }; const navLink = mount( - <DeleteRepo repository={repository} delete={() => {}} /> + <DeleteRepo repository={repository} delete={() => {}} />, + options.get() ); - navLink.find("a").simulate("click"); + navLink.find("button").simulate("click"); expect(confirmAlert.mock.calls.length).toBe(1); }); @@ -73,9 +80,10 @@ describe("DeleteRepo", () => { repository={repository} confirmDialog={false} delete={capture} - /> + />, + options.get() ); - navLink.find("a").simulate("click"); + navLink.find("button").simulate("click"); expect(calledUrl).toBe("/repos"); }); diff --git a/scm-ui/src/repos/components/PermissionsNavLink.test.js b/scm-ui/src/repos/components/PermissionsNavLink.test.js index 5dddfe0cf4..3f6a95fe7d 100644 --- a/scm-ui/src/repos/components/PermissionsNavLink.test.js +++ b/scm-ui/src/repos/components/PermissionsNavLink.test.js @@ -33,6 +33,6 @@ describe("PermissionsNavLink", () => { <PermissionsNavLink repository={repository} permissionUrl="" />, options.get() ); - expect(navLink.text()).toBe("repository-root.menu.permissions"); + expect(navLink.text()).toBe("repositoryRoot.menu.permissionsNavLink"); }); }); diff --git a/scm-ui/src/users/components/DeleteUser.test.js b/scm-ui/src/users/components/DeleteUser.test.js index e03aad5b49..312efe51d9 100644 --- a/scm-ui/src/users/components/DeleteUser.test.js +++ b/scm-ui/src/users/components/DeleteUser.test.js @@ -1,15 +1,20 @@ import React from "react"; import { mount, shallow } from "enzyme"; +import ReactRouterEnzymeContext from "react-router-enzyme-context"; + import "../../tests/enzyme"; -import "../../../tests/i18n"; +import "../../tests/i18n"; import DeleteUser from "./DeleteUser"; import { confirmAlert } from "@scm-manager/ui-components"; jest.mock("@scm-manager/ui-components", () => ({ confirmAlert: jest.fn(), - NavAction: require.requireActual("@scm-manager/ui-components").NavAction + Subtitle: require.requireActual("@scm-manager/ui-components").Subtitle, + DeleteButton: require.requireActual("@scm-manager/ui-components").DeleteButton })); +const options = new ReactRouterEnzymeContext(); + describe("DeleteUser", () => { it("should render nothing, if the delete link is missing", () => { const user = { @@ -32,7 +37,8 @@ describe("DeleteUser", () => { }; const navLink = mount( - <DeleteUser user={user} deleteUser={() => {}} /> + <DeleteUser user={user} deleteUser={() => {}} />, + options.get() ); expect(navLink.text()).not.toBe(""); }); @@ -47,9 +53,10 @@ describe("DeleteUser", () => { }; const navLink = mount( - <DeleteUser user={user} deleteUser={() => {}} /> + <DeleteUser user={user} deleteUser={() => {}} />, + options.get() ); - navLink.find("a").simulate("click"); + navLink.find("button").simulate("click"); expect(confirmAlert.mock.calls.length).toBe(1); }); @@ -73,9 +80,10 @@ describe("DeleteUser", () => { user={user} confirmDialog={false} deleteUser={capture} - /> + />, + options.get() ); - navLink.find("a").simulate("click"); + navLink.find("button").simulate("click"); expect(calledUrl).toBe("/users"); }); From 27f02c7e8773c267a6ab53f89523d5b2bfd99ed0 Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Wed, 23 Jan 2019 17:12:10 +0100 Subject: [PATCH 029/120] added default icon for subnavigation and removed icons from subnavigation navlinks --- .../ui-components/src/navigation/SubNavigation.js | 9 ++++++++- .../groups/components/navLinks/GeneralGroupNavLink.js | 2 +- scm-ui/src/repos/components/GeneralRepoNavLink.js | 2 +- scm-ui/src/repos/components/PermissionsNavLink.js | 2 +- .../src/users/components/navLinks/GeneralUserNavLink.js | 2 +- 5 files changed, 12 insertions(+), 5 deletions(-) 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 0eef244f59..7acfd93841 100644 --- a/scm-ui-components/packages/ui-components/src/navigation/SubNavigation.js +++ b/scm-ui-components/packages/ui-components/src/navigation/SubNavigation.js @@ -4,6 +4,7 @@ import {Link, Route} from "react-router-dom"; type Props = { to: string, + icon?: string, label: string, activeOnlyWhenExact?: boolean, activeWhenMatch?: (route: any) => boolean, @@ -21,7 +22,12 @@ class SubNavigation extends React.Component<Props> { } renderLink = (route: any) => { - const { to, label } = this.props; + const { to, icon, label } = this.props; + + let defaultIcon = "fas fa-cog"; + if (icon) { + defaultIcon = icon; + } let children = null; if(this.isActive(route)) { @@ -33,6 +39,7 @@ class SubNavigation extends React.Component<Props> { return ( <li> <Link className={this.isActive(route) ? "is-active" : ""} to={to}> + <><i className={defaultIcon}></i>{" "}</> {label} </Link> {children} diff --git a/scm-ui/src/groups/components/navLinks/GeneralGroupNavLink.js b/scm-ui/src/groups/components/navLinks/GeneralGroupNavLink.js index 291742cf25..b04e572937 100644 --- a/scm-ui/src/groups/components/navLinks/GeneralGroupNavLink.js +++ b/scm-ui/src/groups/components/navLinks/GeneralGroupNavLink.js @@ -21,7 +21,7 @@ class GeneralGroupNavLink extends React.Component<Props> { if (!this.isEditable()) { return null; } - return <NavLink to={editUrl} icon="fas fa-cog" label={t("singleGroup.menu.generalNavLink")} />; + return <NavLink to={editUrl} label={t("singleGroup.menu.generalNavLink")} />; } } diff --git a/scm-ui/src/repos/components/GeneralRepoNavLink.js b/scm-ui/src/repos/components/GeneralRepoNavLink.js index 314c86c6ff..650cf0f62c 100644 --- a/scm-ui/src/repos/components/GeneralRepoNavLink.js +++ b/scm-ui/src/repos/components/GeneralRepoNavLink.js @@ -21,7 +21,7 @@ class GeneralRepoNavLink extends React.Component<Props> { if (!this.isEditable()) { return null; } - return <NavLink to={editUrl} icon="fas fa-cog" label={t("repositoryRoot.menu.generalNavLink")} />; + return <NavLink to={editUrl} label={t("repositoryRoot.menu.generalNavLink")} />; } } diff --git a/scm-ui/src/repos/components/PermissionsNavLink.js b/scm-ui/src/repos/components/PermissionsNavLink.js index 1d6d52eb0b..773ad94246 100644 --- a/scm-ui/src/repos/components/PermissionsNavLink.js +++ b/scm-ui/src/repos/components/PermissionsNavLink.js @@ -20,7 +20,7 @@ class PermissionsNavLink extends React.Component<Props> { } const { permissionUrl, t } = this.props; return ( - <NavLink to={permissionUrl} icon="fas fa-lock" label={t("repositoryRoot.menu.permissionsNavLink")} /> + <NavLink to={permissionUrl} label={t("repositoryRoot.menu.permissionsNavLink")} /> ); } } diff --git a/scm-ui/src/users/components/navLinks/GeneralUserNavLink.js b/scm-ui/src/users/components/navLinks/GeneralUserNavLink.js index a9b3b9a37f..e222a7bec2 100644 --- a/scm-ui/src/users/components/navLinks/GeneralUserNavLink.js +++ b/scm-ui/src/users/components/navLinks/GeneralUserNavLink.js @@ -21,7 +21,7 @@ class GeneralUserNavLink extends React.Component<Props> { if (!this.isEditable()) { return null; } - return <NavLink to={editUrl} icon="fas fa-cog" label={t("singleUser.menu.generalNavLink")} />; + return <NavLink to={editUrl} label={t("singleUser.menu.generalNavLink")} />; } } From df8f2af3416770c1db1e3a3374c17650ce03b134 Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Wed, 23 Jan 2019 17:14:29 +0100 Subject: [PATCH 030/120] small icon correction --- .../packages/ui-components/src/navigation/NavLink.js | 2 +- .../packages/ui-components/src/navigation/SubNavigation.js | 3 ++- 2 files changed, 3 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 53b124ef31..98c3138a8f 100644 --- a/scm-ui-components/packages/ui-components/src/navigation/NavLink.js +++ b/scm-ui-components/packages/ui-components/src/navigation/NavLink.js @@ -28,7 +28,7 @@ class NavLink extends React.Component<Props> { let showIcon = null; if (icon) { - showIcon = (<><i className={icon}></i>{" "}</>); + showIcon = (<><i className={icon} />{" "}</>); } 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 7acfd93841..4d2a6b2a42 100644 --- a/scm-ui-components/packages/ui-components/src/navigation/SubNavigation.js +++ b/scm-ui-components/packages/ui-components/src/navigation/SubNavigation.js @@ -39,7 +39,8 @@ class SubNavigation extends React.Component<Props> { return ( <li> <Link className={this.isActive(route) ? "is-active" : ""} to={to}> - <><i className={defaultIcon}></i>{" "}</> + <i className={defaultIcon} /> + {" "} {label} </Link> {children} From f06d2d67a3b65b6f6dd237bd57ffa5c19441982c Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Tue, 29 Jan 2019 08:22:43 +0100 Subject: [PATCH 031/120] renamed general to edit --- .../{GeneralGroupNavLink.js => EditGroupNavLink.js} | 4 ++-- ...neralGroupNavLink.test.js => EditGroupNavLink.test.js} | 6 +++--- scm-ui/src/groups/components/navLinks/index.js | 2 +- .../groups/containers/{GeneralGroup.js => EditGroup.js} | 4 ++-- scm-ui/src/groups/containers/SingleGroup.js | 8 ++++---- .../{GeneralRepoNavLink.js => EditRepoNavLink.js} | 4 ++-- ...GeneralRepoNavLink.test.js => EditRepoNavLink.test.js} | 6 +++--- .../src/repos/containers/{GeneralRepo.js => EditRepo.js} | 4 ++-- scm-ui/src/repos/containers/RepositoryRoot.js | 8 ++++---- .../{GeneralUserNavLink.js => EditUserNavLink.js} | 4 ++-- .../users/components/navLinks/GeneralUserNavLink.test.js | 6 +++--- scm-ui/src/users/components/navLinks/index.js | 2 +- .../src/users/containers/{GeneralUser.js => EditUser.js} | 4 ++-- scm-ui/src/users/containers/SingleUser.js | 8 ++++---- 14 files changed, 35 insertions(+), 35 deletions(-) rename scm-ui/src/groups/components/navLinks/{GeneralGroupNavLink.js => EditGroupNavLink.js} (82%) rename scm-ui/src/groups/components/navLinks/{GeneralGroupNavLink.test.js => EditGroupNavLink.test.js} (66%) rename scm-ui/src/groups/containers/{GeneralGroup.js => EditGroup.js} (97%) rename scm-ui/src/repos/components/{GeneralRepoNavLink.js => EditRepoNavLink.js} (83%) rename scm-ui/src/repos/components/{GeneralRepoNavLink.test.js => EditRepoNavLink.test.js} (81%) rename scm-ui/src/repos/containers/{GeneralRepo.js => EditRepo.js} (96%) rename scm-ui/src/users/components/navLinks/{GeneralUserNavLink.js => EditUserNavLink.js} (82%) rename scm-ui/src/users/containers/{GeneralUser.js => EditUser.js} (96%) diff --git a/scm-ui/src/groups/components/navLinks/GeneralGroupNavLink.js b/scm-ui/src/groups/components/navLinks/EditGroupNavLink.js similarity index 82% rename from scm-ui/src/groups/components/navLinks/GeneralGroupNavLink.js rename to scm-ui/src/groups/components/navLinks/EditGroupNavLink.js index b04e572937..9713c5c5c9 100644 --- a/scm-ui/src/groups/components/navLinks/GeneralGroupNavLink.js +++ b/scm-ui/src/groups/components/navLinks/EditGroupNavLink.js @@ -10,7 +10,7 @@ type Props = { t: string => string }; -class GeneralGroupNavLink extends React.Component<Props> { +class EditGroupNavLink extends React.Component<Props> { isEditable = () => { return this.props.group._links.update; }; @@ -25,4 +25,4 @@ class GeneralGroupNavLink extends React.Component<Props> { } } -export default translate("groups")(GeneralGroupNavLink); +export default translate("groups")(EditGroupNavLink); diff --git a/scm-ui/src/groups/components/navLinks/GeneralGroupNavLink.test.js b/scm-ui/src/groups/components/navLinks/EditGroupNavLink.test.js similarity index 66% rename from scm-ui/src/groups/components/navLinks/GeneralGroupNavLink.test.js rename to scm-ui/src/groups/components/navLinks/EditGroupNavLink.test.js index 1120dab2fd..7399f4f714 100644 --- a/scm-ui/src/groups/components/navLinks/GeneralGroupNavLink.test.js +++ b/scm-ui/src/groups/components/navLinks/EditGroupNavLink.test.js @@ -4,14 +4,14 @@ import React from "react"; import { shallow } from "enzyme"; import "../../../tests/enzyme"; import "../../../tests/i18n"; -import GeneralGroupNavLink from "./GeneralGroupNavLink"; +import EditGroupNavLink from "./EditGroupNavLink"; it("should render nothing, if the edit link is missing", () => { const group = { _links: {} }; - const navLink = shallow(<GeneralGroupNavLink group={group} editUrl='/group/edit'/>); + const navLink = shallow(<EditGroupNavLink group={group} editUrl='/group/edit'/>); expect(navLink.text()).toBe(""); }); @@ -24,6 +24,6 @@ it("should render the navLink", () => { } }; - const navLink = shallow(<GeneralGroupNavLink group={group} editUrl='/group/edit'/>); + const navLink = shallow(<EditGroupNavLink group={group} editUrl='/group/edit'/>); expect(navLink.text()).not.toBe(""); }); diff --git a/scm-ui/src/groups/components/navLinks/index.js b/scm-ui/src/groups/components/navLinks/index.js index 88e2e7f4b5..992e9ab9a3 100644 --- a/scm-ui/src/groups/components/navLinks/index.js +++ b/scm-ui/src/groups/components/navLinks/index.js @@ -1,2 +1,2 @@ -export { default as GeneralGroupNavLink } from "./GeneralGroupNavLink"; +export { default as EditGroupNavLink } from "./EditGroupNavLink"; export { default as SetPermissionsNavLink } from "./SetPermissionsNavLink"; diff --git a/scm-ui/src/groups/containers/GeneralGroup.js b/scm-ui/src/groups/containers/EditGroup.js similarity index 97% rename from scm-ui/src/groups/containers/GeneralGroup.js rename to scm-ui/src/groups/containers/EditGroup.js index a89491568f..241a61356a 100644 --- a/scm-ui/src/groups/containers/GeneralGroup.js +++ b/scm-ui/src/groups/containers/EditGroup.js @@ -30,7 +30,7 @@ type Props = { error: Error }; -class GeneralGroup extends React.Component<Props> { +class EditGroup extends React.Component<Props> { componentDidMount() { const { group, modifyGroupReset } = this.props; modifyGroupReset(group); @@ -116,4 +116,4 @@ const mapDispatchToProps = dispatch => { export default connect( mapStateToProps, mapDispatchToProps -)(withRouter(GeneralGroup)); +)(withRouter(EditGroup)); diff --git a/scm-ui/src/groups/containers/SingleGroup.js b/scm-ui/src/groups/containers/SingleGroup.js index 3db1fa0006..f8d88d7a7d 100644 --- a/scm-ui/src/groups/containers/SingleGroup.js +++ b/scm-ui/src/groups/containers/SingleGroup.js @@ -13,7 +13,7 @@ import { import { Route } from "react-router"; import { Details } from "./../components/table"; import { - GeneralGroupNavLink, + EditGroupNavLink, SetPermissionsNavLink } from "./../components/navLinks"; import type { Group } from "@scm-manager/ui-types"; @@ -26,7 +26,7 @@ import { } from "../modules/groups"; import { translate } from "react-i18next"; -import GeneralGroup from "./GeneralGroup"; +import EditGroup from "./EditGroup"; import { getGroupsLink } from "../../modules/indexResource"; import SetPermissions from "../../permissions/components/SetPermissions"; import { ExtensionPoint } from "@scm-manager/ui-extensions"; @@ -99,7 +99,7 @@ class SingleGroup extends React.Component<Props> { <Route path={`${url}/settings/general`} exact - component={() => <GeneralGroup group={group} />} + component={() => <EditGroup group={group} />} /> <Route path={`${url}/settings/permissions`} @@ -133,7 +133,7 @@ class SingleGroup extends React.Component<Props> { to={`${url}/settings/general`} label={t("singleGroup.menu.settingsNavLink")} > - <GeneralGroupNavLink + <EditGroupNavLink group={group} editUrl={`${url}/settings/general`} /> diff --git a/scm-ui/src/repos/components/GeneralRepoNavLink.js b/scm-ui/src/repos/components/EditRepoNavLink.js similarity index 83% rename from scm-ui/src/repos/components/GeneralRepoNavLink.js rename to scm-ui/src/repos/components/EditRepoNavLink.js index 650cf0f62c..9a502cdafb 100644 --- a/scm-ui/src/repos/components/GeneralRepoNavLink.js +++ b/scm-ui/src/repos/components/EditRepoNavLink.js @@ -10,7 +10,7 @@ type Props = { t: string => string }; -class GeneralRepoNavLink extends React.Component<Props> { +class EditRepoNavLink extends React.Component<Props> { isEditable = () => { return this.props.repository._links.update; }; @@ -25,4 +25,4 @@ class GeneralRepoNavLink extends React.Component<Props> { } } -export default translate("repos")(GeneralRepoNavLink); +export default translate("repos")(EditRepoNavLink); diff --git a/scm-ui/src/repos/components/GeneralRepoNavLink.test.js b/scm-ui/src/repos/components/EditRepoNavLink.test.js similarity index 81% rename from scm-ui/src/repos/components/GeneralRepoNavLink.test.js rename to scm-ui/src/repos/components/EditRepoNavLink.test.js index 2ce050f69c..22bb06fae0 100644 --- a/scm-ui/src/repos/components/GeneralRepoNavLink.test.js +++ b/scm-ui/src/repos/components/EditRepoNavLink.test.js @@ -3,7 +3,7 @@ import { shallow, mount } from "enzyme"; import "../../tests/enzyme"; import "../../tests/i18n"; import ReactRouterEnzymeContext from "react-router-enzyme-context"; -import GeneralRepoNavLink from "./GeneralRepoNavLink"; +import EditRepoNavLink from "./EditRepoNavLink"; describe("GeneralNavLink", () => { const options = new ReactRouterEnzymeContext(); @@ -14,7 +14,7 @@ describe("GeneralNavLink", () => { }; const navLink = shallow( - <GeneralRepoNavLink repository={repository} editUrl="" />, + <EditRepoNavLink repository={repository} editUrl="" />, options.get() ); expect(navLink.text()).toBe(""); @@ -30,7 +30,7 @@ describe("GeneralNavLink", () => { }; const navLink = mount( - <GeneralRepoNavLink repository={repository} editUrl="" />, + <EditRepoNavLink repository={repository} editUrl="" />, options.get() ); expect(navLink.text()).toBe("repositoryRoot.menu.generalNavLink"); diff --git a/scm-ui/src/repos/containers/GeneralRepo.js b/scm-ui/src/repos/containers/EditRepo.js similarity index 96% rename from scm-ui/src/repos/containers/GeneralRepo.js rename to scm-ui/src/repos/containers/EditRepo.js index 83c5d47b52..072a45b670 100644 --- a/scm-ui/src/repos/containers/GeneralRepo.js +++ b/scm-ui/src/repos/containers/EditRepo.js @@ -28,7 +28,7 @@ type Props = { history: History }; -class GeneralRepo extends React.Component<Props> { +class EditRepo extends React.Component<Props> { componentDidMount() { const { modifyRepoReset, repository } = this.props; modifyRepoReset(repository); @@ -93,4 +93,4 @@ const mapDispatchToProps = dispatch => { export default connect( mapStateToProps, mapDispatchToProps -)(withRouter(GeneralRepo)); +)(withRouter(EditRepo)); diff --git a/scm-ui/src/repos/containers/RepositoryRoot.js b/scm-ui/src/repos/containers/RepositoryRoot.js index 9291017ba9..583d351a97 100644 --- a/scm-ui/src/repos/containers/RepositoryRoot.js +++ b/scm-ui/src/repos/containers/RepositoryRoot.js @@ -22,11 +22,11 @@ import { } from "@scm-manager/ui-components"; import { translate } from "react-i18next"; import RepositoryDetails from "../components/RepositoryDetails"; -import GeneralRepo from "./GeneralRepo"; +import EditRepo from "./EditRepo"; import Permissions from "../permissions/containers/Permissions"; import type { History } from "history"; -import GeneralRepoNavLink from "../components/GeneralRepoNavLink"; +import EditRepoNavLink from "../components/EditRepoNavLink"; import BranchRoot from "./ChangesetsRoot"; import ChangesetView from "./ChangesetView"; @@ -113,7 +113,7 @@ class RepositoryRoot extends React.Component<Props> { /> <Route path={`${url}/settings/general`} - component={() => <GeneralRepo repository={repository} />} + component={() => <EditRepo repository={repository} />} /> <Route path={`${url}/settings/permissions`} @@ -203,7 +203,7 @@ class RepositoryRoot extends React.Component<Props> { to={`${url}/settings/general`} label={t("repositoryRoot.menu.settingsNavLink")} > - <GeneralRepoNavLink + <EditRepoNavLink repository={repository} editUrl={`${url}/settings/general`} /> diff --git a/scm-ui/src/users/components/navLinks/GeneralUserNavLink.js b/scm-ui/src/users/components/navLinks/EditUserNavLink.js similarity index 82% rename from scm-ui/src/users/components/navLinks/GeneralUserNavLink.js rename to scm-ui/src/users/components/navLinks/EditUserNavLink.js index e222a7bec2..051bf9a4bd 100644 --- a/scm-ui/src/users/components/navLinks/GeneralUserNavLink.js +++ b/scm-ui/src/users/components/navLinks/EditUserNavLink.js @@ -10,7 +10,7 @@ type Props = { t: string => string }; -class GeneralUserNavLink extends React.Component<Props> { +class EditUserNavLink extends React.Component<Props> { isEditable = () => { return this.props.user._links.update; }; @@ -25,4 +25,4 @@ class GeneralUserNavLink extends React.Component<Props> { } } -export default translate("users")(GeneralUserNavLink); +export default translate("users")(EditUserNavLink); diff --git a/scm-ui/src/users/components/navLinks/GeneralUserNavLink.test.js b/scm-ui/src/users/components/navLinks/GeneralUserNavLink.test.js index 018e98f47e..ec46c531a1 100644 --- a/scm-ui/src/users/components/navLinks/GeneralUserNavLink.test.js +++ b/scm-ui/src/users/components/navLinks/GeneralUserNavLink.test.js @@ -2,14 +2,14 @@ import React from "react"; import { shallow } from "enzyme"; import "../../../tests/enzyme"; import "../../../tests/i18n"; -import GeneralUserNavLink from "./GeneralUserNavLink"; +import EditUserNavLink from "./EditUserNavLink"; it("should render nothing, if the edit link is missing", () => { const user = { _links: {} }; - const navLink = shallow(<GeneralUserNavLink user={user} editUrl='/user/edit'/>); + const navLink = shallow(<EditUserNavLink user={user} editUrl='/user/edit'/>); expect(navLink.text()).toBe(""); }); @@ -22,6 +22,6 @@ it("should render the navLink", () => { } }; - const navLink = shallow(<GeneralUserNavLink user={user} editUrl='/user/edit'/>); + const navLink = shallow(<EditUserNavLink user={user} editUrl='/user/edit'/>); expect(navLink.text()).not.toBe(""); }); diff --git a/scm-ui/src/users/components/navLinks/index.js b/scm-ui/src/users/components/navLinks/index.js index 87eda98f68..cb97c57e3f 100644 --- a/scm-ui/src/users/components/navLinks/index.js +++ b/scm-ui/src/users/components/navLinks/index.js @@ -1,3 +1,3 @@ -export { default as GeneralUserNavLink } from "./GeneralUserNavLink"; +export { default as EditUserNavLink } from "./EditUserNavLink"; export { default as SetPasswordNavLink } from "./SetPasswordNavLink"; export { default as SetPermissionsNavLink } from "./SetPermissionsNavLink"; diff --git a/scm-ui/src/users/containers/GeneralUser.js b/scm-ui/src/users/containers/EditUser.js similarity index 96% rename from scm-ui/src/users/containers/GeneralUser.js rename to scm-ui/src/users/containers/EditUser.js index cb578d6f1e..6b061c48bf 100644 --- a/scm-ui/src/users/containers/GeneralUser.js +++ b/scm-ui/src/users/containers/EditUser.js @@ -31,7 +31,7 @@ type Props = { history: History }; -class GeneralUser extends React.Component<Props> { +class EditUser extends React.Component<Props> { componentDidMount() { const { modifyUserReset, user } = this.props; modifyUserReset(user); @@ -96,4 +96,4 @@ const mapDispatchToProps = dispatch => { export default connect( mapStateToProps, mapDispatchToProps -)(withRouter(GeneralUser)); +)(withRouter(EditUser)); diff --git a/scm-ui/src/users/containers/SingleUser.js b/scm-ui/src/users/containers/SingleUser.js index 64ed7946e4..9a1633a162 100644 --- a/scm-ui/src/users/containers/SingleUser.js +++ b/scm-ui/src/users/containers/SingleUser.js @@ -12,7 +12,7 @@ import { } from "@scm-manager/ui-components"; import { Route } from "react-router"; import { Details } from "./../components/table"; -import GeneralUser from "./GeneralUser"; +import EditUser from "./EditUser"; import type { User } from "@scm-manager/ui-types"; import type { History } from "history"; import { @@ -21,7 +21,7 @@ import { isFetchUserPending, getFetchUserFailure } from "../modules/users"; -import { GeneralUserNavLink, SetPasswordNavLink, SetPermissionsNavLink } from "./../components/navLinks"; +import { EditUserNavLink, SetPasswordNavLink, SetPermissionsNavLink } from "./../components/navLinks"; import { translate } from "react-i18next"; import { getUsersLink } from "../../modules/indexResource"; import SetUserPassword from "../components/SetUserPassword"; @@ -91,7 +91,7 @@ class SingleUser extends React.Component<Props> { <Route path={url} exact component={() => <Details user={user} />} /> <Route path={`${url}/settings/general`} - component={() => <GeneralUser user={user} />} + component={() => <EditUser user={user} />} /> <Route path={`${url}/settings/password`} @@ -118,7 +118,7 @@ class SingleUser extends React.Component<Props> { to={`${url}/settings/general`} label={t("singleUser.menu.settingsNavLink")} > - <GeneralUserNavLink + <EditUserNavLink user={user} editUrl={`${url}/settings/general`} /> From a13a144c21c0712b711dfd60560bfef6e0608bae Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Tue, 29 Jan 2019 08:58:45 +0100 Subject: [PATCH 032/120] redesigned submenu --- .../src/navigation/SubNavigation.js | 20 ++++---- scm-ui/styles/scm.scss | 51 ++++++++++++++----- 2 files changed, 48 insertions(+), 23 deletions(-) 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 4d2a6b2a42..0a6612a173 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,6 @@ //@flow import * as React from "react"; -import {Link, Route} from "react-router-dom"; +import { Link, Route } from "react-router-dom"; type Props = { to: string, @@ -30,18 +30,14 @@ class SubNavigation extends React.Component<Props> { } let children = null; - if(this.isActive(route)) { - children = ( - <ul>{this.props.children}</ul> - ); + if (this.isActive(route)) { + children = <ul className="sub-menu">{this.props.children}</ul>; } return ( <li> <Link className={this.isActive(route) ? "is-active" : ""} to={to}> - <i className={defaultIcon} /> - {" "} - {label} + <i className={defaultIcon} /> {label} </Link> {children} </li> @@ -53,11 +49,15 @@ class SubNavigation extends React.Component<Props> { // removes last part of url let parents = to.split("/"); - parents.splice(-1,1); + parents.splice(-1, 1); let parent = parents.join("/"); return ( - <Route path={parent} exact={activeOnlyWhenExact} children={this.renderLink} /> + <Route + path={parent} + exact={activeOnlyWhenExact} + children={this.renderLink} + /> ); } } diff --git a/scm-ui/styles/scm.scss b/scm-ui/styles/scm.scss index 3ceab48f9f..a5d2eda70e 100644 --- a/scm-ui/styles/scm.scss +++ b/scm-ui/styles/scm.scss @@ -259,21 +259,10 @@ $fa-font-path: "webfonts"; &.is-active { color: $blue; background-color: #fff; - - &:before { - position: relative; - content: " "; - background: $blue; - height: 53px; - width: 2px; - display: block; - left: -17px; - float: left; - top: -16px; - } } } - li { + + > li { ul { margin: 0; border-top: 1px solid #eee; @@ -286,6 +275,18 @@ $fa-font-path: "webfonts"; } } + > a.is-active:before { + position: relative; + content: " "; + background: $blue; + height: 53px; + width: 2px; + display: block; + left: -17px; + float: left; + top: -16px; + } + border-radius: 0; border-top: 1px solid #eee; border-left: 1px solid #eee; @@ -301,3 +302,27 @@ $fa-font-path: "webfonts"; margin-bottom: 0; } } +.sub-menu li { + line-height: 1; + + a { + padding: 0.75rem 1rem; + } + + a:before { + font-family: "Font Awesome 5 Free"; + font-weight: 900; + -webkit-font-smoothing: antialiased; + display: inline-block; + font-style: normal; + font-variant: normal; + text-rendering: auto; + line-height: 1; + content: "\f105"; + padding-right: 5px; + } + + i { + display: none; + } +} From d201c156f0fb927dd3efbf21448ec9d55214538a Mon Sep 17 00:00:00 2001 From: Mohamed Karray <mohamed.karray.sr@gmail.com> Date: Tue, 29 Jan 2019 14:05:12 +0100 Subject: [PATCH 033/120] fix get modifications for SVN --- .../spi/SvnModificationsCommand.java | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnModificationsCommand.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnModificationsCommand.java index 4b4f655b12..bbcaa9a9d5 100644 --- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnModificationsCommand.java +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnModificationsCommand.java @@ -4,6 +4,8 @@ import lombok.extern.slf4j.Slf4j; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.SVNLogEntry; import org.tmatesoft.svn.core.io.SVNRepository; +import org.tmatesoft.svn.core.wc.SVNClientManager; +import org.tmatesoft.svn.core.wc.admin.SVNLookClient; import sonia.scm.repository.InternalRepositoryException; import sonia.scm.repository.Modifications; import sonia.scm.repository.Repository; @@ -23,20 +25,31 @@ public class SvnModificationsCommand extends AbstractSvnCommand implements Modif @Override @SuppressWarnings("unchecked") public Modifications getModifications(String revision) { - Modifications modifications = null; + final Modifications modifications = new Modifications(); log.debug("get modifications {}", revision); try { - long revisionNumber = SvnUtil.parseRevision(revision, repository); - SVNRepository repo = open(); - Collection<SVNLogEntry> entries = repo.log(null, null, revisionNumber, - revisionNumber, true, true); - if (Util.isNotEmpty(entries)) { - modifications = SvnUtil.createModifications(entries.iterator().next(), revision); + if (SvnUtil.isTransactionEntryId(revision)) { + + SVNLookClient client = SVNClientManager.newInstance().getLookClient(); + client.doGetChanged(context.getDirectory(), SvnUtil.getTransactionId(revision), + e -> SvnUtil.appendModification(modifications, e.getType(), e.getPath()), true); + + return modifications; + + } else { + + long revisionNumber = SvnUtil.getRevisionNumber(revision, repository); + SVNRepository repo = open(); + Collection<SVNLogEntry> entries = repo.log(null, null, revisionNumber, + revisionNumber, true, true); + if (Util.isNotEmpty(entries)) { + return SvnUtil.createModifications(entries.iterator().next(), revision); + } } } catch (SVNException ex) { throw new InternalRepositoryException(repository, "could not open repository", ex); } - return modifications; + return null; } @Override From 9959eb375b99fbfce9fc4ee5efc970a239b28003 Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Tue, 29 Jan 2019 15:17:51 +0100 Subject: [PATCH 034/120] added TableHeader component --- .../ui-components/src/BranchSelector.js | 16 ++------- .../packages/ui-components/src/TableHeader.js | 36 +++++++++++++++++++ .../packages/ui-components/src/index.js | 1 + 3 files changed, 40 insertions(+), 13 deletions(-) create mode 100644 scm-ui-components/packages/ui-components/src/TableHeader.js diff --git a/scm-ui-components/packages/ui-components/src/BranchSelector.js b/scm-ui-components/packages/ui-components/src/BranchSelector.js index d03011bfdd..f44de639ba 100644 --- a/scm-ui-components/packages/ui-components/src/BranchSelector.js +++ b/scm-ui-components/packages/ui-components/src/BranchSelector.js @@ -2,6 +2,7 @@ import React from "react"; import type {Branch} from "@scm-manager/ui-types"; +import TableHeader from "./TableHeader"; import injectSheet from "react-jss"; import classNames from "classnames"; import DropDown from "./forms/DropDown"; @@ -12,11 +13,6 @@ const styles = { }, minWidthOfLabel: { minWidth: "4.5rem" - }, - wrapper: { - padding: "1rem 1.5rem 0.25rem 1.5rem", - border: "1px solid #eee", - borderRadius: "5px 5px 0 0" } }; @@ -48,13 +44,7 @@ class BranchSelector extends React.Component<Props, State> { if (branches) { return ( - <div - className={classNames( - "has-background-light field", - "is-horizontal", - classes.wrapper - )} - > + <TableHeader> <div className={classNames( "field-label", @@ -81,7 +71,7 @@ class BranchSelector extends React.Component<Props, State> { </div> </div> </div> - </div> + </TableHeader> ); } else { return null; diff --git a/scm-ui-components/packages/ui-components/src/TableHeader.js b/scm-ui-components/packages/ui-components/src/TableHeader.js new file mode 100644 index 0000000000..8b38160027 --- /dev/null +++ b/scm-ui-components/packages/ui-components/src/TableHeader.js @@ -0,0 +1,36 @@ +//@flow +import * as React from "react"; +import classNames from "classnames"; +import injectSheet from "react-jss"; + +type Props = { + children?: React.Node, + classes: Object +}; + +const styles = { + wrapper: { + padding: "1rem 1.5rem 0.25rem 1.5rem", + border: "1px solid #eee", + borderRadius: "5px 5px 0 0" + } +}; + +class TableHeader extends React.Component<Props> { + render() { + const { classes, children } = this.props; + return ( + <div + className={classNames( + "has-background-light field", + "is-horizontal", + classes.wrapper + )} + > + {children} + </div> + ); + } +} + +export default injectSheet(styles)(TableHeader); diff --git a/scm-ui-components/packages/ui-components/src/index.js b/scm-ui-components/packages/ui-components/src/index.js index 5b3cdb4c95..d510e28ade 100644 --- a/scm-ui-components/packages/ui-components/src/index.js +++ b/scm-ui-components/packages/ui-components/src/index.js @@ -25,6 +25,7 @@ export { default as Tooltip } from "./Tooltip"; export { getPageFromMatch } from "./urls"; export { default as Autocomplete} from "./Autocomplete"; export { default as BranchSelector } from "./BranchSelector"; +export { default as TableHeader } from "./TableHeader"; export { apiClient, NOT_FOUND_ERROR, UNAUTHORIZED_ERROR, CONFLICT_ERROR } from "./apiclient.js"; From d70c2bd6245ff169691115d7ca4af6532d2e2fae Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Tue, 29 Jan 2019 16:38:01 +0100 Subject: [PATCH 035/120] changed border handling --- .../packages/ui-components/src/TableHeader.js | 3 +-- scm-ui/src/repos/containers/ChangesetsRoot.js | 4 ++-- scm-ui/src/repos/sources/containers/Sources.js | 2 +- scm-ui/styles/scm.scss | 9 +++++---- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/scm-ui-components/packages/ui-components/src/TableHeader.js b/scm-ui-components/packages/ui-components/src/TableHeader.js index 8b38160027..f7463d3136 100644 --- a/scm-ui-components/packages/ui-components/src/TableHeader.js +++ b/scm-ui-components/packages/ui-components/src/TableHeader.js @@ -11,8 +11,7 @@ type Props = { const styles = { wrapper: { padding: "1rem 1.5rem 0.25rem 1.5rem", - border: "1px solid #eee", - borderRadius: "5px 5px 0 0" + borderBottom: "1px solid #dbdbdb" } }; diff --git a/scm-ui/src/repos/containers/ChangesetsRoot.js b/scm-ui/src/repos/containers/ChangesetsRoot.js index 2eea64b8e1..c65c8f32cb 100644 --- a/scm-ui/src/repos/containers/ChangesetsRoot.js +++ b/scm-ui/src/repos/containers/ChangesetsRoot.js @@ -89,10 +89,10 @@ class BranchRoot extends React.Component<Props> { const changesets = <Changesets repository={repository} branch={branch} />; return ( - <> + <div className="has-border-around is-round"> {this.renderBranchSelector()} <Route path={`${url}/:page?`} component={() => changesets} /> - </> + </div> ); } diff --git a/scm-ui/src/repos/sources/containers/Sources.js b/scm-ui/src/repos/sources/containers/Sources.js index 810e2309ee..321c1faa16 100644 --- a/scm-ui/src/repos/sources/containers/Sources.js +++ b/scm-ui/src/repos/sources/containers/Sources.js @@ -93,7 +93,7 @@ class Sources extends React.Component<Props> { if (currentFileIsDirectory) { return ( - <div className={"has-border-around"}> + <div className="has-border-around is-round"> {this.renderBranchSelector()} <FileTree repository={repository} diff --git a/scm-ui/styles/scm.scss b/scm-ui/styles/scm.scss index d31f40ceea..620410bd48 100644 --- a/scm-ui/styles/scm.scss +++ b/scm-ui/styles/scm.scss @@ -87,10 +87,11 @@ $fa-font-path: "webfonts"; //border around options .has-border-around { - border-top: 1px solid #eee; - border-left: 1px solid #eee; - border-right: 1px solid #eee; - border-bottom: 1px solid #eee; + border: 1px solid #dbdbdb; + + &.is-round { + border-radius: 4px; + } } // multiline Columns From 258e11025a61894606b69b336fd2f3b8dd06e769 Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Tue, 29 Jan 2019 17:12:18 +0100 Subject: [PATCH 036/120] changed panel usage + added pannel-footer --- .../src/repos/changesets/ChangesetList.js | 21 +++++++++++++++---- scm-ui/src/repos/containers/Changesets.js | 2 +- .../src/repos/sources/containers/Content.js | 6 ++---- .../repos/sources/containers/HistoryView.js | 12 ++++++----- .../repos/sources/containers/SourcesView.js | 17 +++++++++++---- scm-ui/styles/scm.scss | 18 ++++++++++++++++ 6 files changed, 58 insertions(+), 18 deletions(-) diff --git a/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetList.js b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetList.js index 74ec816369..c50c8a2d50 100644 --- a/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetList.js +++ b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetList.js @@ -1,17 +1,26 @@ // @flow import ChangesetRow from "./ChangesetRow"; import React from "react"; +import injectSheet from "react-jss"; +import classNames from "classnames"; import type { Changeset, Repository } from "@scm-manager/ui-types"; type Props = { repository: Repository, - changesets: Changeset[] + changesets: Changeset[], + classes: any +}; + +const styles = { + toCenterContent: { + display: "block" + } }; class ChangesetList extends React.Component<Props> { render() { - const { repository, changesets } = this.props; + const { repository, changesets, classes } = this.props; const content = changesets.map(changeset => { return ( <ChangesetRow @@ -21,8 +30,12 @@ class ChangesetList extends React.Component<Props> { /> ); }); - return <div className="box">{content}</div>; + return ( + <div className={classNames("panel-block", classes.toCenterContent)}> + {content} + </div> + ); } } -export default ChangesetList; +export default injectSheet(styles)(ChangesetList); diff --git a/scm-ui/src/repos/containers/Changesets.js b/scm-ui/src/repos/containers/Changesets.js index 95bf0459a6..69dc41da2d 100644 --- a/scm-ui/src/repos/containers/Changesets.js +++ b/scm-ui/src/repos/containers/Changesets.js @@ -70,7 +70,7 @@ class Changesets extends React.Component<Props> { renderPaginator = () => { const { page, list } = this.props; if (list) { - return <LinkPaginator page={page} collection={list} />; + return <div className="panel-footer"><LinkPaginator page={page} collection={list} /></div>; } return null; }; diff --git a/scm-ui/src/repos/sources/containers/Content.js b/scm-ui/src/repos/sources/containers/Content.js index 6339c49a3d..cc4aa32b5b 100644 --- a/scm-ui/src/repos/sources/containers/Content.js +++ b/scm-ui/src/repos/sources/containers/Content.js @@ -161,7 +161,7 @@ class Content extends React.Component<Props, State> { } render() { - const { file, revision, repository, path, classes } = this.props; + const { file, revision, repository, path } = this.props; const { showHistory } = this.state; const header = this.showHeader(); @@ -183,9 +183,7 @@ class Content extends React.Component<Props, State> { <nav className="panel"> <article className="panel-heading">{header}</article> {moreInformation} - <div className={classNames("panel-block", classes.toCenterContent)}> - {content} - </div> + {content} </nav> </div> ); diff --git a/scm-ui/src/repos/sources/containers/HistoryView.js b/scm-ui/src/repos/sources/containers/HistoryView.js index 98400248d9..c118ef9cea 100644 --- a/scm-ui/src/repos/sources/containers/HistoryView.js +++ b/scm-ui/src/repos/sources/containers/HistoryView.js @@ -80,11 +80,13 @@ class HistoryView extends React.Component<Props, State> { return ( <> <ChangesetList repository={repository} changesets={changesets} /> - <StatePaginator - page={currentPage} - collection={pageCollection} - updatePage={(newPage: number) => this.updatePage(newPage)} - /> + <div className="panel-footer"> + <StatePaginator + page={currentPage} + collection={pageCollection} + updatePage={(newPage: number) => this.updatePage(newPage)} + /> + </div> </> ); } diff --git a/scm-ui/src/repos/sources/containers/SourcesView.js b/scm-ui/src/repos/sources/containers/SourcesView.js index 1e76c6d6bf..220c6ecc27 100644 --- a/scm-ui/src/repos/sources/containers/SourcesView.js +++ b/scm-ui/src/repos/sources/containers/SourcesView.js @@ -8,12 +8,15 @@ import { ExtensionPoint } from "@scm-manager/ui-extensions"; import { getContentType } from "./contentType"; import type { File, Repository } from "@scm-manager/ui-types"; import { ErrorNotification, Loading } from "@scm-manager/ui-components"; +import injectSheet from "react-jss"; +import classNames from "classnames"; type Props = { repository: Repository, file: File, revision: string, - path: string + path: string, + classes: any }; type State = { @@ -23,6 +26,12 @@ type State = { error?: Error }; +const styles = { + toCenterContent: { + display: "block" + } +}; + class SourcesView extends React.Component<Props, State> { constructor(props: Props) { super(props); @@ -78,7 +87,7 @@ class SourcesView extends React.Component<Props, State> { } render() { - const { file } = this.props; + const { file, classes } = this.props; const { loaded, error } = this.state; if (!file || !loaded) { @@ -90,8 +99,8 @@ class SourcesView extends React.Component<Props, State> { const sources = this.showSources(); - return <>{sources}</>; + return <div className={classNames("panel-block", classes.toCenterContent)}>{sources}</div>; } } -export default SourcesView; +export default injectSheet(styles)(SourcesView); diff --git a/scm-ui/styles/scm.scss b/scm-ui/styles/scm.scss index 620410bd48..59f1ca07a4 100644 --- a/scm-ui/styles/scm.scss +++ b/scm-ui/styles/scm.scss @@ -194,6 +194,24 @@ $fa-font-path: "webfonts"; } } +//panels +.panel-footer { + background-color: whitesmoke; + border-radius: 0 0 4px 4px; + color: #363636; + font-size: 1.25em; + font-weight: 300; + line-height: 1.25; + padding: 0.5em 0.75em; + + border-left: 1px solid #dbdbdb; + border-right: 1px solid #dbdbdb; + + &:last-child { + border-bottom: 1px solid #dbdbdb; + } +} + // forms .field:not(.is-grouped) { margin-bottom: 1rem; From 6edc3d12c7c89a7b99c7169ecfd75183769288f3 Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Tue, 29 Jan 2019 17:45:47 +0100 Subject: [PATCH 037/120] =?UTF-8?q?replaced=20self-hacked=E2=84=A2=20solut?= =?UTF-8?q?ion=20with=20bulma=20panels=20for=20changesets?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui-components/src/BranchSelector.js | 31 ++++++++++++---- .../packages/ui-components/src/TableHeader.js | 35 ------------------- .../packages/ui-components/src/index.js | 1 - scm-ui/src/repos/containers/ChangesetsRoot.js | 6 ++-- 4 files changed, 28 insertions(+), 45 deletions(-) delete mode 100644 scm-ui-components/packages/ui-components/src/TableHeader.js diff --git a/scm-ui-components/packages/ui-components/src/BranchSelector.js b/scm-ui-components/packages/ui-components/src/BranchSelector.js index f44de639ba..c473527472 100644 --- a/scm-ui-components/packages/ui-components/src/BranchSelector.js +++ b/scm-ui-components/packages/ui-components/src/BranchSelector.js @@ -1,8 +1,7 @@ // @flow import React from "react"; -import type {Branch} from "@scm-manager/ui-types"; -import TableHeader from "./TableHeader"; +import type { Branch } from "@scm-manager/ui-types"; import injectSheet from "react-jss"; import classNames from "classnames"; import DropDown from "./forms/DropDown"; @@ -13,6 +12,12 @@ const styles = { }, minWidthOfLabel: { minWidth: "4.5rem" + }, + labelSizing: { + fontSize: "1rem !important" + }, + noBottomMargin: { + marginBottom: "0 !important" } }; @@ -35,7 +40,9 @@ class BranchSelector extends React.Component<Props, State> { } componentDidMount() { - const selectedBranch = this.props.branches.find(branch => branch.name === this.props.selectedBranch); + const selectedBranch = this.props.branches.find( + branch => branch.name === this.props.selectedBranch + ); this.setState({ selectedBranch }); } @@ -44,7 +51,13 @@ class BranchSelector extends React.Component<Props, State> { if (branches) { return ( - <TableHeader> + <div + className={classNames( + "field", + "is-horizontal", + classes.noBottomMargin + )} + > <div className={classNames( "field-label", @@ -53,10 +66,14 @@ class BranchSelector extends React.Component<Props, State> { classes.minWidthOfLabel )} > - <label className="label">{label}</label> + <label className={classNames("label", classes.labelSizing)}> + {label} + </label> </div> <div className="field-body"> - <div className="field is-narrow"> + <div + className={classNames("field is-narrow", classes.noBottomMargin)} + > <div className="control"> <DropDown className="is-fullwidth" @@ -71,7 +88,7 @@ class BranchSelector extends React.Component<Props, State> { </div> </div> </div> - </TableHeader> + </div> ); } else { return null; diff --git a/scm-ui-components/packages/ui-components/src/TableHeader.js b/scm-ui-components/packages/ui-components/src/TableHeader.js deleted file mode 100644 index f7463d3136..0000000000 --- a/scm-ui-components/packages/ui-components/src/TableHeader.js +++ /dev/null @@ -1,35 +0,0 @@ -//@flow -import * as React from "react"; -import classNames from "classnames"; -import injectSheet from "react-jss"; - -type Props = { - children?: React.Node, - classes: Object -}; - -const styles = { - wrapper: { - padding: "1rem 1.5rem 0.25rem 1.5rem", - borderBottom: "1px solid #dbdbdb" - } -}; - -class TableHeader extends React.Component<Props> { - render() { - const { classes, children } = this.props; - return ( - <div - className={classNames( - "has-background-light field", - "is-horizontal", - classes.wrapper - )} - > - {children} - </div> - ); - } -} - -export default injectSheet(styles)(TableHeader); diff --git a/scm-ui-components/packages/ui-components/src/index.js b/scm-ui-components/packages/ui-components/src/index.js index d510e28ade..5b3cdb4c95 100644 --- a/scm-ui-components/packages/ui-components/src/index.js +++ b/scm-ui-components/packages/ui-components/src/index.js @@ -25,7 +25,6 @@ export { default as Tooltip } from "./Tooltip"; export { getPageFromMatch } from "./urls"; export { default as Autocomplete} from "./Autocomplete"; export { default as BranchSelector } from "./BranchSelector"; -export { default as TableHeader } from "./TableHeader"; export { apiClient, NOT_FOUND_ERROR, UNAUTHORIZED_ERROR, CONFLICT_ERROR } from "./apiclient.js"; diff --git a/scm-ui/src/repos/containers/ChangesetsRoot.js b/scm-ui/src/repos/containers/ChangesetsRoot.js index c65c8f32cb..d3d4635e18 100644 --- a/scm-ui/src/repos/containers/ChangesetsRoot.js +++ b/scm-ui/src/repos/containers/ChangesetsRoot.js @@ -89,10 +89,12 @@ class BranchRoot extends React.Component<Props> { const changesets = <Changesets repository={repository} branch={branch} />; return ( - <div className="has-border-around is-round"> + <nav className="panel"> + <article className="panel-heading"> {this.renderBranchSelector()} + </article> <Route path={`${url}/:page?`} component={() => changesets} /> - </div> + </nav> ); } From e99752ed2e152f5adec7c7a37628d7404e4a8050 Mon Sep 17 00:00:00 2001 From: Mohamed Karray <mohamed.karray.sr@gmail.com> Date: Tue, 29 Jan 2019 17:46:36 +0100 Subject: [PATCH 038/120] move the ChangesetDto to core because the activity plugin need it. --- .../src/main/java/sonia/scm/api/v2/resources/ChangesetDto.java | 0 .../src/main/java/sonia/scm/api/v2/resources/PersonDto.java | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename {scm-webapp => scm-core}/src/main/java/sonia/scm/api/v2/resources/ChangesetDto.java (100%) rename {scm-webapp => scm-core}/src/main/java/sonia/scm/api/v2/resources/PersonDto.java (100%) diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetDto.java b/scm-core/src/main/java/sonia/scm/api/v2/resources/ChangesetDto.java similarity index 100% rename from scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetDto.java rename to scm-core/src/main/java/sonia/scm/api/v2/resources/ChangesetDto.java diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PersonDto.java b/scm-core/src/main/java/sonia/scm/api/v2/resources/PersonDto.java similarity index 100% rename from scm-webapp/src/main/java/sonia/scm/api/v2/resources/PersonDto.java rename to scm-core/src/main/java/sonia/scm/api/v2/resources/PersonDto.java From dfd187a2474c0f13be8353f8bafbb6e91c19b861 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra <sebastian.sdorra@cloudogu.com> Date: Wed, 30 Jan 2019 11:25:01 +0100 Subject: [PATCH 039/120] integrate sonatype lifecycle analysis into jenkins build --- Jenkinsfile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Jenkinsfile b/Jenkinsfile index 8bb52d6030..e817ac2ba1 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -50,6 +50,11 @@ node('docker') { def dockerImageTag = "2.0.0-dev-${commitHash.substring(0,7)}-${BUILD_NUMBER}" if (isMainBranch()) { + + stage('Lifecycle') { + nexusPolicyEvaluation iqApplication: selectedApplication('scm'), iqScanPatterns: [[scanPattern: 'scm-server/target/scm-server-app.zip']], iqStage: 'build' + } + stage('Archive') { archiveArtifacts 'scm-webapp/target/scm-webapp.war' archiveArtifacts 'scm-server/target/scm-server-app.*' From 69dda6403d3d25ed90e8467990b81599ab03bcee Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra <sebastian.sdorra@cloudogu.com> Date: Wed, 30 Jan 2019 13:21:11 +0100 Subject: [PATCH 040/120] update resteasy to v3.6.2.Final in order to fix CVE-2017-7561 and CVE-2016-6347 --- pom.xml | 4 +-- scm-core/pom.xml | 2 +- .../v2/resources/GitConfigResourceTest.java | 15 +++++----- .../v2/resources/HgConfigResourceTest.java | 5 ++-- .../v2/resources/SvnConfigResourceTest.java | 6 ++-- scm-webapp/pom.xml | 14 ++++----- .../api/v2/resources/ConfigResourceTest.java | 3 +- .../v2/resources/GroupRootResourceTest.java | 9 +++--- .../scm/api/v2/resources/MeResourceTest.java | 3 +- .../RepositoryPermissionRootResourceTest.java | 29 ++++++++++++++----- .../resources/RepositoryRootResourceTest.java | 7 +++-- .../api/v2/resources/UIRootResourceTest.java | 7 +++-- .../v2/resources/UserRootResourceTest.java | 13 +++++---- 13 files changed, 70 insertions(+), 47 deletions(-) diff --git a/pom.xml b/pom.xml index 2d8bc13f28..cae4aa9bb6 100644 --- a/pom.xml +++ b/pom.xml @@ -825,8 +825,8 @@ <logback.version>1.2.3</logback.version> <servlet.version>3.0.1</servlet.version> - <jaxrs.version>2.0.1</jaxrs.version> - <resteasy.version>3.1.3.Final</resteasy.version> + <jaxrs.version>2.1.1</jaxrs.version> + <resteasy.version>3.6.2.Final</resteasy.version> <jersey-client.version>1.19.4</jersey-client.version> <enunciate.version>2.11.1</enunciate.version> <jackson.version>2.8.6</jackson.version> diff --git a/scm-core/pom.xml b/scm-core/pom.xml index c4bf3a2e6f..f19d50064d 100644 --- a/scm-core/pom.xml +++ b/scm-core/pom.xml @@ -93,6 +93,7 @@ <dependency> <groupId>javax.ws.rs</groupId> <artifactId>javax.ws.rs-api</artifactId> + <scope>provided</scope> </dependency> <dependency> @@ -235,7 +236,6 @@ <links> <link>http://download.oracle.com/javase/6/docs/api/</link> <link>http://download.oracle.com/docs/cd/E17802_01/products/products/servlet/2.5/docs/servlet-2_5-mr2/</link> - <link>http://jersey.java.net/nonav/apidocs/${jersey.version}/jersey/</link> <link>https://google.github.io/guice/api-docs/${guice.version}/javadoc</link> <link>http://www.slf4j.org/api/</link> <link>http://shiro.apache.org/static/${shiro.version}/apidocs/</link> diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigResourceTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigResourceTest.java index 0c28a28e59..8e657b1050 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigResourceTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigResourceTest.java @@ -17,7 +17,7 @@ import org.mockito.Captor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import sonia.scm.repository.GitConfig; import sonia.scm.repository.GitRepositoryConfig; import sonia.scm.repository.GitRepositoryHandler; @@ -29,6 +29,7 @@ import sonia.scm.store.ConfigurationStoreFactory; import sonia.scm.web.GitVndMediaType; import javax.servlet.http.HttpServletResponse; +import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; @@ -100,7 +101,7 @@ public class GitConfigResourceTest { @Test @SubjectAware(username = "readWrite") - public void shouldGetGitConfig() throws URISyntaxException { + public void shouldGetGitConfig() throws URISyntaxException, UnsupportedEncodingException { MockHttpResponse response = get(); assertEquals(HttpServletResponse.SC_OK, response.getStatus()); @@ -115,7 +116,7 @@ public class GitConfigResourceTest { @Test @SubjectAware(username = "readWrite") - public void shouldGetGitConfigEvenWhenItsEmpty() throws URISyntaxException { + public void shouldGetGitConfigEvenWhenItsEmpty() throws URISyntaxException, UnsupportedEncodingException { when(repositoryHandler.getConfig()).thenReturn(null); MockHttpResponse response = get(); @@ -126,7 +127,7 @@ public class GitConfigResourceTest { @Test @SubjectAware(username = "readOnly") - public void shouldGetGitConfigWithoutUpdateLink() throws URISyntaxException { + public void shouldGetGitConfigWithoutUpdateLink() throws URISyntaxException, UnsupportedEncodingException { MockHttpResponse response = get(); assertEquals(HttpServletResponse.SC_OK, response.getStatus()); @@ -159,7 +160,7 @@ public class GitConfigResourceTest { @Test @SubjectAware(username = "writeOnly") - public void shouldReadDefaultRepositoryConfig() throws URISyntaxException { + public void shouldReadDefaultRepositoryConfig() throws URISyntaxException, UnsupportedEncodingException { when(repositoryManager.get(new NamespaceAndName("space", "X"))).thenReturn(new Repository("id", "git", "space", "X")); MockHttpRequest request = MockHttpRequest.get("/" + GitConfigResource.GIT_CONFIG_PATH_V2 + "/space/X"); @@ -176,7 +177,7 @@ public class GitConfigResourceTest { @Test @SubjectAware(username = "readOnly") - public void shouldNotHaveUpdateLinkForReadOnlyUser() throws URISyntaxException { + public void shouldNotHaveUpdateLinkForReadOnlyUser() throws URISyntaxException, UnsupportedEncodingException { when(repositoryManager.get(new NamespaceAndName("space", "X"))).thenReturn(new Repository("id", "git", "space", "X")); MockHttpRequest request = MockHttpRequest.get("/" + GitConfigResource.GIT_CONFIG_PATH_V2 + "/space/X"); @@ -193,7 +194,7 @@ public class GitConfigResourceTest { @Test @SubjectAware(username = "writeOnly") - public void shouldReadStoredRepositoryConfig() throws URISyntaxException { + public void shouldReadStoredRepositoryConfig() throws URISyntaxException, UnsupportedEncodingException { when(repositoryManager.get(new NamespaceAndName("space", "X"))).thenReturn(new Repository("id", "git", "space", "X")); GitRepositoryConfig gitRepositoryConfig = new GitRepositoryConfig(); gitRepositoryConfig.setDefaultBranch("test"); diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigResourceTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigResourceTest.java index df59954971..e9b96fd71b 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigResourceTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigResourceTest.java @@ -25,6 +25,7 @@ import javax.inject.Provider; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.IOException; +import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; @@ -99,7 +100,7 @@ public class HgConfigResourceTest { @Test @SubjectAware(username = "readWrite") - public void shouldGetHgConfigEvenWhenItsEmpty() throws URISyntaxException { + public void shouldGetHgConfigEvenWhenItsEmpty() throws URISyntaxException, UnsupportedEncodingException { when(repositoryHandler.getConfig()).thenReturn(null); MockHttpResponse response = get(); @@ -110,7 +111,7 @@ public class HgConfigResourceTest { @Test @SubjectAware(username = "readOnly") - public void shouldGetHgConfigWithoutUpdateLink() throws URISyntaxException { + public void shouldGetHgConfigWithoutUpdateLink() throws URISyntaxException, UnsupportedEncodingException { MockHttpResponse response = get(); assertEquals(HttpServletResponse.SC_OK, response.getStatus()); diff --git a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/api/v2/resources/SvnConfigResourceTest.java b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/api/v2/resources/SvnConfigResourceTest.java index 3077bb34f3..f7ccf039b2 100644 --- a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/api/v2/resources/SvnConfigResourceTest.java +++ b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/api/v2/resources/SvnConfigResourceTest.java @@ -16,14 +16,14 @@ import org.junit.runner.RunWith; import org.mockito.Answers; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import sonia.scm.repository.SvnConfig; import sonia.scm.repository.SvnRepositoryHandler; import sonia.scm.web.SvnVndMediaType; import javax.servlet.http.HttpServletResponse; -import java.io.File; import java.io.IOException; +import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; @@ -98,7 +98,7 @@ public class SvnConfigResourceTest { @Test @SubjectAware(username = "readOnly") - public void shouldGetSvnConfigWithoutUpdateLink() throws URISyntaxException { + public void shouldGetSvnConfigWithoutUpdateLink() throws URISyntaxException, UnsupportedEncodingException { MockHttpResponse response = get(); assertEquals(HttpServletResponse.SC_OK, response.getStatus()); diff --git a/scm-webapp/pom.xml b/scm-webapp/pom.xml index c83cea36d5..93db9005b2 100644 --- a/scm-webapp/pom.xml +++ b/scm-webapp/pom.xml @@ -114,12 +114,6 @@ <version>${jackson.version}</version> </dependency> - <dependency> - <groupId>javax</groupId> - <artifactId>javaee-api</artifactId> - <version>7.0</version> - </dependency> - <!-- rest api --> <dependency> @@ -158,6 +152,13 @@ <version>${resteasy.version}</version> </dependency> + <dependency> + <groupId>javax.el</groupId> + <artifactId>javax.el-api</artifactId> + <version>3.0.1-b06</version> + <scope>provided</scope> + </dependency> + <!-- injection --> <dependency> @@ -561,7 +562,6 @@ <selenium.version>2.53.1</selenium.version> <wagon.version>1.0</wagon.version> <mustache.version>0.8.17</mustache.version> - <resteasy.version>3.1.4.Final</resteasy.version> <jackson.version>2.8.9</jackson.version> <netbeans.hint.deploy.server>Tomcat</netbeans.hint.deploy.server> <sonar.issue.ignore.multicriteria>e1</sonar.issue.ignore.multicriteria> diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ConfigResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ConfigResourceTest.java index 033824cbea..c058e06086 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ConfigResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ConfigResourceTest.java @@ -18,6 +18,7 @@ import sonia.scm.web.VndMediaType; import javax.servlet.http.HttpServletResponse; import java.io.IOException; +import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; @@ -68,7 +69,7 @@ public class ConfigResourceTest { @Test @SubjectAware(username = "readOnly") - public void shouldGetGlobalConfig() throws URISyntaxException { + public void shouldGetGlobalConfig() throws URISyntaxException, UnsupportedEncodingException { MockHttpRequest request = MockHttpRequest.get("/" + ConfigResource.CONFIG_PATH_V2); MockHttpResponse response = new MockHttpResponse(); dispatcher.invoke(request, response); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/GroupRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/GroupRootResourceTest.java index 646e9d0839..3e2d0f9663 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/GroupRootResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/GroupRootResourceTest.java @@ -24,6 +24,7 @@ import sonia.scm.web.VndMediaType; import javax.servlet.http.HttpServletResponse; import java.io.IOException; +import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; @@ -100,7 +101,7 @@ public class GroupRootResourceTest { } @Test - public void shouldGetGroup() throws URISyntaxException { + public void shouldGetGroup() throws URISyntaxException, UnsupportedEncodingException { Group group = createDummyGroup(); when(groupManager.get("admin")).thenReturn(group); @@ -305,7 +306,7 @@ public class GroupRootResourceTest { } @Test - public void shouldGetAll() throws URISyntaxException { + public void shouldGetAll() throws URISyntaxException, UnsupportedEncodingException { MockHttpRequest request = MockHttpRequest.get("/" + GroupRootResource.GROUPS_PATH_V2); MockHttpResponse response = new MockHttpResponse(); @@ -317,7 +318,7 @@ public class GroupRootResourceTest { } @Test - public void shouldGetPermissionLink() throws URISyntaxException { + public void shouldGetPermissionLink() throws URISyntaxException, UnsupportedEncodingException { MockHttpRequest request = MockHttpRequest.get("/" + GroupRootResource.GROUPS_PATH_V2 + "admin"); MockHttpResponse response = new MockHttpResponse(); @@ -329,7 +330,7 @@ public class GroupRootResourceTest { } @Test - public void shouldGetPermissions() throws URISyntaxException { + public void shouldGetPermissions() throws URISyntaxException, UnsupportedEncodingException { when(permissionAssigner.readPermissionsForGroup("admin")).thenReturn(singletonList(new PermissionDescriptor("something:*"))); MockHttpRequest request = MockHttpRequest.get("/" + GroupRootResource.GROUPS_PATH_V2 + "admin/permissions"); MockHttpResponse response = new MockHttpResponse(); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MeResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MeResourceTest.java index 052a059959..d83cea50a0 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MeResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MeResourceTest.java @@ -22,6 +22,7 @@ import sonia.scm.user.UserManager; import sonia.scm.web.VndMediaType; import javax.servlet.http.HttpServletResponse; +import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; @@ -78,7 +79,7 @@ public class MeResourceTest { } @Test - public void shouldReturnCurrentlyAuthenticatedUser() throws URISyntaxException { + public void shouldReturnCurrentlyAuthenticatedUser() throws URISyntaxException, UnsupportedEncodingException { applyUserToSubject(originalUser); MockHttpRequest request = MockHttpRequest.get("/" + MeResource.ME_PATH_V2); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryPermissionRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryPermissionRootResourceTest.java index 2795562b14..8dd451c556 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryPermissionRootResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryPermissionRootResourceTest.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.github.sdorra.shiro.ShiroRule; import com.github.sdorra.shiro.SubjectAware; +import com.google.common.base.Throwables; import com.google.common.collect.ImmutableList; import com.google.inject.util.Providers; import de.otto.edison.hal.HalRepresentation; @@ -36,6 +37,7 @@ import sonia.scm.repository.RepositoryPermission; import sonia.scm.web.VndMediaType; import java.io.IOException; +import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; @@ -214,7 +216,12 @@ public class RepositoryPermissionRootResourceTest extends RepositoryTestBase { .expectedResponseStatus(200) .path(PATH_OF_ALL_PERMISSIONS + expectedPermission.getName()) .responseValidator((response) -> { - String body = response.getContentAsString(); + String body = null; + try { + body = response.getContentAsString(); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } ObjectMapper mapper = new ObjectMapper(); try { RepositoryPermissionDto actualRepositoryPermissionDto = mapper.readValue(body, RepositoryPermissionDto.class); @@ -268,13 +275,21 @@ public class RepositoryPermissionRootResourceTest extends RepositoryTestBase { assertExpectedRequest(requestPOSTPermission .content("{\"name\" : \"" + newPermission.getName() + "\" , \"verbs\" : [\"read\",\"pull\",\"push\"], \"groupPermission\" : true}") .expectedResponseStatus(201) - .responseValidator(response -> assertThat(response.getContentAsString()) + .responseValidator(response -> assertThat(getContentAsString(response)) .as("POST response has no body") .isBlank()) ); assertGettingExpectedPermissions(expectedPermissions, PERMISSION_WRITE); } + private String getContentAsString(MockHttpResponse response) { + try { + return response.getContentAsString(); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("could not get content from response", e); + } + } + @Test public void shouldNotAddExistingPermission() throws URISyntaxException { createUserWithRepositoryAndPermissions(TEST_PERMISSIONS, PERMISSION_WRITE); @@ -296,7 +311,7 @@ public class RepositoryPermissionRootResourceTest extends RepositoryTestBase { .content("{\"name\" : \"" + modifiedPermission.getName() + "\" , \"verbs\" : [\"*\"], \"groupPermission\" : false}") .path(PATH_OF_ALL_PERMISSIONS + modifiedPermission.getName()) .expectedResponseStatus(204) - .responseValidator(response -> assertThat(response.getContentAsString()) + .responseValidator(response -> assertThat(getContentAsString(response)) .as("PUT response has no body") .isBlank()) ); @@ -312,7 +327,7 @@ public class RepositoryPermissionRootResourceTest extends RepositoryTestBase { assertExpectedRequest(requestDELETEPermission .path(PATH_OF_ALL_PERMISSIONS + deletedPermission.getName()) .expectedResponseStatus(204) - .responseValidator(response -> assertThat(response.getContentAsString()) + .responseValidator(response -> assertThat(getContentAsString(response)) .as("DELETE response has no body") .isBlank()) ); @@ -327,7 +342,7 @@ public class RepositoryPermissionRootResourceTest extends RepositoryTestBase { assertExpectedRequest(requestDELETEPermission .path(PATH_OF_ALL_PERMISSIONS + deletedPermission.getName()) .expectedResponseStatus(204) - .responseValidator(response -> assertThat(response.getContentAsString()) + .responseValidator(response -> assertThat(getContentAsString(response)) .as("DELETE response has no body") .isBlank()) ); @@ -335,7 +350,7 @@ public class RepositoryPermissionRootResourceTest extends RepositoryTestBase { assertExpectedRequest(requestDELETEPermission .path(PATH_OF_ALL_PERMISSIONS + deletedPermission.getName()) .expectedResponseStatus(204) - .responseValidator(response -> assertThat(response.getContentAsString()) + .responseValidator(response -> assertThat(getContentAsString(response)) .as("DELETE response has no body") .isBlank()) ); @@ -346,7 +361,7 @@ public class RepositoryPermissionRootResourceTest extends RepositoryTestBase { assertExpectedRequest(requestGETAllPermissions .expectedResponseStatus(200) .responseValidator((response) -> { - String body = response.getContentAsString(); + String body = getContentAsString(response); ObjectMapper mapper = new ObjectMapper(); try { HalRepresentation halRepresentation = mapper.readValue(body, HalRepresentation.class); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryRootResourceTest.java index bf4366f0b2..032b79d2b7 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryRootResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryRootResourceTest.java @@ -27,6 +27,7 @@ import sonia.scm.web.VndMediaType; import javax.servlet.http.HttpServletResponse; import java.io.IOException; +import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; @@ -120,7 +121,7 @@ public class RepositoryRootResourceTest extends RepositoryTestBase { } @Test - public void shouldFindExistingRepository() throws URISyntaxException { + public void shouldFindExistingRepository() throws URISyntaxException, UnsupportedEncodingException { mockRepository("space", "repo"); MockHttpRequest request = MockHttpRequest.get("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo"); @@ -133,7 +134,7 @@ public class RepositoryRootResourceTest extends RepositoryTestBase { } @Test - public void shouldMapProperties() throws URISyntaxException { + public void shouldMapProperties() throws URISyntaxException, UnsupportedEncodingException { Repository repository = mockRepository("space", "repo"); repository.setProperty("testKey", "testValue"); @@ -146,7 +147,7 @@ public class RepositoryRootResourceTest extends RepositoryTestBase { } @Test - public void shouldGetAll() throws URISyntaxException { + public void shouldGetAll() throws URISyntaxException, UnsupportedEncodingException { PageResult<Repository> singletonPageResult = createSingletonPageResult(mockRepository("space", "repo")); when(repositoryManager.getPage(any(), eq(0), eq(10))).thenReturn(singletonPageResult); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UIRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UIRootResourceTest.java index 99a1435923..b2dafc8cfe 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UIRootResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UIRootResourceTest.java @@ -16,6 +16,7 @@ import sonia.scm.plugin.*; import sonia.scm.web.VndMediaType; import javax.servlet.http.HttpServletRequest; +import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; import java.util.HashSet; @@ -87,7 +88,7 @@ public class UIRootResourceTest { } @Test - public void shouldReturnPlugin() throws URISyntaxException { + public void shouldReturnPlugin() throws URISyntaxException, UnsupportedEncodingException { mockPlugins(mockPlugin("awesome", "Awesome", createPluginResources("my/awesome.bundle.js"))); MockHttpRequest request = MockHttpRequest.get("/v2/ui/plugins/awesome"); @@ -101,7 +102,7 @@ public class UIRootResourceTest { } @Test - public void shouldReturnPlugins() throws URISyntaxException { + public void shouldReturnPlugins() throws URISyntaxException, UnsupportedEncodingException { mockPlugins( mockPlugin("awesome", "Awesome", createPluginResources("my/awesome.bundle.js")), mockPlugin("special", "Special", createPluginResources("my/special.bundle.js")) @@ -120,7 +121,7 @@ public class UIRootResourceTest { } @Test - public void shouldNotReturnPluginsWithoutResources() throws URISyntaxException { + public void shouldNotReturnPluginsWithoutResources() throws URISyntaxException, UnsupportedEncodingException { mockPlugins( mockPlugin("awesome", "Awesome", createPluginResources("my/awesome.bundle.js")), mockPlugin("special") diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserRootResourceTest.java index 88142e4d50..1da540106a 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserRootResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserRootResourceTest.java @@ -26,6 +26,7 @@ import sonia.scm.user.UserManager; import sonia.scm.web.VndMediaType; import javax.servlet.http.HttpServletResponse; +import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; @@ -97,7 +98,7 @@ public class UserRootResourceTest { } @Test - public void shouldCreateFullResponseForAdmin() throws URISyntaxException { + public void shouldCreateFullResponseForAdmin() throws URISyntaxException, UnsupportedEncodingException { MockHttpRequest request = MockHttpRequest.get("/" + UserRootResource.USERS_PATH_V2 + "Neo"); MockHttpResponse response = new MockHttpResponse(); @@ -137,7 +138,7 @@ public class UserRootResourceTest { @Test @SubjectAware(username = "unpriv") - public void shouldCreateLimitedResponseForSimpleUser() throws URISyntaxException { + public void shouldCreateLimitedResponseForSimpleUser() throws URISyntaxException, UnsupportedEncodingException { MockHttpRequest request = MockHttpRequest.get("/" + UserRootResource.USERS_PATH_V2 + "Neo"); MockHttpResponse response = new MockHttpResponse(); @@ -331,7 +332,7 @@ public class UserRootResourceTest { } @Test - public void shouldCreatePageForOnePageOnly() throws URISyntaxException { + public void shouldCreatePageForOnePageOnly() throws URISyntaxException, UnsupportedEncodingException { PageResult<User> singletonPageResult = createSingletonPageResult(1); when(userManager.getPage(any(), eq(0), eq(10))).thenReturn(singletonPageResult); MockHttpRequest request = MockHttpRequest.get("/" + UserRootResource.USERS_PATH_V2); @@ -347,7 +348,7 @@ public class UserRootResourceTest { } @Test - public void shouldCreatePageForMultiplePages() throws URISyntaxException { + public void shouldCreatePageForMultiplePages() throws URISyntaxException, UnsupportedEncodingException { PageResult<User> singletonPageResult = createSingletonPageResult(3); when(userManager.getPage(any(), eq(1), eq(1))).thenReturn(singletonPageResult); MockHttpRequest request = MockHttpRequest.get("/" + UserRootResource.USERS_PATH_V2 + "?page=1&pageSize=1"); @@ -365,7 +366,7 @@ public class UserRootResourceTest { } @Test - public void shouldGetPermissionLink() throws URISyntaxException { + public void shouldGetPermissionLink() throws URISyntaxException, UnsupportedEncodingException { MockHttpRequest request = MockHttpRequest.get("/" + UserRootResource.USERS_PATH_V2 + "Neo"); MockHttpResponse response = new MockHttpResponse(); @@ -377,7 +378,7 @@ public class UserRootResourceTest { } @Test - public void shouldGetPermissions() throws URISyntaxException { + public void shouldGetPermissions() throws URISyntaxException, UnsupportedEncodingException { when(permissionAssigner.readPermissionsForUser("Neo")).thenReturn(singletonList(new PermissionDescriptor("something:*"))); MockHttpRequest request = MockHttpRequest.get("/" + UserRootResource.USERS_PATH_V2 + "Neo/permissions"); MockHttpResponse response = new MockHttpResponse(); From e5e4ae9ac99071eb2e1519f82d906da80b27659d Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra <sebastian.sdorra@cloudogu.com> Date: Wed, 30 Jan 2019 13:43:29 +0100 Subject: [PATCH 041/120] fix gzip filter on resteasy 3.6.2 --- .../sonia/scm/filter/GZipResponseFilter.java | 52 +++++++++--- .../scm/filter/GZipResponseFilterTest.java | 81 +++++++++++++++++++ 2 files changed, 120 insertions(+), 13 deletions(-) create mode 100644 scm-core/src/test/java/sonia/scm/filter/GZipResponseFilterTest.java diff --git a/scm-core/src/main/java/sonia/scm/filter/GZipResponseFilter.java b/scm-core/src/main/java/sonia/scm/filter/GZipResponseFilter.java index 1fa525d4fd..92dd033af0 100644 --- a/scm-core/src/main/java/sonia/scm/filter/GZipResponseFilter.java +++ b/scm-core/src/main/java/sonia/scm/filter/GZipResponseFilter.java @@ -1,24 +1,50 @@ package sonia.scm.filter; -import lombok.extern.slf4j.Slf4j; -import sonia.scm.util.WebUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import javax.ws.rs.container.ContainerRequestContext; -import javax.ws.rs.container.ContainerResponseContext; -import javax.ws.rs.container.ContainerResponseFilter; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.ext.Provider; +import javax.ws.rs.ext.WriterInterceptor; +import javax.ws.rs.ext.WriterInterceptorContext; import java.io.IOException; +import java.io.OutputStream; +import java.util.Locale; import java.util.zip.GZIPOutputStream; @Provider -@Slf4j -public class GZipResponseFilter implements ContainerResponseFilter { - public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException { - if (WebUtil.isGzipSupported(requestContext::getHeaderString)) { - log.trace("compress output with gzip"); - GZIPOutputStream wrappedResponse = new GZIPOutputStream(responseContext.getEntityStream()); - responseContext.getHeaders().add("Content-Encoding", "gzip"); - responseContext.setEntityStream(wrappedResponse); +public class GZipResponseFilter implements WriterInterceptor { + + private static final Logger LOG = LoggerFactory.getLogger(GZipResponseFilter.class); + + @Override + public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException { + if (isGZipSupported(context)) { + LOG.trace("compress output with gzip"); + encodeWithGZip(context); + } else { + context.proceed(); } } + + private void encodeWithGZip(WriterInterceptorContext context) throws IOException { + context.getHeaders().remove(HttpHeaders.CONTENT_LENGTH); + context.getHeaders().add(HttpHeaders.CONTENT_ENCODING, "gzip"); + + OutputStream outputStream = context.getOutputStream(); + GZIPOutputStream compressedOutputStream = new GZIPOutputStream(outputStream); + context.setOutputStream(compressedOutputStream); + try { + context.proceed(); + } finally { + compressedOutputStream.finish(); + context.setOutputStream(outputStream); + } + } + + private boolean isGZipSupported(WriterInterceptorContext context) { + Object encoding = context.getHeaders().getFirst(HttpHeaders.ACCEPT_ENCODING); + return encoding != null && encoding.toString().toLowerCase(Locale.ENGLISH).contains("gzip"); + } } diff --git a/scm-core/src/test/java/sonia/scm/filter/GZipResponseFilterTest.java b/scm-core/src/test/java/sonia/scm/filter/GZipResponseFilterTest.java new file mode 100644 index 0000000000..b0a704c765 --- /dev/null +++ b/scm-core/src/test/java/sonia/scm/filter/GZipResponseFilterTest.java @@ -0,0 +1,81 @@ +package sonia.scm.filter; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.WriterInterceptorContext; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.zip.GZIPOutputStream; + +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class GZipResponseFilterTest { + + @Mock + private WriterInterceptorContext context; + + @Mock + private MultivaluedMap<String,Object> headers; + + private final GZipResponseFilter filter = new GZipResponseFilter(); + + @BeforeEach + void setUpContext() { + when(context.getHeaders()).thenReturn(headers); + } + + @Test + void shouldSkipGZipCompression() throws IOException { + when(headers.getFirst(HttpHeaders.ACCEPT_ENCODING)).thenReturn("deflate, br"); + + filter.aroundWriteTo(context); + + verifySkipped(); + } + + @Test + void shouldSkipGZipCompressionWithoutAcceptEncodingHeader() throws IOException { + filter.aroundWriteTo(context); + + verifySkipped(); + } + + private void verifySkipped() throws IOException { + verify(context, never()).getOutputStream(); + verify(context).proceed(); + } + + + @Nested + class AcceptGZipEncoding { + + @BeforeEach + void setUpContext() { + when(headers.getFirst(HttpHeaders.ACCEPT_ENCODING)).thenReturn("gzip, deflate, br"); + when(context.getOutputStream()).thenReturn(new ByteArrayOutputStream()); + } + + @Test + void shouldEncode() throws IOException { + filter.aroundWriteTo(context); + + verify(headers).remove(HttpHeaders.CONTENT_LENGTH); + verify(headers).add(HttpHeaders.CONTENT_ENCODING, "gzip"); + + verify(context).setOutputStream(any(GZIPOutputStream.class)); + verify(context, times(2)).setOutputStream(any(OutputStream.class)); + } + + } + + +} From 6ece9ea8b071824f9209da25b71704034c596b59 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra <sebastian.sdorra@cloudogu.com> Date: Wed, 30 Jan 2019 13:48:32 +0100 Subject: [PATCH 042/120] update web-resources, spotter and tika to prevent CVE-2018-11761 and CVE-2018-17197 --- scm-webapp/pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scm-webapp/pom.xml b/scm-webapp/pom.xml index 93db9005b2..dfaac3b4c0 100644 --- a/scm-webapp/pom.xml +++ b/scm-webapp/pom.xml @@ -256,19 +256,19 @@ <dependency> <groupId>com.github.sdorra</groupId> <artifactId>web-resources</artifactId> - <version>1.0.2</version> + <version>1.0.4</version> </dependency> <dependency> <groupId>com.github.sdorra</groupId> <artifactId>spotter-core</artifactId> - <version>1.1.0</version> + <version>1.2.1</version> </dependency> <dependency> <groupId>org.apache.tika</groupId> <artifactId>tika-core</artifactId> - <version>1.18</version> + <version>1.20</version> </dependency> <!-- test scope --> From f8f5237ad08366bec8343ea1e6f01949875f78e9 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra <sebastian.sdorra@cloudogu.com> Date: Wed, 30 Jan 2019 14:44:38 +0100 Subject: [PATCH 043/120] fix usage of deprecated mockito classes --- .../api/v2/resources/GitConfigDtoToGitConfigMapperTest.java | 2 +- .../api/v2/resources/GitConfigToGitConfigDtoMapperTest.java | 2 +- .../test/java/sonia/scm/repository/GitHeadModifierTest.java | 2 +- .../test/java/sonia/scm/web/GitReceivePackFactoryTest.java | 2 +- .../v2/resources/HgConfigAutoConfigurationResourceTest.java | 2 +- .../api/v2/resources/HgConfigDtoToHgConfigMapperTest.java | 2 +- .../api/v2/resources/HgConfigInstallationsResourceTest.java | 2 +- .../v2/resources/HgConfigInstallationsToDtoMapperTest.java | 2 +- .../scm/api/v2/resources/HgConfigPackageResourceTest.java | 2 +- .../api/v2/resources/HgConfigPackagesToDtoMapperTest.java | 2 +- .../sonia/scm/api/v2/resources/HgConfigResourceTest.java | 2 +- .../api/v2/resources/HgConfigToHgConfigDtoMapperTest.java | 2 +- .../test/java/sonia/scm/web/HgHookCallbackServletTest.java | 2 +- .../src/test/java/sonia/scm/web/WireProtocolTest.java | 2 +- .../api/v2/resources/SvnConfigDtoToSvnConfigMapperTest.java | 2 +- .../api/v2/resources/SvnConfigToSvnConfigDtoMapperTest.java | 2 +- .../java/sonia/scm/repository/SvnRepositoryHandlerTest.java | 2 +- .../scm/api/rest/resources/AbstractManagerResourceTest.java | 2 +- .../v2/resources/RepositoryPermissionRootResourceTest.java | 2 +- .../scm/api/v2/resources/RepositoryRootResourceTest.java | 6 +++--- .../v2/resources/RepositoryToRepositoryDtoMapperTest.java | 2 +- .../sonia/scm/api/v2/resources/UserRootResourceTest.java | 6 +++--- .../test/java/sonia/scm/web/cgi/DefaultCGIExecutorTest.java | 2 +- 23 files changed, 27 insertions(+), 27 deletions(-) diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigDtoToGitConfigMapperTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigDtoToGitConfigMapperTest.java index ed34db3008..6a05875aa9 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigDtoToGitConfigMapperTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigDtoToGitConfigMapperTest.java @@ -3,7 +3,7 @@ package sonia.scm.api.v2.resources; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import sonia.scm.repository.GitConfig; import static org.junit.Assert.*; diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigToGitConfigDtoMapperTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigToGitConfigDtoMapperTest.java index 40cf36e8dd..ee17ecb34b 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigToGitConfigDtoMapperTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigToGitConfigDtoMapperTest.java @@ -11,7 +11,7 @@ import org.junit.runner.RunWith; import org.mockito.Answers; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import sonia.scm.repository.GitConfig; import java.io.File; diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitHeadModifierTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitHeadModifierTest.java index 23b3110567..3362c8a22b 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitHeadModifierTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitHeadModifierTest.java @@ -40,7 +40,7 @@ import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import java.io.File; import java.io.IOException; diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/web/GitReceivePackFactoryTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/web/GitReceivePackFactoryTest.java index dc0822deba..4ed9d5a46a 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/web/GitReceivePackFactoryTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/web/GitReceivePackFactoryTest.java @@ -42,7 +42,7 @@ import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import sonia.scm.repository.GitConfig; import sonia.scm.repository.GitRepositoryHandler; diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigAutoConfigurationResourceTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigAutoConfigurationResourceTest.java index 4b66444bbe..1f88bfe665 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigAutoConfigurationResourceTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigAutoConfigurationResourceTest.java @@ -14,7 +14,7 @@ import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import sonia.scm.repository.HgConfig; import sonia.scm.repository.HgRepositoryHandler; import sonia.scm.web.HgVndMediaType; diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigDtoToHgConfigMapperTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigDtoToHgConfigMapperTest.java index 524e33e265..11dbd4638d 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigDtoToHgConfigMapperTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigDtoToHgConfigMapperTest.java @@ -3,7 +3,7 @@ package sonia.scm.api.v2.resources; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import sonia.scm.repository.HgConfig; import java.io.File; diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigInstallationsResourceTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigInstallationsResourceTest.java index 65b9c262cb..bcd9543d28 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigInstallationsResourceTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigInstallationsResourceTest.java @@ -14,7 +14,7 @@ import org.junit.runner.RunWith; import org.mockito.Answers; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import javax.inject.Provider; import javax.servlet.http.HttpServletResponse; diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigInstallationsToDtoMapperTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigInstallationsToDtoMapperTest.java index 7cae1d9f7e..80f8ec32b1 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigInstallationsToDtoMapperTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigInstallationsToDtoMapperTest.java @@ -6,7 +6,7 @@ import org.junit.runner.RunWith; import org.mockito.Answers; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import java.net.URI; import java.util.Arrays; diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigPackageResourceTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigPackageResourceTest.java index f1558b6efb..473ddfe4b4 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigPackageResourceTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigPackageResourceTest.java @@ -17,7 +17,7 @@ import org.junit.runner.RunWith; import org.mockito.Answers; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import sonia.scm.installer.HgPackage; import sonia.scm.installer.HgPackageReader; import sonia.scm.net.ahc.AdvancedHttpClient; diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigPackagesToDtoMapperTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigPackagesToDtoMapperTest.java index c4431da6d5..0b5d7b14d0 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigPackagesToDtoMapperTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigPackagesToDtoMapperTest.java @@ -6,7 +6,7 @@ import org.junit.runner.RunWith; import org.mockito.Answers; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import sonia.scm.installer.HgPackage; import sonia.scm.installer.HgPackages; diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigResourceTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigResourceTest.java index e9b96fd71b..764f999efa 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigResourceTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigResourceTest.java @@ -16,7 +16,7 @@ import org.junit.runner.RunWith; import org.mockito.Answers; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import sonia.scm.repository.HgConfig; import sonia.scm.repository.HgRepositoryHandler; import sonia.scm.web.HgVndMediaType; diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigToHgConfigDtoMapperTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigToHgConfigDtoMapperTest.java index 81c50f3d58..d4bc8be549 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigToHgConfigDtoMapperTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigToHgConfigDtoMapperTest.java @@ -11,7 +11,7 @@ import org.junit.runner.RunWith; import org.mockito.Answers; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import sonia.scm.repository.HgConfig; import java.net.URI; diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/web/HgHookCallbackServletTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/web/HgHookCallbackServletTest.java index 7d74024630..efe9983951 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/web/HgHookCallbackServletTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/web/HgHookCallbackServletTest.java @@ -8,7 +8,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; -import static org.mockito.Matchers.anyInt; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/web/WireProtocolTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/web/WireProtocolTest.java index 519dadfd6c..9237127c88 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/web/WireProtocolTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/web/WireProtocolTest.java @@ -37,7 +37,7 @@ import com.google.common.collect.Lists; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; diff --git a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/api/v2/resources/SvnConfigDtoToSvnConfigMapperTest.java b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/api/v2/resources/SvnConfigDtoToSvnConfigMapperTest.java index 8ab947fbaf..27ca6d5635 100644 --- a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/api/v2/resources/SvnConfigDtoToSvnConfigMapperTest.java +++ b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/api/v2/resources/SvnConfigDtoToSvnConfigMapperTest.java @@ -3,7 +3,7 @@ package sonia.scm.api.v2.resources; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import sonia.scm.repository.Compatibility; import sonia.scm.repository.SvnConfig; diff --git a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/api/v2/resources/SvnConfigToSvnConfigDtoMapperTest.java b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/api/v2/resources/SvnConfigToSvnConfigDtoMapperTest.java index 6bbff499e1..b48a959c83 100644 --- a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/api/v2/resources/SvnConfigToSvnConfigDtoMapperTest.java +++ b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/api/v2/resources/SvnConfigToSvnConfigDtoMapperTest.java @@ -11,7 +11,7 @@ import org.junit.runner.RunWith; import org.mockito.Answers; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import sonia.scm.repository.Compatibility; import sonia.scm.repository.SvnConfig; diff --git a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/SvnRepositoryHandlerTest.java b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/SvnRepositoryHandlerTest.java index 7b22e15c94..9b73c22c04 100644 --- a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/SvnRepositoryHandlerTest.java +++ b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/SvnRepositoryHandlerTest.java @@ -47,7 +47,7 @@ import java.io.IOException; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.initMocks; diff --git a/scm-webapp/src/test/java/sonia/scm/api/rest/resources/AbstractManagerResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/rest/resources/AbstractManagerResourceTest.java index 696174d6e0..fd9745be83 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/rest/resources/AbstractManagerResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/rest/resources/AbstractManagerResourceTest.java @@ -21,7 +21,7 @@ import java.util.Comparator; import static java.util.Collections.emptyList; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.class) diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryPermissionRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryPermissionRootResourceTest.java index 8dd451c556..33b30c5532 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryPermissionRootResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryPermissionRootResourceTest.java @@ -55,7 +55,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import static org.junit.jupiter.api.DynamicTest.dynamicTest; -import static org.mockito.Matchers.any; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryRootResourceTest.java index 032b79d2b7..1f6ed6b3a7 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryRootResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryRootResourceTest.java @@ -42,9 +42,9 @@ import static javax.servlet.http.HttpServletResponse.SC_PRECONDITION_FAILED; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyObject; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyObject; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapperTest.java index 8469e966c8..c5e2fb4670 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapperTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapperTest.java @@ -23,7 +23,7 @@ import static java.util.stream.Stream.of; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.initMocks; diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserRootResourceTest.java index 1da540106a..ffe1a746d5 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserRootResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserRootResourceTest.java @@ -36,8 +36,8 @@ import static java.util.Collections.singletonList; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.never; @@ -77,7 +77,7 @@ public class UserRootResourceTest { private User originalUser; @Before - public void prepareEnvironment() throws Exception { + public void prepareEnvironment() { initMocks(this); originalUser = createDummyUser("Neo"); when(userManager.create(userCaptor.capture())).thenAnswer(invocation -> invocation.getArguments()[0]); diff --git a/scm-webapp/src/test/java/sonia/scm/web/cgi/DefaultCGIExecutorTest.java b/scm-webapp/src/test/java/sonia/scm/web/cgi/DefaultCGIExecutorTest.java index 29c7dea358..5f95a171d2 100644 --- a/scm-webapp/src/test/java/sonia/scm/web/cgi/DefaultCGIExecutorTest.java +++ b/scm-webapp/src/test/java/sonia/scm/web/cgi/DefaultCGIExecutorTest.java @@ -3,7 +3,7 @@ package sonia.scm.web.cgi; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import javax.servlet.http.HttpServletRequest; From 8bf82213b8f4af9fef277f15e14061250a54258c Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra <sebastian.sdorra@cloudogu.com> Date: Wed, 30 Jan 2019 14:48:37 +0100 Subject: [PATCH 044/120] remove unused imports --- scm-core/src/main/java/sonia/scm/HandlerBase.java | 1 - .../src/main/java/sonia/scm/net/ahc/BaseHttpRequest.java | 1 - scm-core/src/main/java/sonia/scm/plugin/PluginLoader.java | 2 -- .../java/sonia/scm/repository/spi/MergeCommandRequest.java | 1 - .../main/java/sonia/scm/security/PermissionDescriptor.java | 1 - .../java/sonia/scm/security/StoredAssignedPermission.java | 2 -- .../sonia/scm/net/ahc/AdvancedHttpRequestWithBodyTest.java | 2 +- .../test/java/sonia/scm/security/DAORealmHelperTest.java | 1 - .../src/main/java/sonia/scm/store/JAXBDataStoreFactory.java | 3 --- scm-dao-xml/src/main/java/sonia/scm/xml/AbstractXmlDAO.java | 1 - scm-dao-xml/src/main/java/sonia/scm/xml/XmlStreams.java | 1 - scm-it/src/test/java/sonia/scm/it/RepositoriesITCase.java | 1 - scm-it/src/test/java/sonia/scm/it/utils/ScmRequests.java | 2 -- .../java/sonia/scm/api/v2/resources/GitConfigResource.java | 2 -- .../src/main/java/sonia/scm/web/GitRepositoryResolver.java | 1 - .../api/v2/resources/GitConfigToGitConfigDtoMapperTest.java | 1 - .../api/v2/resources/GitRepositoryConfigEnricherTest.java | 3 --- .../scm/repository/spi/AbstractGitCommandTestBase.java | 2 -- .../java/sonia/scm/repository/spi/GitBlameCommandTest.java | 2 -- .../java/sonia/scm/repository/spi/GitBrowseCommandTest.java | 2 -- .../java/sonia/scm/repository/spi/GitLogCommandTest.java | 2 -- .../main/java/sonia/scm/installer/AbstractHgInstaller.java | 4 +--- .../src/main/java/sonia/scm/repository/HgImportHandler.java | 1 - .../sonia/scm/repository/spi/javahg/HgFileviewCommand.java | 2 -- .../src/main/java/sonia/scm/web/HgPermissionFilter.java | 1 - .../api/v2/resources/HgConfigDtoToHgConfigMapperTest.java | 2 -- .../sonia/scm/api/v2/resources/HgConfigResourceTest.java | 1 - .../test/java/sonia/scm/api/v2/resources/HgConfigTests.java | 2 -- .../src/test/java/sonia/scm/web/HgPermissionFilterTest.java | 2 -- .../java/sonia/scm/repository/SvnRepositoryHandler.java | 1 - .../java/sonia/scm/repository/spi/SvnBrowseCommand.java | 2 -- .../api/v2/resources/SvnConfigToSvnConfigDtoMapperTest.java | 1 - .../java/sonia/scm/repository/SvnRepositoryHandlerTest.java | 4 ---- .../java/sonia/scm/repository/spi/SvnLogCommandTest.java | 2 -- .../scm/api/rest/resources/BrowserStreamingOutput.java | 2 -- .../sonia/scm/api/rest/resources/DiffStreamingOutput.java | 1 - .../java/sonia/scm/api/v2/resources/BranchRootResource.java | 1 - .../src/main/java/sonia/scm/api/v2/resources/ErrorDto.java | 1 - .../api/v2/resources/FileObjectToFileObjectDtoMapper.java | 3 --- .../sonia/scm/api/v2/resources/GroupCollectionResource.java | 1 - .../src/main/java/sonia/scm/api/v2/resources/GroupDto.java | 1 - .../sonia/scm/api/v2/resources/GroupDtoToGroupMapper.java | 2 -- .../api/v2/resources/PermissionCollectionToDtoMapper.java | 1 - .../scm/api/v2/resources/RepositoryCollectionResource.java | 1 - .../sonia/scm/api/v2/resources/RepositoryPermissionDto.java | 2 -- .../sonia/scm/api/v2/resources/UserCollectionResource.java | 1 - .../main/java/sonia/scm/boot/BootstrapContextListener.java | 3 --- scm-webapp/src/main/java/sonia/scm/filter/MDCFilter.java | 1 - .../java/sonia/scm/filter/PropagatePrincipleFilter.java | 2 -- .../src/main/java/sonia/scm/security/JwtAccessToken.java | 1 - .../web/filter/HttpProtocolServletAuthenticationFilter.java | 3 --- .../src/test/java/sonia/scm/api/v2/JsonFiltersTest.java | 1 - .../v2/resources/ChangesetCollectionToDtoMapperTest.java | 1 - .../scm/api/v2/resources/GroupToGroupDtoMapperTest.java | 1 - .../java/sonia/scm/api/v2/resources/MeDtoFactoryTest.java | 1 - .../java/sonia/scm/api/v2/resources/MeResourceTest.java | 1 - .../v2/resources/RepositoryPermissionRootResourceTest.java | 1 - .../sonia/scm/api/v2/resources/UserDtoToUserMapperTest.java | 1 - .../sonia/scm/api/v2/resources/UserRootResourceTest.java | 1 - .../src/test/java/sonia/scm/boot/RestartServletTest.java | 1 - .../test/java/sonia/scm/boot/ServletContextCleanerTest.java | 1 - .../sonia/scm/net/ahc/DefaultAdvancedHttpResponseTest.java | 2 -- .../java/sonia/scm/plugin/MultiParentClassLoaderTest.java | 3 --- .../src/test/java/sonia/scm/schedule/QuartzTaskTest.java | 5 ++--- .../src/test/java/sonia/scm/security/BearerRealmTest.java | 6 ------ .../test/java/sonia/scm/security/SecureKeyResolverTest.java | 1 - .../test/java/sonia/scm/user/DefaultUserManagerTest.java | 3 --- .../src/test/java/sonia/scm/web/i18n/I18nServletTest.java | 2 -- 68 files changed, 4 insertions(+), 114 deletions(-) diff --git a/scm-core/src/main/java/sonia/scm/HandlerBase.java b/scm-core/src/main/java/sonia/scm/HandlerBase.java index a621f4f697..d960cc6107 100644 --- a/scm-core/src/main/java/sonia/scm/HandlerBase.java +++ b/scm-core/src/main/java/sonia/scm/HandlerBase.java @@ -36,7 +36,6 @@ package sonia.scm; //~--- JDK imports ------------------------------------------------------------ import java.io.Closeable; -import java.io.IOException; /** * The base class of all handlers. diff --git a/scm-core/src/main/java/sonia/scm/net/ahc/BaseHttpRequest.java b/scm-core/src/main/java/sonia/scm/net/ahc/BaseHttpRequest.java index c80ae9b1c4..0723f44b6c 100644 --- a/scm-core/src/main/java/sonia/scm/net/ahc/BaseHttpRequest.java +++ b/scm-core/src/main/java/sonia/scm/net/ahc/BaseHttpRequest.java @@ -35,7 +35,6 @@ package sonia.scm.net.ahc; import com.google.common.base.Charsets; import com.google.common.base.Strings; -import com.google.common.collect.HashMultimap; import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.Multimap; diff --git a/scm-core/src/main/java/sonia/scm/plugin/PluginLoader.java b/scm-core/src/main/java/sonia/scm/plugin/PluginLoader.java index 0bf37054a8..2d65d1cc98 100644 --- a/scm-core/src/main/java/sonia/scm/plugin/PluginLoader.java +++ b/scm-core/src/main/java/sonia/scm/plugin/PluginLoader.java @@ -35,8 +35,6 @@ package sonia.scm.plugin; //~--- non-JDK imports -------------------------------------------------------- -import com.google.inject.Module; - //~--- JDK imports ------------------------------------------------------------ import java.util.Collection; diff --git a/scm-core/src/main/java/sonia/scm/repository/spi/MergeCommandRequest.java b/scm-core/src/main/java/sonia/scm/repository/spi/MergeCommandRequest.java index baf03a0aef..45dfc0b2b7 100644 --- a/scm-core/src/main/java/sonia/scm/repository/spi/MergeCommandRequest.java +++ b/scm-core/src/main/java/sonia/scm/repository/spi/MergeCommandRequest.java @@ -5,7 +5,6 @@ import com.google.common.base.Objects; import com.google.common.base.Strings; import sonia.scm.Validateable; import sonia.scm.repository.Person; -import sonia.scm.util.Util; import java.io.Serializable; diff --git a/scm-core/src/main/java/sonia/scm/security/PermissionDescriptor.java b/scm-core/src/main/java/sonia/scm/security/PermissionDescriptor.java index a005583256..8d95131ee6 100644 --- a/scm-core/src/main/java/sonia/scm/security/PermissionDescriptor.java +++ b/scm-core/src/main/java/sonia/scm/security/PermissionDescriptor.java @@ -39,7 +39,6 @@ import com.google.common.base.Objects; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import java.io.Serializable; diff --git a/scm-core/src/main/java/sonia/scm/security/StoredAssignedPermission.java b/scm-core/src/main/java/sonia/scm/security/StoredAssignedPermission.java index 4b2e46b665..903f86df90 100644 --- a/scm-core/src/main/java/sonia/scm/security/StoredAssignedPermission.java +++ b/scm-core/src/main/java/sonia/scm/security/StoredAssignedPermission.java @@ -34,8 +34,6 @@ package sonia.scm.security; //~--- JDK imports ------------------------------------------------------------ -import com.google.common.base.Objects; - import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlRootElement; diff --git a/scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpRequestWithBodyTest.java b/scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpRequestWithBodyTest.java index b7fa9ae84a..92ca488ddf 100644 --- a/scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpRequestWithBodyTest.java +++ b/scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpRequestWithBodyTest.java @@ -36,7 +36,7 @@ import com.google.common.io.ByteSource; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; -import java.io.UnsupportedEncodingException; + import org.junit.Test; import static org.junit.Assert.*; import static org.hamcrest.Matchers.*; diff --git a/scm-core/src/test/java/sonia/scm/security/DAORealmHelperTest.java b/scm-core/src/test/java/sonia/scm/security/DAORealmHelperTest.java index af4bf37915..7ddeabd8ac 100644 --- a/scm-core/src/test/java/sonia/scm/security/DAORealmHelperTest.java +++ b/scm-core/src/test/java/sonia/scm/security/DAORealmHelperTest.java @@ -1,7 +1,6 @@ package sonia.scm.security; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.DisabledAccountException; import org.apache.shiro.authc.UnknownAccountException; diff --git a/scm-dao-xml/src/main/java/sonia/scm/store/JAXBDataStoreFactory.java b/scm-dao-xml/src/main/java/sonia/scm/store/JAXBDataStoreFactory.java index 5b5c00a298..579ef75b71 100644 --- a/scm-dao-xml/src/main/java/sonia/scm/store/JAXBDataStoreFactory.java +++ b/scm-dao-xml/src/main/java/sonia/scm/store/JAXBDataStoreFactory.java @@ -37,9 +37,6 @@ package sonia.scm.store; import com.google.inject.Inject; import com.google.inject.Singleton; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import sonia.scm.SCMContextProvider; import sonia.scm.repository.RepositoryLocationResolver; import sonia.scm.security.KeyGenerator; diff --git a/scm-dao-xml/src/main/java/sonia/scm/xml/AbstractXmlDAO.java b/scm-dao-xml/src/main/java/sonia/scm/xml/AbstractXmlDAO.java index 1ce0508616..5b24096eb5 100644 --- a/scm-dao-xml/src/main/java/sonia/scm/xml/AbstractXmlDAO.java +++ b/scm-dao-xml/src/main/java/sonia/scm/xml/AbstractXmlDAO.java @@ -38,7 +38,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sonia.scm.GenericDAO; import sonia.scm.ModelObject; -import sonia.scm.group.xml.XmlGroupDAO; import sonia.scm.store.ConfigurationStore; import java.util.Collection; diff --git a/scm-dao-xml/src/main/java/sonia/scm/xml/XmlStreams.java b/scm-dao-xml/src/main/java/sonia/scm/xml/XmlStreams.java index d812eedc35..4b3d9b0f28 100644 --- a/scm-dao-xml/src/main/java/sonia/scm/xml/XmlStreams.java +++ b/scm-dao-xml/src/main/java/sonia/scm/xml/XmlStreams.java @@ -1,6 +1,5 @@ package sonia.scm.xml; -import com.google.common.base.Charsets; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/scm-it/src/test/java/sonia/scm/it/RepositoriesITCase.java b/scm-it/src/test/java/sonia/scm/it/RepositoriesITCase.java index 3c67ca3dc3..c49a65bea2 100644 --- a/scm-it/src/test/java/sonia/scm/it/RepositoriesITCase.java +++ b/scm-it/src/test/java/sonia/scm/it/RepositoriesITCase.java @@ -36,7 +36,6 @@ package sonia.scm.it; import org.apache.http.HttpStatus; import org.assertj.core.api.Assertions; import org.junit.Before; -import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; diff --git a/scm-it/src/test/java/sonia/scm/it/utils/ScmRequests.java b/scm-it/src/test/java/sonia/scm/it/utils/ScmRequests.java index 9386f1d9c5..0a5693ad2e 100644 --- a/scm-it/src/test/java/sonia/scm/it/utils/ScmRequests.java +++ b/scm-it/src/test/java/sonia/scm/it/utils/ScmRequests.java @@ -5,10 +5,8 @@ import io.restassured.response.Response; import org.junit.Assert; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sonia.scm.user.User; import sonia.scm.web.VndMediaType; -import java.net.ConnectException; import java.util.List; import java.util.Map; import java.util.function.Consumer; diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/api/v2/resources/GitConfigResource.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/api/v2/resources/GitConfigResource.java index e078b04b08..7cda4bc9d3 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/api/v2/resources/GitConfigResource.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/api/v2/resources/GitConfigResource.java @@ -18,8 +18,6 @@ import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.Response; -import static sonia.scm.ContextEntry.ContextBuilder.entity; - /** * RESTful Web Service Resource to manage the configuration of the git plugin. */ diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitRepositoryResolver.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitRepositoryResolver.java index a2114a1b6a..6db7d694d5 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitRepositoryResolver.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitRepositoryResolver.java @@ -35,7 +35,6 @@ package sonia.scm.web; //~--- non-JDK imports -------------------------------------------------------- -import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.inject.Inject; import org.eclipse.jgit.errors.RepositoryNotFoundException; diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigToGitConfigDtoMapperTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigToGitConfigDtoMapperTest.java index ee17ecb34b..62fa8d33b4 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigToGitConfigDtoMapperTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigToGitConfigDtoMapperTest.java @@ -14,7 +14,6 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import sonia.scm.repository.GitConfig; -import java.io.File; import java.net.URI; import static org.junit.Assert.assertEquals; diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitRepositoryConfigEnricherTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitRepositoryConfigEnricherTest.java index d2942d08a3..a1e349dd57 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitRepositoryConfigEnricherTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitRepositoryConfigEnricherTest.java @@ -14,9 +14,6 @@ import org.mockito.junit.jupiter.MockitoExtension; import sonia.scm.repository.NamespaceAndName; import sonia.scm.repository.Repository; import sonia.scm.repository.RepositoryManager; -import sonia.scm.repository.api.Command; -import sonia.scm.repository.api.RepositoryService; -import sonia.scm.repository.api.RepositoryServiceFactory; import sonia.scm.web.JsonEnricherContext; import sonia.scm.web.VndMediaType; diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/AbstractGitCommandTestBase.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/AbstractGitCommandTestBase.java index 630236b20b..f2a4ed4954 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/AbstractGitCommandTestBase.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/AbstractGitCommandTestBase.java @@ -35,10 +35,8 @@ package sonia.scm.repository.spi; //~--- non-JDK imports -------------------------------------------------------- import org.junit.After; -import org.junit.Before; import sonia.scm.api.v2.resources.GitRepositoryConfigStoreProvider; import sonia.scm.repository.GitRepositoryConfig; -import sonia.scm.store.InMemoryConfigurationStore; import sonia.scm.store.InMemoryConfigurationStoreFactory; /** diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBlameCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBlameCommandTest.java index 817e4641dd..c8d260d503 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBlameCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBlameCommandTest.java @@ -35,11 +35,9 @@ package sonia.scm.repository.spi; //~--- non-JDK imports -------------------------------------------------------- import org.junit.Test; -import sonia.scm.api.v2.resources.GitRepositoryConfigStoreProvider; import sonia.scm.repository.BlameLine; import sonia.scm.repository.BlameResult; import sonia.scm.repository.GitRepositoryConfig; -import sonia.scm.store.InMemoryConfigurationStoreFactory; import java.io.IOException; diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBrowseCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBrowseCommandTest.java index 2ff3c73420..1feceba652 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBrowseCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBrowseCommandTest.java @@ -32,11 +32,9 @@ package sonia.scm.repository.spi; import org.junit.Test; -import sonia.scm.api.v2.resources.GitRepositoryConfigStoreProvider; import sonia.scm.repository.BrowserResult; import sonia.scm.repository.FileObject; import sonia.scm.repository.GitRepositoryConfig; -import sonia.scm.store.InMemoryConfigurationStoreFactory; import java.io.IOException; import java.util.Collection; diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitLogCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitLogCommandTest.java index e2ab85d9a7..06e9b17fe7 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitLogCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitLogCommandTest.java @@ -36,10 +36,8 @@ package sonia.scm.repository.spi; import com.google.common.io.Files; import org.junit.Test; -import sonia.scm.event.ScmEventBus; import sonia.scm.repository.Changeset; import sonia.scm.repository.ChangesetPagingResult; -import sonia.scm.repository.ClearRepositoryCacheEvent; import sonia.scm.repository.GitRepositoryConfig; import sonia.scm.repository.Modifications; diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/installer/AbstractHgInstaller.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/installer/AbstractHgInstaller.java index 785aa399b1..27fdc7a296 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/installer/AbstractHgInstaller.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/installer/AbstractHgInstaller.java @@ -35,14 +35,12 @@ package sonia.scm.installer; //~--- non-JDK imports -------------------------------------------------------- -import sonia.scm.repository.HgConfig; import sonia.scm.repository.HgRepositoryHandler; -import sonia.scm.util.IOUtil; //~--- JDK imports ------------------------------------------------------------ import java.io.File; -import java.io.IOException; + import sonia.scm.net.ahc.AdvancedHttpClient; /** diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgImportHandler.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgImportHandler.java index 4b6998f09a..b1d431c742 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgImportHandler.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgImportHandler.java @@ -39,7 +39,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sonia.scm.io.INIConfiguration; import sonia.scm.io.INIConfigurationReader; -import sonia.scm.io.INIConfigurationWriter; import sonia.scm.io.INISection; import sonia.scm.util.ValidationUtil; diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/javahg/HgFileviewCommand.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/javahg/HgFileviewCommand.java index f351ffa572..0897a191a1 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/javahg/HgFileviewCommand.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/javahg/HgFileviewCommand.java @@ -41,7 +41,6 @@ import com.aragost.javahg.internals.AbstractCommand; import com.aragost.javahg.internals.HgInputStream; import com.google.common.base.Strings; -import com.google.common.collect.Lists; import sonia.scm.repository.FileObject; import sonia.scm.repository.SubRepository; @@ -52,7 +51,6 @@ import java.io.IOException; import java.util.Deque; import java.util.LinkedList; -import java.util.List; /** * Mercurial command to list files of a repository. diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgPermissionFilter.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgPermissionFilter.java index 93b9699fc9..18b716b665 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgPermissionFilter.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgPermissionFilter.java @@ -44,7 +44,6 @@ import sonia.scm.web.filter.PermissionFilter; import sonia.scm.repository.HgRepositoryHandler; -import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import java.util.Set; diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigDtoToHgConfigMapperTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigDtoToHgConfigMapperTest.java index 11dbd4638d..6e181f4886 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigDtoToHgConfigMapperTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigDtoToHgConfigMapperTest.java @@ -6,8 +6,6 @@ import org.mockito.InjectMocks; import org.mockito.junit.MockitoJUnitRunner; import sonia.scm.repository.HgConfig; -import java.io.File; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigResourceTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigResourceTest.java index 764f999efa..e0253ad86a 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigResourceTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigResourceTest.java @@ -23,7 +23,6 @@ import sonia.scm.web.HgVndMediaType; import javax.inject.Provider; import javax.servlet.http.HttpServletResponse; -import java.io.File; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URI; diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigTests.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigTests.java index 84343cdf72..a3430aac43 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigTests.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigTests.java @@ -3,8 +3,6 @@ package sonia.scm.api.v2.resources; import sonia.scm.installer.HgPackage; import sonia.scm.repository.HgConfig; -import java.io.File; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/web/HgPermissionFilterTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/web/HgPermissionFilterTest.java index b3a4a0c2a4..f9bc77bbda 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/web/HgPermissionFilterTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/web/HgPermissionFilterTest.java @@ -48,8 +48,6 @@ import javax.servlet.http.HttpServletRequest; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.junit.Assert.*; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; import static sonia.scm.web.WireProtocolRequestMockFactory.CMDS_HEADS_KNOWN_NODES; import static sonia.scm.web.WireProtocolRequestMockFactory.Namespace.BOOKMARKS; import static sonia.scm.web.WireProtocolRequestMockFactory.Namespace.PHASES; diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnRepositoryHandler.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnRepositoryHandler.java index 86f99cd517..97698d7a77 100644 --- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnRepositoryHandler.java +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnRepositoryHandler.java @@ -55,7 +55,6 @@ import sonia.scm.logging.SVNKitLogger; import sonia.scm.plugin.Extension; import sonia.scm.repository.spi.HookEventFacade; import sonia.scm.repository.spi.SvnRepositoryServiceProvider; -import sonia.scm.store.ConfigurationStore; import sonia.scm.store.ConfigurationStoreFactory; import sonia.scm.util.Util; diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnBrowseCommand.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnBrowseCommand.java index e2f58b593b..df266a11af 100644 --- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnBrowseCommand.java +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnBrowseCommand.java @@ -36,7 +36,6 @@ package sonia.scm.repository.spi; //~--- non-JDK imports -------------------------------------------------------- import com.google.common.base.Strings; -import com.google.common.collect.Lists; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.tmatesoft.svn.core.SVNDirEntry; @@ -53,7 +52,6 @@ import sonia.scm.repository.SvnUtil; import sonia.scm.util.Util; import java.util.Collection; -import java.util.List; //~--- JDK imports ------------------------------------------------------------ diff --git a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/api/v2/resources/SvnConfigToSvnConfigDtoMapperTest.java b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/api/v2/resources/SvnConfigToSvnConfigDtoMapperTest.java index b48a959c83..07ead15322 100644 --- a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/api/v2/resources/SvnConfigToSvnConfigDtoMapperTest.java +++ b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/api/v2/resources/SvnConfigToSvnConfigDtoMapperTest.java @@ -15,7 +15,6 @@ import org.mockito.junit.MockitoJUnitRunner; import sonia.scm.repository.Compatibility; import sonia.scm.repository.SvnConfig; -import java.io.File; import java.net.URI; import static org.junit.Assert.assertEquals; diff --git a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/SvnRepositoryHandlerTest.java b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/SvnRepositoryHandlerTest.java index 9b73c22c04..7c8dc15407 100644 --- a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/SvnRepositoryHandlerTest.java +++ b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/SvnRepositoryHandlerTest.java @@ -32,14 +32,10 @@ package sonia.scm.repository; -import org.junit.Before; import org.junit.Test; -import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import sonia.scm.repository.api.HookContextFactory; import sonia.scm.repository.spi.HookEventFacade; -import sonia.scm.store.ConfigurationStore; import sonia.scm.store.ConfigurationStoreFactory; import java.io.File; diff --git a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/spi/SvnLogCommandTest.java b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/spi/SvnLogCommandTest.java index f2511a9ad9..0cfeaa3a1c 100644 --- a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/spi/SvnLogCommandTest.java +++ b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/spi/SvnLogCommandTest.java @@ -39,8 +39,6 @@ import sonia.scm.repository.Changeset; import sonia.scm.repository.ChangesetPagingResult; import sonia.scm.repository.Modifications; -import java.io.IOException; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/BrowserStreamingOutput.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/BrowserStreamingOutput.java index d2ce744c19..79b5dbc2ae 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/BrowserStreamingOutput.java +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/BrowserStreamingOutput.java @@ -6,8 +6,6 @@ import sonia.scm.repository.api.CatCommandBuilder; import sonia.scm.repository.api.RepositoryService; import sonia.scm.util.IOUtil; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Response; import javax.ws.rs.core.StreamingOutput; import java.io.IOException; import java.io.OutputStream; diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/DiffStreamingOutput.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/DiffStreamingOutput.java index d177e05a5e..b7f994b967 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/DiffStreamingOutput.java +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/DiffStreamingOutput.java @@ -42,7 +42,6 @@ import sonia.scm.repository.api.RepositoryService; import sonia.scm.util.IOUtil; import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Response; import javax.ws.rs.core.StreamingOutput; import java.io.IOException; import java.io.OutputStream; diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchRootResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchRootResource.java index 658abbded8..71b1127ad8 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchRootResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchRootResource.java @@ -26,7 +26,6 @@ import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Response; import java.io.IOException; -import java.util.List; import static sonia.scm.ContextEntry.ContextBuilder.entity; import static sonia.scm.NotFoundException.notFound; diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ErrorDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ErrorDto.java index bd889d5de5..d155fbede6 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ErrorDto.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ErrorDto.java @@ -5,7 +5,6 @@ import lombok.Getter; import lombok.Setter; import sonia.scm.ContextEntry; -import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlRootElement; import java.util.List; diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapper.java index 2432d5168c..2ec662ef9b 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapper.java @@ -12,9 +12,6 @@ import sonia.scm.repository.SubRepository; import javax.inject.Inject; -import java.util.List; -import java.util.stream.Collectors; - import static de.otto.edison.hal.Link.link; @Mapper diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupCollectionResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupCollectionResource.java index 4c111e6707..6c13dc33a5 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupCollectionResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupCollectionResource.java @@ -10,7 +10,6 @@ import sonia.scm.group.GroupManager; import sonia.scm.web.VndMediaType; import javax.inject.Inject; -import javax.inject.Named; import javax.validation.Valid; import javax.ws.rs.Consumes; import javax.ws.rs.DefaultValue; 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 760beab1da..eb174c69b6 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 @@ -6,7 +6,6 @@ import de.otto.edison.hal.Links; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; -import org.hibernate.validator.constraints.NotEmpty; import javax.validation.constraints.Pattern; import java.time.Instant; diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupDtoToGroupMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupDtoToGroupMapper.java index be1aca5814..3812f700da 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupDtoToGroupMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupDtoToGroupMapper.java @@ -4,8 +4,6 @@ import org.mapstruct.Mapper; import org.mapstruct.Mapping; import sonia.scm.group.Group; -import java.time.Instant; - @Mapper public abstract class GroupDtoToGroupMapper extends BaseDtoMapper { diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionCollectionToDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionCollectionToDtoMapper.java index 87d1aeca9f..31269d468e 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionCollectionToDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionCollectionToDtoMapper.java @@ -1,7 +1,6 @@ package sonia.scm.api.v2.resources; import de.otto.edison.hal.Links; -import org.mapstruct.Context; import sonia.scm.security.PermissionDescriptor; import sonia.scm.security.PermissionPermissions; diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryCollectionResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryCollectionResource.java index a9bd5c2424..e1e1260a4d 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryCollectionResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryCollectionResource.java @@ -23,7 +23,6 @@ import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Response; -import static java.util.Arrays.asList; import static java.util.Collections.singletonList; public class RepositoryCollectionResource { diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryPermissionDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryPermissionDto.java index 09683db488..fe8c2c19b1 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryPermissionDto.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryPermissionDto.java @@ -1,6 +1,5 @@ package sonia.scm.api.v2.resources; -import com.fasterxml.jackson.annotation.JsonInclude; import de.otto.edison.hal.HalRepresentation; import de.otto.edison.hal.Links; import lombok.Getter; @@ -9,7 +8,6 @@ import lombok.Setter; import lombok.ToString; import org.hibernate.validator.constraints.NotEmpty; -import javax.validation.constraints.NotNull; import javax.validation.constraints.Pattern; import java.util.Collection; diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserCollectionResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserCollectionResource.java index a4fe9adb94..a7442a2262 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserCollectionResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserCollectionResource.java @@ -11,7 +11,6 @@ import sonia.scm.user.UserManager; import sonia.scm.web.VndMediaType; import javax.inject.Inject; -import javax.inject.Named; import javax.validation.Valid; import javax.ws.rs.Consumes; import javax.ws.rs.DefaultValue; diff --git a/scm-webapp/src/main/java/sonia/scm/boot/BootstrapContextListener.java b/scm-webapp/src/main/java/sonia/scm/boot/BootstrapContextListener.java index afaa28bfe8..be5a1e7ac2 100644 --- a/scm-webapp/src/main/java/sonia/scm/boot/BootstrapContextListener.java +++ b/scm-webapp/src/main/java/sonia/scm/boot/BootstrapContextListener.java @@ -33,12 +33,9 @@ package sonia.scm.boot; //~--- non-JDK imports -------------------------------------------------------- -import com.github.legman.Subscribe; - import com.google.common.base.Charsets; import com.google.common.collect.ImmutableList; import com.google.common.io.Files; -import com.google.inject.servlet.GuiceFilter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/scm-webapp/src/main/java/sonia/scm/filter/MDCFilter.java b/scm-webapp/src/main/java/sonia/scm/filter/MDCFilter.java index b77a927a2d..fc52ef4eff 100644 --- a/scm-webapp/src/main/java/sonia/scm/filter/MDCFilter.java +++ b/scm-webapp/src/main/java/sonia/scm/filter/MDCFilter.java @@ -42,7 +42,6 @@ import org.slf4j.MDC; import sonia.scm.SCMContext; import sonia.scm.security.DefaultKeyGenerator; -import sonia.scm.security.KeyGenerator; import sonia.scm.web.filter.HttpFilter; //~--- JDK imports ------------------------------------------------------------ diff --git a/scm-webapp/src/main/java/sonia/scm/filter/PropagatePrincipleFilter.java b/scm-webapp/src/main/java/sonia/scm/filter/PropagatePrincipleFilter.java index 508e804d1f..e7d020ae18 100644 --- a/scm-webapp/src/main/java/sonia/scm/filter/PropagatePrincipleFilter.java +++ b/scm-webapp/src/main/java/sonia/scm/filter/PropagatePrincipleFilter.java @@ -51,8 +51,6 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; -import static sonia.scm.api.v2.resources.ScmPathInfo.REST_API_PATH; - //~--- JDK imports ------------------------------------------------------------ /** diff --git a/scm-webapp/src/main/java/sonia/scm/security/JwtAccessToken.java b/scm-webapp/src/main/java/sonia/scm/security/JwtAccessToken.java index 64e26405a1..5b895a34fa 100644 --- a/scm-webapp/src/main/java/sonia/scm/security/JwtAccessToken.java +++ b/scm-webapp/src/main/java/sonia/scm/security/JwtAccessToken.java @@ -35,7 +35,6 @@ import io.jsonwebtoken.Claims; import java.util.Collections; import java.util.Date; -import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; diff --git a/scm-webapp/src/main/java/sonia/scm/web/filter/HttpProtocolServletAuthenticationFilter.java b/scm-webapp/src/main/java/sonia/scm/web/filter/HttpProtocolServletAuthenticationFilter.java index 77683bd6be..e58945a346 100644 --- a/scm-webapp/src/main/java/sonia/scm/web/filter/HttpProtocolServletAuthenticationFilter.java +++ b/scm-webapp/src/main/java/sonia/scm/web/filter/HttpProtocolServletAuthenticationFilter.java @@ -17,9 +17,6 @@ import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Set; -import static sonia.scm.util.HttpUtil.AUTHENTICATION_REALM; -import static sonia.scm.util.HttpUtil.HEADER_WWW_AUTHENTICATE; - @Priority(Filters.PRIORITY_AUTHENTICATION) @WebElement(value = HttpProtocolServlet.PATTERN) public class HttpProtocolServletAuthenticationFilter extends AuthenticationFilter { diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/JsonFiltersTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/JsonFiltersTest.java index b60775a73b..8bdbacafc2 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/JsonFiltersTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/JsonFiltersTest.java @@ -3,7 +3,6 @@ package sonia.scm.api.v2; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; -import com.google.common.collect.Lists; import com.google.common.io.Resources; import org.junit.Test; diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ChangesetCollectionToDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ChangesetCollectionToDtoMapperTest.java index 69695279e6..87ee5943db 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ChangesetCollectionToDtoMapperTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ChangesetCollectionToDtoMapperTest.java @@ -1,6 +1,5 @@ package sonia.scm.api.v2.resources; -import org.assertj.core.api.Assertions; import org.junit.Test; import sonia.scm.PageResult; import sonia.scm.repository.Changeset; diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/GroupToGroupDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/GroupToGroupDtoMapperTest.java index b681dff21f..7965d949c4 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/GroupToGroupDtoMapperTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/GroupToGroupDtoMapperTest.java @@ -11,7 +11,6 @@ import org.mockito.InjectMocks; import sonia.scm.group.Group; import java.net.URI; -import java.net.URISyntaxException; import java.util.stream.IntStream; import static java.util.stream.Collectors.toList; diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MeDtoFactoryTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MeDtoFactoryTest.java index 138387938b..c85a803b00 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MeDtoFactoryTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MeDtoFactoryTest.java @@ -15,7 +15,6 @@ import org.mockito.quality.Strictness; import sonia.scm.group.GroupNames; import sonia.scm.user.User; import sonia.scm.user.UserManager; -import sonia.scm.user.UserPermissions; import sonia.scm.user.UserTestData; import java.net.URI; diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MeResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MeResourceTest.java index d83cea50a0..cd2a172c1b 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MeResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MeResourceTest.java @@ -28,7 +28,6 @@ import java.net.URISyntaxException; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; import static org.mockito.MockitoAnnotations.initMocks; import static sonia.scm.api.v2.resources.DispatcherMock.createDispatcher; diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryPermissionRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryPermissionRootResourceTest.java index 33b30c5532..4472acb2c5 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryPermissionRootResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryPermissionRootResourceTest.java @@ -4,7 +4,6 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.github.sdorra.shiro.ShiroRule; import com.github.sdorra.shiro.SubjectAware; -import com.google.common.base.Throwables; import com.google.common.collect.ImmutableList; import com.google.inject.util.Providers; import de.otto.edison.hal.HalRepresentation; diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserDtoToUserMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserDtoToUserMapperTest.java index 19f247b3b2..552009b73f 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserDtoToUserMapperTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserDtoToUserMapperTest.java @@ -10,7 +10,6 @@ import sonia.scm.user.User; import java.time.Instant; import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.initMocks; public class UserDtoToUserMapperTest { diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserRootResourceTest.java index ffe1a746d5..4047dfadd2 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserRootResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserRootResourceTest.java @@ -14,7 +14,6 @@ import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.Spy; import sonia.scm.ContextEntry; import sonia.scm.NotFoundException; import sonia.scm.PageResult; diff --git a/scm-webapp/src/test/java/sonia/scm/boot/RestartServletTest.java b/scm-webapp/src/test/java/sonia/scm/boot/RestartServletTest.java index b8b538c82b..eac4a12340 100644 --- a/scm-webapp/src/test/java/sonia/scm/boot/RestartServletTest.java +++ b/scm-webapp/src/test/java/sonia/scm/boot/RestartServletTest.java @@ -2,7 +2,6 @@ package sonia.scm.boot; import com.github.legman.Subscribe; import com.google.common.base.Charsets; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; diff --git a/scm-webapp/src/test/java/sonia/scm/boot/ServletContextCleanerTest.java b/scm-webapp/src/test/java/sonia/scm/boot/ServletContextCleanerTest.java index a26cf3b215..c9d8c594b4 100644 --- a/scm-webapp/src/test/java/sonia/scm/boot/ServletContextCleanerTest.java +++ b/scm-webapp/src/test/java/sonia/scm/boot/ServletContextCleanerTest.java @@ -13,7 +13,6 @@ import java.util.Enumeration; import java.util.Set; import java.util.Vector; -import static org.junit.Assert.*; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; diff --git a/scm-webapp/src/test/java/sonia/scm/net/ahc/DefaultAdvancedHttpResponseTest.java b/scm-webapp/src/test/java/sonia/scm/net/ahc/DefaultAdvancedHttpResponseTest.java index a60c884b64..314dcdbeff 100644 --- a/scm-webapp/src/test/java/sonia/scm/net/ahc/DefaultAdvancedHttpResponseTest.java +++ b/scm-webapp/src/test/java/sonia/scm/net/ahc/DefaultAdvancedHttpResponseTest.java @@ -49,8 +49,6 @@ import org.mockito.junit.MockitoJUnitRunner; import sonia.scm.config.ScmConfiguration; -import static org.hamcrest.Matchers.*; - import static org.junit.Assert.*; import static org.mockito.Mockito.*; diff --git a/scm-webapp/src/test/java/sonia/scm/plugin/MultiParentClassLoaderTest.java b/scm-webapp/src/test/java/sonia/scm/plugin/MultiParentClassLoaderTest.java index ae65f5c1ae..df31977de1 100644 --- a/scm-webapp/src/test/java/sonia/scm/plugin/MultiParentClassLoaderTest.java +++ b/scm-webapp/src/test/java/sonia/scm/plugin/MultiParentClassLoaderTest.java @@ -29,9 +29,6 @@ package sonia.scm.plugin; -import com.google.common.base.Enums; -import com.google.common.collect.Iterables; -import com.google.common.collect.Iterators; import java.io.IOException; import java.net.URL; import java.util.Arrays; diff --git a/scm-webapp/src/test/java/sonia/scm/schedule/QuartzTaskTest.java b/scm-webapp/src/test/java/sonia/scm/schedule/QuartzTaskTest.java index efaeb702fe..baf4c659cc 100644 --- a/scm-webapp/src/test/java/sonia/scm/schedule/QuartzTaskTest.java +++ b/scm-webapp/src/test/java/sonia/scm/schedule/QuartzTaskTest.java @@ -32,12 +32,11 @@ package sonia.scm.schedule; import org.junit.Test; -import static org.junit.Assert.*; + import static org.mockito.Mockito.*; -import static org.hamcrest.Matchers.*; + import org.junit.Before; import org.junit.runner.RunWith; -import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import org.quartz.JobKey; diff --git a/scm-webapp/src/test/java/sonia/scm/security/BearerRealmTest.java b/scm-webapp/src/test/java/sonia/scm/security/BearerRealmTest.java index 5c7aa08f37..c2d75358fd 100644 --- a/scm-webapp/src/test/java/sonia/scm/security/BearerRealmTest.java +++ b/scm-webapp/src/test/java/sonia/scm/security/BearerRealmTest.java @@ -33,18 +33,13 @@ package sonia.scm.security; import com.google.common.collect.ImmutableSet; import org.apache.shiro.authc.AuthenticationInfo; -import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.authc.UsernamePasswordToken; -import org.junit.Ignore; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Answers; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.invocation.InvocationOnMock; import org.mockito.junit.jupiter.MockitoExtension; -import org.mockito.stubbing.Answer; import java.util.HashMap; import java.util.Set; @@ -52,7 +47,6 @@ import java.util.Set; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; diff --git a/scm-webapp/src/test/java/sonia/scm/security/SecureKeyResolverTest.java b/scm-webapp/src/test/java/sonia/scm/security/SecureKeyResolverTest.java index cce3fea2b1..f59991f2cc 100644 --- a/scm-webapp/src/test/java/sonia/scm/security/SecureKeyResolverTest.java +++ b/scm-webapp/src/test/java/sonia/scm/security/SecureKeyResolverTest.java @@ -47,7 +47,6 @@ import sonia.scm.store.ConfigurationEntryStoreFactory; import java.util.Random; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.in; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertSame; diff --git a/scm-webapp/src/test/java/sonia/scm/user/DefaultUserManagerTest.java b/scm-webapp/src/test/java/sonia/scm/user/DefaultUserManagerTest.java index 8e261b75cc..ab31d751fd 100644 --- a/scm-webapp/src/test/java/sonia/scm/user/DefaultUserManagerTest.java +++ b/scm-webapp/src/test/java/sonia/scm/user/DefaultUserManagerTest.java @@ -45,9 +45,6 @@ import org.junit.Test; import org.mockito.ArgumentCaptor; import sonia.scm.NotFoundException; -import sonia.scm.repository.InitialRepositoryLocationResolver; -import sonia.scm.repository.RepositoryDAO; -import sonia.scm.repository.RepositoryLocationResolver; import sonia.scm.store.JAXBConfigurationStoreFactory; import sonia.scm.user.xml.XmlUserDAO; diff --git a/scm-webapp/src/test/java/sonia/scm/web/i18n/I18nServletTest.java b/scm-webapp/src/test/java/sonia/scm/web/i18n/I18nServletTest.java index a912f738e2..7679056758 100644 --- a/scm-webapp/src/test/java/sonia/scm/web/i18n/I18nServletTest.java +++ b/scm-webapp/src/test/java/sonia/scm/web/i18n/I18nServletTest.java @@ -37,8 +37,6 @@ import java.util.Enumeration; import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.*; @RunWith(MockitoJUnitRunner.Silent.class) From 9cc9e0937e36110d47872b60cdeb624ad2d07294 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra <sebastian.sdorra@cloudogu.com> Date: Wed, 30 Jan 2019 15:23:34 +0100 Subject: [PATCH 045/120] update jackson to version 2.9.8 --- pom.xml | 2 +- .../sonia/scm/net/ahc/DefaultAdvancedHttpResponseTest.java | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index cae4aa9bb6..a40f09c441 100644 --- a/pom.xml +++ b/pom.xml @@ -829,7 +829,7 @@ <resteasy.version>3.6.2.Final</resteasy.version> <jersey-client.version>1.19.4</jersey-client.version> <enunciate.version>2.11.1</enunciate.version> - <jackson.version>2.8.6</jackson.version> + <jackson.version>2.9.8</jackson.version> <guice.version>4.0</guice.version> <jaxb.version>2.3.0</jaxb.version> diff --git a/scm-webapp/src/test/java/sonia/scm/net/ahc/DefaultAdvancedHttpResponseTest.java b/scm-webapp/src/test/java/sonia/scm/net/ahc/DefaultAdvancedHttpResponseTest.java index 314dcdbeff..399f20cd3f 100644 --- a/scm-webapp/src/test/java/sonia/scm/net/ahc/DefaultAdvancedHttpResponseTest.java +++ b/scm-webapp/src/test/java/sonia/scm/net/ahc/DefaultAdvancedHttpResponseTest.java @@ -41,6 +41,7 @@ import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.common.io.ByteSource; +import org.hamcrest.Matchers; import org.junit.Test; import org.junit.runner.RunWith; @@ -134,7 +135,7 @@ public class DefaultAdvancedHttpResponseTest connection, 200, "OK"); Multimap<String, String> headers = response.getHeaders(); - assertThat(headers.get("Test"), contains("One", "Two")); + assertThat(headers.get("Test"), Matchers.contains("One", "Two")); assertTrue(headers.get("Test-2").isEmpty()); } @@ -142,8 +143,7 @@ public class DefaultAdvancedHttpResponseTest /** Field description */ private final DefaultAdvancedHttpClient client = - new DefaultAdvancedHttpClient(new ScmConfiguration(), - new HashSet<ContentTransformer>(), new SSLContextProvider()); + new DefaultAdvancedHttpClient(new ScmConfiguration(), new HashSet<>(), new SSLContextProvider()); /** Field description */ @Mock From 470ef7aaf464fb1425421318d5a9492c8afd55fe Mon Sep 17 00:00:00 2001 From: Mohamed Karray <mohamed.karray.sr@gmail.com> Date: Wed, 30 Jan 2019 17:57:58 +0100 Subject: [PATCH 046/120] move the ChangesetDtoMapper to core --- .../resources/ChangesetToChangesetDtoMapper.java | 14 ++++++++++++++ ...a => DefaultChangesetToChangesetDtoMapper.java} | 2 +- .../sonia/scm/api/v2/resources/MapperModule.java | 2 +- .../api/v2/resources/BranchRootResourceTest.java | 2 +- .../ChangesetCollectionToDtoMapperTest.java | 3 +-- .../v2/resources/ChangesetRootResourceTest.java | 2 +- .../api/v2/resources/FileHistoryResourceTest.java | 2 +- .../api/v2/resources/IncomingRootResourceTest.java | 2 +- 8 files changed, 21 insertions(+), 8 deletions(-) create mode 100644 scm-core/src/main/java/sonia/scm/api/v2/resources/ChangesetToChangesetDtoMapper.java rename scm-webapp/src/main/java/sonia/scm/api/v2/resources/{ChangesetToChangesetDtoMapper.java => DefaultChangesetToChangesetDtoMapper.java} (94%) diff --git a/scm-core/src/main/java/sonia/scm/api/v2/resources/ChangesetToChangesetDtoMapper.java b/scm-core/src/main/java/sonia/scm/api/v2/resources/ChangesetToChangesetDtoMapper.java new file mode 100644 index 0000000000..cd7d7ecebe --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/api/v2/resources/ChangesetToChangesetDtoMapper.java @@ -0,0 +1,14 @@ +package sonia.scm.api.v2.resources; + +import org.mapstruct.Context; +import org.mapstruct.Mapping; +import sonia.scm.repository.Changeset; +import sonia.scm.repository.Repository; + +public interface ChangesetToChangesetDtoMapper { + + @Mapping(target = "attributes", ignore = true) // We do not map HAL attributes + ChangesetDto map(Changeset changeset, @Context Repository repository); + + +} diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetToChangesetDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/DefaultChangesetToChangesetDtoMapper.java similarity index 94% rename from scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetToChangesetDtoMapper.java rename to scm-webapp/src/main/java/sonia/scm/api/v2/resources/DefaultChangesetToChangesetDtoMapper.java index 219062d320..924209d3da 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetToChangesetDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/DefaultChangesetToChangesetDtoMapper.java @@ -23,7 +23,7 @@ import static de.otto.edison.hal.Link.link; import static de.otto.edison.hal.Links.linkingTo; @Mapper -public abstract class ChangesetToChangesetDtoMapper extends LinkAppenderMapper implements InstantAttributeMapper { +public abstract class DefaultChangesetToChangesetDtoMapper extends LinkAppenderMapper implements InstantAttributeMapper , ChangesetToChangesetDtoMapper { @Inject private RepositoryServiceFactory serviceFactory; diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MapperModule.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MapperModule.java index 4cf66f7b28..e5142dc9fa 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MapperModule.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MapperModule.java @@ -28,7 +28,7 @@ public class MapperModule extends AbstractModule { bind(PermissionDtoToPermissionMapper.class).to(Mappers.getMapper(PermissionDtoToPermissionMapper.class).getClass()); bind(RepositoryPermissionToRepositoryPermissionDtoMapper.class).to(Mappers.getMapper(RepositoryPermissionToRepositoryPermissionDtoMapper.class).getClass()); - bind(ChangesetToChangesetDtoMapper.class).to(Mappers.getMapper(ChangesetToChangesetDtoMapper.class).getClass()); + bind(ChangesetToChangesetDtoMapper.class).to(Mappers.getMapper(DefaultChangesetToChangesetDtoMapper.class).getClass()); bind(ChangesetToParentDtoMapper.class).to(Mappers.getMapper(ChangesetToParentDtoMapper.class).getClass()); bind(TagToTagDtoMapper.class).to(Mappers.getMapper(TagToTagDtoMapper.class).getClass()); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/BranchRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/BranchRootResourceTest.java index 4994c11b08..9216922e19 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/BranchRootResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/BranchRootResourceTest.java @@ -82,7 +82,7 @@ public class BranchRootResourceTest extends RepositoryTestBase { @InjectMocks - private ChangesetToChangesetDtoMapperImpl changesetToChangesetDtoMapper; + private DefaultChangesetToChangesetDtoMapperImpl changesetToChangesetDtoMapper; private final Subject subject = mock(Subject.class); private final ThreadState subjectThreadState = new SubjectThreadState(subject); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ChangesetCollectionToDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ChangesetCollectionToDtoMapperTest.java index 69695279e6..7653fbe122 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ChangesetCollectionToDtoMapperTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ChangesetCollectionToDtoMapperTest.java @@ -1,6 +1,5 @@ package sonia.scm.api.v2.resources; -import org.assertj.core.api.Assertions; import org.junit.Test; import sonia.scm.PageResult; import sonia.scm.repository.Changeset; @@ -17,7 +16,7 @@ public class ChangesetCollectionToDtoMapperTest { public static final Repository REPOSITORY = new Repository("", "git", "space", "name"); public static final Changeset CHANGESET = new Changeset(); - private final ChangesetToChangesetDtoMapper changesetToChangesetDtoMapper = mock(ChangesetToChangesetDtoMapper.class); + private final DefaultChangesetToChangesetDtoMapperImpl changesetToChangesetDtoMapper = mock(DefaultChangesetToChangesetDtoMapperImpl.class); private final ChangesetCollectionToDtoMapper changesetCollectionToDtoMapper = new ChangesetCollectionToDtoMapper(changesetToChangesetDtoMapper, ResourceLinksMock.createMock(URI.create("/"))); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ChangesetRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ChangesetRootResourceTest.java index d5c0f91f81..952c8504f6 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ChangesetRootResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ChangesetRootResourceTest.java @@ -65,7 +65,7 @@ public class ChangesetRootResourceTest extends RepositoryTestBase { private ChangesetCollectionToDtoMapper changesetCollectionToDtoMapper; @InjectMocks - private ChangesetToChangesetDtoMapperImpl changesetToChangesetDtoMapper; + private DefaultChangesetToChangesetDtoMapperImpl changesetToChangesetDtoMapper; private ChangesetRootResource changesetRootResource; diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileHistoryResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileHistoryResourceTest.java index 52c9a434c0..a8b3c15158 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileHistoryResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileHistoryResourceTest.java @@ -66,7 +66,7 @@ public class FileHistoryResourceTest extends RepositoryTestBase { private FileHistoryCollectionToDtoMapper fileHistoryCollectionToDtoMapper; @InjectMocks - private ChangesetToChangesetDtoMapperImpl changesetToChangesetDtoMapper; + private DefaultChangesetToChangesetDtoMapperImpl changesetToChangesetDtoMapper; private FileHistoryRootResource fileHistoryRootResource; diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/IncomingRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/IncomingRootResourceTest.java index e4495a0455..b965c2f2c3 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/IncomingRootResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/IncomingRootResourceTest.java @@ -74,7 +74,7 @@ public class IncomingRootResourceTest extends RepositoryTestBase { private IncomingChangesetCollectionToDtoMapper incomingChangesetCollectionToDtoMapper; @InjectMocks - private ChangesetToChangesetDtoMapperImpl changesetToChangesetDtoMapper; + private DefaultChangesetToChangesetDtoMapperImpl changesetToChangesetDtoMapper; private IncomingRootResource incomingRootResource; From e2d089e9867d243e58bff7ddf65c6a77409a667a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maren=20S=C3=BCwer?= <maren.suewer@cloudogu.com> Date: Thu, 31 Jan 2019 10:50:15 +0100 Subject: [PATCH 047/120] change classes to bulma classes --- .../ui-components/src/modals/ConfirmAlert.js | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.js b/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.js index 1f93024865..37cd849047 100644 --- a/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.js +++ b/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.js @@ -32,11 +32,15 @@ class ConfirmAlert extends React.Component<Props> { const { title, message, buttons } = this.props; return ( - <div className="react-confirm-alert-overlay"> - <div className="react-confirm-alert"> - { - <div className="react-confirm-alert-body"> - {title && <h1>{title}</h1>} + <div className="modal is-active"> + <div className="modal-card"> + + <header className="modal-card-head"> + <p className="modal-card-title"> + {title} + </p> + </header> + <section className="modal-card-body"> {message} <div className="react-confirm-alert-button-group"> {buttons.map((button, i) => ( @@ -49,8 +53,8 @@ class ConfirmAlert extends React.Component<Props> { </button> ))} </div> - </div> - } + </section> + </div> </div> ); From 7a6d5e4cc4034453a73d9b4b21c2b0e14f5d8fe7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maren=20S=C3=BCwer?= <maren.suewer@cloudogu.com> Date: Thu, 31 Jan 2019 11:01:37 +0100 Subject: [PATCH 048/120] set background --- .../packages/ui-components/src/modals/ConfirmAlert.js | 1 + 1 file changed, 1 insertion(+) diff --git a/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.js b/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.js index 37cd849047..e7501a39a6 100644 --- a/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.js +++ b/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.js @@ -33,6 +33,7 @@ class ConfirmAlert extends React.Component<Props> { return ( <div className="modal is-active"> + <div className="modal-background" /> <div className="modal-card"> <header className="modal-card-head"> From 1f6801978071744e95f136531dc05a43abdf52c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maren=20S=C3=BCwer?= <maren.suewer@cloudogu.com> Date: Thu, 31 Jan 2019 11:07:54 +0100 Subject: [PATCH 049/120] add close button --- .../packages/ui-components/src/modals/ConfirmAlert.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.js b/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.js index e7501a39a6..ad296cee6b 100644 --- a/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.js +++ b/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.js @@ -40,6 +40,11 @@ class ConfirmAlert extends React.Component<Props> { <p className="modal-card-title"> {title} </p> + <button + className="delete" + aria-label="close" + onClick={() => this.close()} + /> </header> <section className="modal-card-body"> {message} From 7962463be25ad0d5526cea7b6229326a124fd635 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra <sebastian.sdorra@cloudogu.com> Date: Thu, 31 Jan 2019 11:47:10 +0100 Subject: [PATCH 050/120] update hibernate-validator to version 5.3.6.Final, to fix CVE-2017-7536 --- scm-webapp/pom.xml | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/scm-webapp/pom.xml b/scm-webapp/pom.xml index dfaac3b4c0..ccfc312999 100644 --- a/scm-webapp/pom.xml +++ b/scm-webapp/pom.xml @@ -152,11 +152,22 @@ <version>${resteasy.version}</version> </dependency> + <dependency> + <groupId>org.hibernate</groupId> + <artifactId>hibernate-validator</artifactId> + <version>5.3.6.Final</version> + </dependency> + <dependency> <groupId>javax.el</groupId> <artifactId>javax.el-api</artifactId> - <version>3.0.1-b06</version> - <scope>provided</scope> + <version>2.2.4</version> + </dependency> + + <dependency> + <groupId>org.glassfish.web</groupId> + <artifactId>javax.el</artifactId> + <version>2.2.4</version> </dependency> <!-- injection --> From c388bb761e31eace4c5b497a54c455df4c4e225e Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra <sebastian.sdorra@cloudogu.com> Date: Thu, 31 Jan 2019 11:53:16 +0100 Subject: [PATCH 051/120] remove duplicated declaration jackson.version --- scm-webapp/pom.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/scm-webapp/pom.xml b/scm-webapp/pom.xml index ccfc312999..06971f713d 100644 --- a/scm-webapp/pom.xml +++ b/scm-webapp/pom.xml @@ -573,7 +573,6 @@ <selenium.version>2.53.1</selenium.version> <wagon.version>1.0</wagon.version> <mustache.version>0.8.17</mustache.version> - <jackson.version>2.8.9</jackson.version> <netbeans.hint.deploy.server>Tomcat</netbeans.hint.deploy.server> <sonar.issue.ignore.multicriteria>e1</sonar.issue.ignore.multicriteria> <sonar.issue.ignore.multicriteria.e1.ruleKey>javascript:S3827</sonar.issue.ignore.multicriteria.e1.ruleKey> From 145ac7a8d80742a364290629b52c4f5cd7831953 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra <sebastian.sdorra@cloudogu.com> Date: Thu, 31 Jan 2019 15:53:19 +0100 Subject: [PATCH 052/120] remove direct dependencies to apache beanutils and commons collections --- pom.xml | 15 ---- .../scm/repository/RepositoryPermission.java | 4 +- .../main/java/sonia/scm/util/Comparables.java | 88 ++++++++++++++++++ .../java/sonia/scm/util/ComparablesTest.java | 57 ++++++++++++ scm-webapp/pom.xml | 12 --- .../resources/AbstractManagerResource.java | 89 ++----------------- .../sonia/scm/security/RepositoryRole.java | 7 +- 7 files changed, 156 insertions(+), 116 deletions(-) create mode 100644 scm-core/src/main/java/sonia/scm/util/Comparables.java create mode 100644 scm-core/src/test/java/sonia/scm/util/ComparablesTest.java diff --git a/pom.xml b/pom.xml index a40f09c441..a7108df873 100644 --- a/pom.xml +++ b/pom.xml @@ -351,21 +351,6 @@ <scope>test</scope> </dependency> - - <!-- utils --> - - <dependency> - <groupId>commons-beanutils</groupId> - <artifactId>commons-beanutils</artifactId> - <version>1.9.3</version> - </dependency> - - <dependency> - <groupId>commons-collections</groupId> - <artifactId>commons-collections</artifactId> - <version>3.2.2</version> - </dependency> - <!-- http --> <dependency> diff --git a/scm-core/src/main/java/sonia/scm/repository/RepositoryPermission.java b/scm-core/src/main/java/sonia/scm/repository/RepositoryPermission.java index 9e132ef93c..5fd14f2e84 100644 --- a/scm-core/src/main/java/sonia/scm/repository/RepositoryPermission.java +++ b/scm-core/src/main/java/sonia/scm/repository/RepositoryPermission.java @@ -37,7 +37,6 @@ package sonia.scm.repository; import com.google.common.base.MoreObjects; import com.google.common.base.Objects; -import org.apache.commons.collections.CollectionUtils; import sonia.scm.security.PermissionObject; import javax.xml.bind.annotation.XmlAccessType; @@ -109,7 +108,8 @@ public class RepositoryPermission implements PermissionObject, Serializable final RepositoryPermission other = (RepositoryPermission) obj; return Objects.equal(name, other.name) - && CollectionUtils.isEqualCollection(verbs, other.verbs) + && verbs.containsAll(other.verbs) + && verbs.size() == other.verbs.size() && Objects.equal(groupPermission, other.groupPermission); } diff --git a/scm-core/src/main/java/sonia/scm/util/Comparables.java b/scm-core/src/main/java/sonia/scm/util/Comparables.java new file mode 100644 index 0000000000..b760021c07 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/util/Comparables.java @@ -0,0 +1,88 @@ +package sonia.scm.util; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; + +import java.beans.BeanInfo; +import java.beans.IntrospectionException; +import java.beans.Introspector; +import java.beans.PropertyDescriptor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Optional; + +import static com.google.common.base.Preconditions.checkArgument; + +public final class Comparables { + + private static final CacheLoader<Class, BeanInfo> beanInfoCacheLoader = new CacheLoader<Class, BeanInfo>() { + @Override + public BeanInfo load(Class type) throws IntrospectionException { + return Introspector.getBeanInfo(type); + } + }; + + private static final LoadingCache<Class, BeanInfo> beanInfoCache = CacheBuilder.newBuilder() + .maximumSize(50) // limit the cache to avoid consuming to much memory on miss usage + .build(beanInfoCacheLoader); + + private Comparables() { + } + + public static <T> Comparator<T> comparator(Class<T> type, String sortBy) { + BeanInfo info = createBeanInfo(type); + PropertyDescriptor propertyDescriptor = findPropertyDescriptor(sortBy, info); + + Method readMethod = propertyDescriptor.getReadMethod(); + checkIfPropertyIsComparable(readMethod, sortBy); + + return new MethodComparator<>(readMethod); + } + + private static void checkIfPropertyIsComparable(Method readMethod, String sortBy) { + checkArgument(isReturnTypeComparable(readMethod), "property %s is not comparable", sortBy); + } + + private static boolean isReturnTypeComparable(Method readMethod) { + return Comparable.class.isAssignableFrom(readMethod.getReturnType()); + } + + private static PropertyDescriptor findPropertyDescriptor(String sortBy, BeanInfo info) { + PropertyDescriptor[] propertyDescriptors = info.getPropertyDescriptors(); + + Optional<PropertyDescriptor> optional = Arrays.stream(propertyDescriptors) + .filter(p -> p.getName().equals(sortBy)) + .findFirst(); + + return optional.orElseThrow(() -> new IllegalArgumentException("could not find property " + sortBy)); + } + + private static <T> BeanInfo createBeanInfo(Class<T> type) { + return beanInfoCache.getUnchecked(type); + } + + private static class MethodComparator<T> implements Comparator<T> { + + private final Method readMethod; + + private MethodComparator(Method readMethod) { + this.readMethod = readMethod; + } + + @Override + @SuppressWarnings("unchecked") + public int compare(T left, T right) { + try { + Comparable leftResult = (Comparable) readMethod.invoke(left); + Comparable rightResult = (Comparable) readMethod.invoke(right); + return leftResult.compareTo(rightResult); + } catch (IllegalAccessException | InvocationTargetException ex) { + throw new IllegalArgumentException("failed to invoke read method", ex); + } + } + } + +} diff --git a/scm-core/src/test/java/sonia/scm/util/ComparablesTest.java b/scm-core/src/test/java/sonia/scm/util/ComparablesTest.java new file mode 100644 index 0000000000..50ae2254e6 --- /dev/null +++ b/scm-core/src/test/java/sonia/scm/util/ComparablesTest.java @@ -0,0 +1,57 @@ +package sonia.scm.util; + +import org.junit.jupiter.api.Test; + +import java.util.Comparator; + +import static org.assertj.core.api.Java6Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class ComparablesTest { + + @Test + void shouldCompare() { + One a = new One("a"); + One b = new One("b"); + + Comparator<One> comparable = Comparables.comparator(One.class, "value"); + assertThat(comparable.compare(a, b)).isEqualTo(-1); + } + + @Test + void shouldThrowAnExceptionForNonExistingField() { + assertThrows(IllegalArgumentException.class, () -> Comparables.comparator(One.class, "awesome")); + } + + @Test + void shouldThrowAnExceptionForNonComparableField() { + assertThrows(IllegalArgumentException.class, () -> Comparables.comparator(One.class, "nonComparable")); + } + + @Test + void shouldThrowAnExceptionIfTheFieldHasNoGetter() { + assertThrows(IllegalArgumentException.class, () -> Comparables.comparator(One.class, "incredible")); + } + + private static class One { + + private String value; + private String incredible; + private NonComparable nonComparable; + + One(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + public NonComparable getNonComparable() { + return nonComparable; + } + } + + private static class NonComparable {} + +} diff --git a/scm-webapp/pom.xml b/scm-webapp/pom.xml index 06971f713d..9f7e311820 100644 --- a/scm-webapp/pom.xml +++ b/scm-webapp/pom.xml @@ -215,18 +215,6 @@ <version>1.4.01</version> </dependency> - <!-- only for BeanComparator, replace with own implementation --> - - <dependency> - <groupId>commons-beanutils</groupId> - <artifactId>commons-beanutils</artifactId> - </dependency> - - <dependency> - <groupId>commons-collections</groupId> - <artifactId>commons-collections</artifactId> - </dependency> - <!-- fix installation of httpasswd-plugin https://groups.google.com/d/topic/scmmanager/eN7UtG8TwW8/discussion diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/AbstractManagerResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/AbstractManagerResource.java index 4253c456fb..dfc0bd2a5d 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/AbstractManagerResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/AbstractManagerResource.java @@ -37,7 +37,6 @@ package sonia.scm.api.rest.resources; import com.google.common.annotations.VisibleForTesting; import com.google.common.net.UrlEscapers; -import org.apache.commons.beanutils.BeanComparator; import org.apache.shiro.authz.AuthorizationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,6 +46,7 @@ import sonia.scm.ModelObject; import sonia.scm.PageResult; import sonia.scm.api.rest.RestExceptionResult; import sonia.scm.util.AssertUtil; +import sonia.scm.util.Comparables; import sonia.scm.util.Util; import javax.ws.rs.core.CacheControl; @@ -56,15 +56,10 @@ import javax.ws.rs.core.Request; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.UriInfo; -import java.beans.BeanInfo; -import java.beans.IntrospectionException; -import java.beans.Introspector; -import java.beans.PropertyDescriptor; -import java.util.Arrays; +import java.net.URI; import java.util.Collection; import java.util.Comparator; import java.util.Date; -import java.net.URI; //~--- JDK imports ------------------------------------------------------------ @@ -510,21 +505,11 @@ public abstract class AbstractManagerResource<T extends ModelObject> { return builder.build(); } - @SuppressWarnings("unchecked") - private Comparator<T> createComparator(String sortBy, boolean desc) - { - checkSortByField(sortBy); - Comparator comparator; - - if (desc) - { - comparator = new BeanReverseComparator(sortBy); + private Comparator<T> createComparator(String sortBy, boolean desc) { + Comparator<T> comparator = Comparables.comparator(type, sortBy); + if (desc) { + comparator = comparator.reversed(); } - else - { - comparator = new BeanComparator(sortBy); - } - return comparator; } @@ -558,21 +543,6 @@ public abstract class AbstractManagerResource<T extends ModelObject> { return items; } - // We have to handle IntrospectionException here, because it's a checked exception - // It shouldn't occur really - so creating a new unchecked exception would be over-engineered here - @SuppressWarnings("squid:S00112") - private void checkSortByField(String sortBy) { - try { - BeanInfo info = Introspector.getBeanInfo(type); - PropertyDescriptor[] pds = info.getPropertyDescriptors(); - if (Arrays.stream(pds).noneMatch(p -> p.getName().equals(sortBy))) { - throw new IllegalArgumentException("sortBy"); - } - } catch (IntrospectionException e) { - throw new RuntimeException("error introspecting model type " + type.getName(), e); - } - } - protected PageResult<T> fetchPage(String sortBy, boolean desc, int pageNumber, int pageSize) { AssertUtil.assertPositive(pageNumber); @@ -608,51 +578,4 @@ public abstract class AbstractManagerResource<T extends ModelObject> { return lastModified; } - - //~--- inner classes -------------------------------------------------------- - - /** - * Class description - * - * - * @version Enter version here..., 11/06/09 - * @author Enter your name here... - */ - private static class BeanReverseComparator extends BeanComparator - { - - /** Field description */ - private static final long serialVersionUID = -8535047820348790009L; - - //~--- constructors ------------------------------------------------------- - - /** - * Constructs ... - * - * - * @param sortby - */ - private BeanReverseComparator(String sortby) - { - super(sortby); - } - - //~--- methods ------------------------------------------------------------ - - /** - * Method description - * - * - * @param o1 - * @param o2 - * - * @return - */ - @Override - @SuppressWarnings("unchecked") - public int compare(Object o1, Object o2) - { - return super.compare(o1, o2) * -1; - } - } } diff --git a/scm-webapp/src/main/java/sonia/scm/security/RepositoryRole.java b/scm-webapp/src/main/java/sonia/scm/security/RepositoryRole.java index 6b6b06aa9c..1fab500d79 100644 --- a/scm-webapp/src/main/java/sonia/scm/security/RepositoryRole.java +++ b/scm-webapp/src/main/java/sonia/scm/security/RepositoryRole.java @@ -1,7 +1,5 @@ package sonia.scm.security; -import org.apache.commons.collections.CollectionUtils; - import java.util.Collection; import java.util.Collections; import java.util.Objects; @@ -33,8 +31,9 @@ public class RepositoryRole { if (this == o) return true; if (!(o instanceof RepositoryRole)) return false; RepositoryRole that = (RepositoryRole) o; - return name.equals(that.name) && - CollectionUtils.isEqualCollection(this.verbs, that.verbs); + return name.equals(that.name) + && this.verbs.containsAll(that.verbs) + && this.verbs.size() == that.verbs.size(); } @Override From 614656a8b5e1f5cbba9e0ddae7bf49ab0561185f Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra <sebastian.sdorra@cloudogu.com> Date: Thu, 31 Jan 2019 16:20:00 +0100 Subject: [PATCH 053/120] fix wrong version of jackson-jaxrs-json-provider --- scm-webapp/pom.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scm-webapp/pom.xml b/scm-webapp/pom.xml index 9f7e311820..45da9fec49 100644 --- a/scm-webapp/pom.xml +++ b/scm-webapp/pom.xml @@ -114,6 +114,12 @@ <version>${jackson.version}</version> </dependency> + <dependency> + <groupId>com.fasterxml.jackson.jaxrs</groupId> + <artifactId>jackson-jaxrs-json-provider</artifactId> + <version>${jackson.version}</version> + </dependency> + <!-- rest api --> <dependency> From 9e9b3b14d7a6a3291bc65ef4c721e26da41cff66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maren=20S=C3=BCwer?= <maren.suewer@cloudogu.com> Date: Fri, 1 Feb 2019 08:47:38 +0100 Subject: [PATCH 054/120] add extra root for modals and use it --- .../ui-components/src/modals/ConfirmAlert.js | 30 ++++--------------- scm-ui/public/index.mustache | 2 ++ 2 files changed, 8 insertions(+), 24 deletions(-) diff --git a/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.js b/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.js index ad296cee6b..416853cffa 100644 --- a/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.js +++ b/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.js @@ -1,8 +1,6 @@ // @flow -//modified from https://github.com/GA-MO/react-confirm-alert - import * as React from "react"; -import { render, unmountComponentAtNode } from "react-dom"; +import ReactDOM from "react-dom"; import "./ConfirmAlert.css"; type Button = { @@ -25,7 +23,7 @@ class ConfirmAlert extends React.Component<Props> { }; close = () => { - removeElementReconfirm(); + ReactDOM.unmountComponentAtNode(document.getElementById("modalRoot")); }; render() { @@ -67,27 +65,11 @@ class ConfirmAlert extends React.Component<Props> { } } -function createElementReconfirm(properties: Props) { - const divTarget = document.createElement("div"); - divTarget.id = "react-confirm-alert"; - if (document.body) { - document.body.appendChild(divTarget); - render(<ConfirmAlert {...properties} />, divTarget); - } -} - -function removeElementReconfirm() { - const target = document.getElementById("react-confirm-alert"); - if (target) { - unmountComponentAtNode(target); - if (target.parentNode) { - target.parentNode.removeChild(target); - } - } -} - export function confirmAlert(properties: Props) { - createElementReconfirm(properties); + const root = document.getElementById("modalRoot"); + if(root){ + ReactDOM.render(<ConfirmAlert {...properties}/>, root); + } } export default ConfirmAlert; diff --git a/scm-ui/public/index.mustache b/scm-ui/public/index.mustache index 590b5e3cdb..75efc78088 100644 --- a/scm-ui/public/index.mustache +++ b/scm-ui/public/index.mustache @@ -21,6 +21,8 @@ You need to enable JavaScript to run this app. </noscript> <div id="root"></div> + <div id="modalRoot"></div> + <!-- This HTML file is a template. If you open it directly in the browser, you will see an empty page. From 7bd69f754bb1778e17810f0707650f0ed4b01008 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maren=20S=C3=BCwer?= <maren.suewer@cloudogu.com> Date: Fri, 1 Feb 2019 09:43:14 +0100 Subject: [PATCH 055/120] use bulma classes --- .../ui-components/src/modals/ConfirmAlert.css | 102 ------------------ .../ui-components/src/modals/ConfirmAlert.js | 14 ++- 2 files changed, 6 insertions(+), 110 deletions(-) delete mode 100644 scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.css diff --git a/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.css b/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.css deleted file mode 100644 index 96246a44a5..0000000000 --- a/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.css +++ /dev/null @@ -1,102 +0,0 @@ -/*modified from https://github.com/GA-MO/react-confirm-alert*/ -.react-confirm-alert-overlay { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - z-index: 99; - background: rgba(255, 255, 255, 0.9); - display: -webkit-flex; - display: -moz-flex; - display: -ms-flex; - display: -o-flex; - display: flex; - justify-content: center; - -ms-align-items: center; - align-items: center; - opacity: 0; - -webkit-animation: react-confirm-alert-fadeIn 0.5s 0.2s forwards; - -moz-animation: react-confirm-alert-fadeIn 0.5s 0.2s forwards; - -o-animation: react-confirm-alert-fadeIn 0.5s 0.2s forwards; - animation: react-confirm-alert-fadeIn 0.5s 0.2s forwards; -} - -.react-confirm-alert-body { - font-family: Arial, Helvetica, sans-serif; - width: 400px; - padding: 30px; - text-align: left; - background: #fff; - border-radius: 10px; - box-shadow: 0 20px 75px rgba(0, 0, 0, 0.13); - color: #666; -} - -.react-confirm-alert-body > h1 { - margin-top: 0; -} - -.react-confirm-alert-body > h3 { - margin: 0; - font-size: 16px; -} - -.react-confirm-alert-button-group { - display: -webkit-flex; - display: -moz-flex; - display: -ms-flex; - display: -o-flex; - display: flex; - justify-content: flex-start; - margin-top: 20px; -} - -.react-confirm-alert-button-group > button { - outline: none; - background: #333; - border: none; - display: inline-block; - padding: 6px 18px; - color: #eee; - margin-right: 10px; - border-radius: 5px; - font-size: 12px; - cursor: pointer; -} - -@-webkit-keyframes react-confirm-alert-fadeIn { - from { - opacity: 0; - } - to { - opacity: 1; - } -} - -@-moz-keyframes react-confirm-alert-fadeIn { - from { - opacity: 0; - } - to { - opacity: 1; - } -} - -@-o-keyframes react-confirm-alert-fadeIn { - from { - opacity: 0; - } - to { - opacity: 1; - } -} - -@keyframes react-confirm-alert-fadeIn { - from { - opacity: 0; - } - to { - opacity: 1; - } -} diff --git a/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.js b/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.js index 416853cffa..16d3d501b6 100644 --- a/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.js +++ b/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.js @@ -1,9 +1,8 @@ // @flow import * as React from "react"; import ReactDOM from "react-dom"; -import "./ConfirmAlert.css"; -type Button = { +type ButtonType = { label: string, onClick: () => void | null }; @@ -11,11 +10,11 @@ type Button = { type Props = { title: string, message: string, - buttons: Button[] + buttons: ButtonType[] }; class ConfirmAlert extends React.Component<Props> { - handleClickButton = (button: Button) => { + handleClickButton = (button: ButtonType) => { if (button.onClick) { button.onClick(); } @@ -46,15 +45,14 @@ class ConfirmAlert extends React.Component<Props> { </header> <section className="modal-card-body"> {message} - <div className="react-confirm-alert-button-group"> + <div className="buttons"> {buttons.map((button, i) => ( - <button + <a className="button" key={i} onClick={() => this.handleClickButton(button)} - href="javascript:void(0);" > {button.label} - </button> + </a> ))} </div> </section> From 87ffe37cd6e0fef732ca57b4be7f324e2b468e88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maren=20S=C3=BCwer?= <maren.suewer@cloudogu.com> Date: Fri, 1 Feb 2019 09:47:03 +0100 Subject: [PATCH 056/120] improve styling --- .../packages/ui-components/src/modals/ConfirmAlert.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.js b/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.js index 16d3d501b6..fd95417d32 100644 --- a/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.js +++ b/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.js @@ -45,9 +45,9 @@ class ConfirmAlert extends React.Component<Props> { </header> <section className="modal-card-body"> {message} - <div className="buttons"> + <div className="buttons is-right"> {buttons.map((button, i) => ( - <a className="button" + <a className="button is-info is-right" key={i} onClick={() => this.handleClickButton(button)} > From 782cdb996fde06ca97f4c2de0af743c3dff30e39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maren=20S=C3=BCwer?= <maren.suewer@cloudogu.com> Date: Fri, 1 Feb 2019 10:02:19 +0100 Subject: [PATCH 057/120] create modal component --- .../ui-components/src/modals/ConfirmAlert.js | 56 +++++++++---------- .../ui-components/src/modals/Modal.js | 44 +++++++++++++++ .../ui-components/src/modals/index.js | 1 + 3 files changed, 71 insertions(+), 30 deletions(-) create mode 100644 scm-ui-components/packages/ui-components/src/modals/Modal.js diff --git a/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.js b/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.js index fd95417d32..e9ecffefd9 100644 --- a/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.js +++ b/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.js @@ -1,6 +1,7 @@ // @flow import * as React from "react"; import ReactDOM from "react-dom"; +import Modal from "./Modal"; type ButtonType = { label: string, @@ -28,37 +29,32 @@ class ConfirmAlert extends React.Component<Props> { render() { const { title, message, buttons } = this.props; - return ( - <div className="modal is-active"> - <div className="modal-background" /> - <div className="modal-card"> - - <header className="modal-card-head"> - <p className="modal-card-title"> - {title} - </p> - <button - className="delete" - aria-label="close" - onClick={() => this.close()} - /> - </header> - <section className="modal-card-body"> - {message} - <div className="buttons is-right"> - {buttons.map((button, i) => ( - <a className="button is-info is-right" - key={i} - onClick={() => this.handleClickButton(button)} - > - {button.label} - </a> - ))} - </div> - </section> - + const closeButton = ( + <button + className="delete" + aria-label="close" + onClick={() => this.close()} + /> + ); + const body= ( + <> + {message} + <div className="buttons is-right"> + {buttons.map((button, i) => ( + <a className="button is-info is-right" + key={i} + onClick={() => this.handleClickButton(button)} + > + {button.label} + </a> + ))} </div> - </div> + </> + ); + + + return ( + <Modal title={title} closeButton={closeButton} body={body} active={true}/> ); } } diff --git a/scm-ui-components/packages/ui-components/src/modals/Modal.js b/scm-ui-components/packages/ui-components/src/modals/Modal.js new file mode 100644 index 0000000000..2638eb13fa --- /dev/null +++ b/scm-ui-components/packages/ui-components/src/modals/Modal.js @@ -0,0 +1,44 @@ +// @flow +import * as React from "react"; +import classNames from "classnames"; + +type Props = { + title: string, + closeButton: any, + body: any, + active: boolean +}; + +class Modal extends React.Component<Props> { + + render() { + const { title, closeButton, body, active } = this.props; + + const isActive = active ? "is-active" : null; + + return ( + <div className={classNames( + "modal", + isActive + )}> + <div className="modal-background" /> + <div className="modal-card"> + + <header className="modal-card-head"> + <p className="modal-card-title"> + {title} + </p> + {closeButton} + </header> + <section className="modal-card-body"> + {body} + </section> + + </div> + </div> + ); + } +} + + +export default Modal; diff --git a/scm-ui-components/packages/ui-components/src/modals/index.js b/scm-ui-components/packages/ui-components/src/modals/index.js index e75d082c1c..13b205dd1c 100644 --- a/scm-ui-components/packages/ui-components/src/modals/index.js +++ b/scm-ui-components/packages/ui-components/src/modals/index.js @@ -1,4 +1,5 @@ // @create-index export { default as ConfirmAlert, confirmAlert } from "./ConfirmAlert.js"; +export { default as Modal } from "./Modal.js"; From a2cb0b15b7804aead6dba5ef61e19725c5b7370a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maren=20S=C3=BCwer?= <maren.suewer@cloudogu.com> Date: Fri, 1 Feb 2019 10:07:00 +0100 Subject: [PATCH 058/120] use modal component for advanced permissions dialog --- .../containers/AdvancedPermissionsDialog.js | 51 +++++++++---------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/scm-ui/src/repos/permissions/containers/AdvancedPermissionsDialog.js b/scm-ui/src/repos/permissions/containers/AdvancedPermissionsDialog.js index 0d844fdf3f..5d53605484 100644 --- a/scm-ui/src/repos/permissions/containers/AdvancedPermissionsDialog.js +++ b/scm-ui/src/repos/permissions/containers/AdvancedPermissionsDialog.js @@ -1,7 +1,7 @@ // @flow import React from "react"; -import { Button, SubmitButton } from "@scm-manager/ui-components"; +import { Button, SubmitButton, Modal } from "@scm-manager/ui-components"; import { translate } from "react-i18next"; import PermissionCheckbox from "../components/PermissionCheckbox"; @@ -38,6 +38,7 @@ class AdvancedPermissionsDialog extends React.Component<Props, State> { const verbSelectBoxes = Object.entries(verbs).map(e => ( <PermissionCheckbox + key={e[0]} disabled={readOnly} name={e[0]} checked={e[1]} @@ -49,32 +50,30 @@ class AdvancedPermissionsDialog extends React.Component<Props, State> { <SubmitButton label={t("permission.advanced.dialog.submit")} /> ) : null; + const closeButton = ( + <button className="delete" aria-label="close" onClick={() => onClose()} /> + ); + + const body = ( + <> + <div className="content">{verbSelectBoxes}</div> + <form onSubmit={this.onSubmit}> + {submitButton} + <Button + label={t("permission.advanced.dialog.abort")} + action={onClose} + /> + </form> + </> + ); + return ( - <div className={"modal is-active"}> - <div className="modal-background" /> - <div className="modal-card"> - <header className="modal-card-head"> - <p className="modal-card-title"> - {t("permission.advanced.dialog.title")} - </p> - <button - className="delete" - aria-label="close" - onClick={() => onClose()} - /> - </header> - <section className="modal-card-body"> - <div className="content">{verbSelectBoxes}</div> - <form onSubmit={this.onSubmit}> - {submitButton} - <Button - label={t("permission.advanced.dialog.abort")} - action={onClose} - /> - </form> - </section> - </div> - </div> + <Modal + title={t("permission.advanced.dialog.title")} + closeButton={closeButton} + body={body} + active={true} + /> ); } From b5f392d73efe2cdfcadb6f296d7559091c91b8c5 Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Fri, 1 Feb 2019 10:12:45 +0100 Subject: [PATCH 059/120] added sources overview --- .../src/repos/changesets/ChangesetList.js | 17 ++---- .../src/repos/sources/components/FileTree.js | 54 ++++++++++--------- .../src/repos/sources/containers/Content.js | 4 -- .../src/repos/sources/containers/Sources.js | 8 +-- .../repos/sources/containers/SourcesView.js | 17 ++---- scm-ui/styles/scm.scss | 39 +++++++------- 6 files changed, 59 insertions(+), 80 deletions(-) diff --git a/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetList.js b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetList.js index c50c8a2d50..e5fac920a2 100644 --- a/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetList.js +++ b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetList.js @@ -1,26 +1,17 @@ // @flow import ChangesetRow from "./ChangesetRow"; import React from "react"; -import injectSheet from "react-jss"; -import classNames from "classnames"; import type { Changeset, Repository } from "@scm-manager/ui-types"; type Props = { repository: Repository, - changesets: Changeset[], - classes: any -}; - -const styles = { - toCenterContent: { - display: "block" - } + changesets: Changeset[] }; class ChangesetList extends React.Component<Props> { render() { - const { repository, changesets, classes } = this.props; + const { repository, changesets } = this.props; const content = changesets.map(changeset => { return ( <ChangesetRow @@ -31,11 +22,11 @@ class ChangesetList extends React.Component<Props> { ); }); return ( - <div className={classNames("panel-block", classes.toCenterContent)}> + <div className="panel-block"> {content} </div> ); } } -export default injectSheet(styles)(ChangesetList); +export default ChangesetList; diff --git a/scm-ui/src/repos/sources/components/FileTree.js b/scm-ui/src/repos/sources/components/FileTree.js index cbe03df62f..18ef1b01c5 100644 --- a/scm-ui/src/repos/sources/components/FileTree.js +++ b/scm-ui/src/repos/sources/components/FileTree.js @@ -108,32 +108,34 @@ class FileTree extends React.Component<Props> { } return ( - <table className="table table-hover table-sm is-fullwidth"> - <thead> - <tr> - <th className={classes.iconColumn} /> - <th>{t("sources.file-tree.name")}</th> - <th className="is-hidden-mobile"> - {t("sources.file-tree.length")} - </th> - <th className="is-hidden-mobile"> - {t("sources.file-tree.lastModified")} - </th> - <th className="is-hidden-mobile"> - {t("sources.file-tree.description")} - </th> - </tr> - </thead> - <tbody> - {files.map(file => ( - <FileTreeLeaf - key={file.name} - file={file} - baseUrl={baseUrlWithRevision} - /> - ))} - </tbody> - </table> + <div className="panel-block"> + <table className="table table-hover table-sm is-fullwidth"> + <thead> + <tr> + <th className={classes.iconColumn} /> + <th>{t("sources.file-tree.name")}</th> + <th className="is-hidden-mobile"> + {t("sources.file-tree.length")} + </th> + <th className="is-hidden-mobile"> + {t("sources.file-tree.lastModified")} + </th> + <th className="is-hidden-mobile"> + {t("sources.file-tree.description")} + </th> + </tr> + </thead> + <tbody> + {files.map(file => ( + <FileTreeLeaf + key={file.name} + file={file} + baseUrl={baseUrlWithRevision} + /> + ))} + </tbody> + </table> + </div> ); } } diff --git a/scm-ui/src/repos/sources/containers/Content.js b/scm-ui/src/repos/sources/containers/Content.js index cc4aa32b5b..899e40b511 100644 --- a/scm-ui/src/repos/sources/containers/Content.js +++ b/scm-ui/src/repos/sources/containers/Content.js @@ -29,9 +29,6 @@ type State = { }; const styles = { - toCenterContent: { - display: "block" - }, pointer: { cursor: "pointer" }, @@ -126,7 +123,6 @@ class Content extends React.Component<Props, State> { <div className={classNames( "panel-block", - classes.toCenterContent, classes.hasBackground )} > diff --git a/scm-ui/src/repos/sources/containers/Sources.js b/scm-ui/src/repos/sources/containers/Sources.js index 321c1faa16..7da024b450 100644 --- a/scm-ui/src/repos/sources/containers/Sources.js +++ b/scm-ui/src/repos/sources/containers/Sources.js @@ -93,15 +93,17 @@ class Sources extends React.Component<Props> { if (currentFileIsDirectory) { return ( - <div className="has-border-around is-round"> - {this.renderBranchSelector()} + <nav className="panel"> + <article className="panel-heading"> + {this.renderBranchSelector()} + </article> <FileTree repository={repository} revision={revision} path={path} baseUrl={baseUrl} /> - </div> + </nav> ); } else { return ( diff --git a/scm-ui/src/repos/sources/containers/SourcesView.js b/scm-ui/src/repos/sources/containers/SourcesView.js index 220c6ecc27..0f729beb2e 100644 --- a/scm-ui/src/repos/sources/containers/SourcesView.js +++ b/scm-ui/src/repos/sources/containers/SourcesView.js @@ -8,15 +8,12 @@ import { ExtensionPoint } from "@scm-manager/ui-extensions"; import { getContentType } from "./contentType"; import type { File, Repository } from "@scm-manager/ui-types"; import { ErrorNotification, Loading } from "@scm-manager/ui-components"; -import injectSheet from "react-jss"; -import classNames from "classnames"; type Props = { repository: Repository, file: File, revision: string, - path: string, - classes: any + path: string }; type State = { @@ -26,12 +23,6 @@ type State = { error?: Error }; -const styles = { - toCenterContent: { - display: "block" - } -}; - class SourcesView extends React.Component<Props, State> { constructor(props: Props) { super(props); @@ -87,7 +78,7 @@ class SourcesView extends React.Component<Props, State> { } render() { - const { file, classes } = this.props; + const { file } = this.props; const { loaded, error } = this.state; if (!file || !loaded) { @@ -99,8 +90,8 @@ class SourcesView extends React.Component<Props, State> { const sources = this.showSources(); - return <div className={classNames("panel-block", classes.toCenterContent)}>{sources}</div>; + return <div className="panel-block">{sources}</div>; } } -export default injectSheet(styles)(SourcesView); +export default SourcesView; diff --git a/scm-ui/styles/scm.scss b/scm-ui/styles/scm.scss index 59f1ca07a4..258c8a84a4 100644 --- a/scm-ui/styles/scm.scss +++ b/scm-ui/styles/scm.scss @@ -85,15 +85,6 @@ $fa-font-path: "webfonts"; } } -//border around options -.has-border-around { - border: 1px solid #dbdbdb; - - &.is-round { - border-radius: 4px; - } -} - // multiline Columns .columns.is-multiline { .column.is-half { @@ -195,20 +186,26 @@ $fa-font-path: "webfonts"; } //panels -.panel-footer { - background-color: whitesmoke; - border-radius: 0 0 4px 4px; - color: #363636; - font-size: 1.25em; - font-weight: 300; - line-height: 1.25; - padding: 0.5em 0.75em; +nav.panel { + .panel-block { + display: block; + } - border-left: 1px solid #dbdbdb; - border-right: 1px solid #dbdbdb; + .panel-footer { + background-color: whitesmoke; + border-radius: 0 0 4px 4px; + color: #363636; + font-size: 1.25em; + font-weight: 300; + line-height: 1.25; + padding: 0.5em 0.75em; - &:last-child { - border-bottom: 1px solid #dbdbdb; + border-left: 1px solid #dbdbdb; + border-right: 1px solid #dbdbdb; + + &:last-child { + border-bottom: 1px solid #dbdbdb; + } } } From 73260f0072258cb00bb91dc3aa149c7b13fc85d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maren=20S=C3=BCwer?= <maren.suewer@cloudogu.com> Date: Fri, 1 Feb 2019 10:39:41 +0100 Subject: [PATCH 060/120] resize modal to length of content --- .../ui-components/src/modals/Modal.js | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/scm-ui-components/packages/ui-components/src/modals/Modal.js b/scm-ui-components/packages/ui-components/src/modals/Modal.js index 2638eb13fa..cf1b011102 100644 --- a/scm-ui-components/packages/ui-components/src/modals/Modal.js +++ b/scm-ui-components/packages/ui-components/src/modals/Modal.js @@ -1,18 +1,30 @@ // @flow import * as React from "react"; import classNames from "classnames"; +import injectSheet from "react-jss"; type Props = { title: string, closeButton: any, body: any, - active: boolean + active: boolean, + classes: any }; +const styles = { + resize: { + maxWidth: "100%", + width: "auto !important", + display: "inline-block" + } +}; + + + class Modal extends React.Component<Props> { render() { - const { title, closeButton, body, active } = this.props; + const { title, closeButton, body, active, classes } = this.props; const isActive = active ? "is-active" : null; @@ -22,7 +34,7 @@ class Modal extends React.Component<Props> { isActive )}> <div className="modal-background" /> - <div className="modal-card"> + <div className={classNames("modal-card", classes.resize)}> <header className="modal-card-head"> <p className="modal-card-title"> @@ -41,4 +53,4 @@ class Modal extends React.Component<Props> { } -export default Modal; +export default injectSheet(styles)(Modal); From ba971beea3cbb79bd654236a2607e6de2084512c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maren=20S=C3=BCwer?= <maren.suewer@cloudogu.com> Date: Fri, 1 Feb 2019 10:43:11 +0100 Subject: [PATCH 061/120] do not forward close button but close function as button is always the same --- .../packages/ui-components/src/modals/ConfirmAlert.js | 9 +-------- .../packages/ui-components/src/modals/Modal.js | 10 +++++++--- .../containers/AdvancedPermissionsDialog.js | 5 +---- 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.js b/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.js index e9ecffefd9..13d5bae5f8 100644 --- a/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.js +++ b/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.js @@ -29,13 +29,6 @@ class ConfirmAlert extends React.Component<Props> { render() { const { title, message, buttons } = this.props; - const closeButton = ( - <button - className="delete" - aria-label="close" - onClick={() => this.close()} - /> - ); const body= ( <> {message} @@ -54,7 +47,7 @@ class ConfirmAlert extends React.Component<Props> { return ( - <Modal title={title} closeButton={closeButton} body={body} active={true}/> + <Modal title={title} closeFunction={() => this.close()} body={body} active={true}/> ); } } diff --git a/scm-ui-components/packages/ui-components/src/modals/Modal.js b/scm-ui-components/packages/ui-components/src/modals/Modal.js index cf1b011102..febcbcd79d 100644 --- a/scm-ui-components/packages/ui-components/src/modals/Modal.js +++ b/scm-ui-components/packages/ui-components/src/modals/Modal.js @@ -5,7 +5,7 @@ import injectSheet from "react-jss"; type Props = { title: string, - closeButton: any, + closeFunction: () => void, body: any, active: boolean, classes: any @@ -24,7 +24,7 @@ const styles = { class Modal extends React.Component<Props> { render() { - const { title, closeButton, body, active, classes } = this.props; + const { title, closeFunction, body, active, classes } = this.props; const isActive = active ? "is-active" : null; @@ -40,7 +40,11 @@ class Modal extends React.Component<Props> { <p className="modal-card-title"> {title} </p> - {closeButton} + <button + className="delete" + aria-label="close" + onClick={closeFunction} + /> </header> <section className="modal-card-body"> {body} diff --git a/scm-ui/src/repos/permissions/containers/AdvancedPermissionsDialog.js b/scm-ui/src/repos/permissions/containers/AdvancedPermissionsDialog.js index 5d53605484..643e2150e5 100644 --- a/scm-ui/src/repos/permissions/containers/AdvancedPermissionsDialog.js +++ b/scm-ui/src/repos/permissions/containers/AdvancedPermissionsDialog.js @@ -50,9 +50,6 @@ class AdvancedPermissionsDialog extends React.Component<Props, State> { <SubmitButton label={t("permission.advanced.dialog.submit")} /> ) : null; - const closeButton = ( - <button className="delete" aria-label="close" onClick={() => onClose()} /> - ); const body = ( <> @@ -70,7 +67,7 @@ class AdvancedPermissionsDialog extends React.Component<Props, State> { return ( <Modal title={t("permission.advanced.dialog.title")} - closeButton={closeButton} + closeFunction={() => onClose()} body={body} active={true} /> From 59abab212a5c2c188062b586f686f9532a668215 Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Fri, 1 Feb 2019 10:50:05 +0100 Subject: [PATCH 062/120] fixed changesets component --- .../src/repos/changesets/ChangesetList.js | 6 +--- scm-ui/src/repos/containers/Changesets.js | 33 +++++++++++++++---- .../repos/sources/containers/HistoryView.js | 4 ++- 3 files changed, 30 insertions(+), 13 deletions(-) diff --git a/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetList.js b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetList.js index e5fac920a2..4cd8ec319c 100644 --- a/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetList.js +++ b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetList.js @@ -21,11 +21,7 @@ class ChangesetList extends React.Component<Props> { /> ); }); - return ( - <div className="panel-block"> - {content} - </div> - ); + return <>{content}</>; } } diff --git a/scm-ui/src/repos/containers/Changesets.js b/scm-ui/src/repos/containers/Changesets.js index 69dc41da2d..5edc677e60 100644 --- a/scm-ui/src/repos/containers/Changesets.js +++ b/scm-ui/src/repos/containers/Changesets.js @@ -1,8 +1,13 @@ // @flow import React from "react"; -import {withRouter} from "react-router-dom"; -import type {Branch, Changeset, PagedCollection, Repository} from "@scm-manager/ui-types"; +import { withRouter } from "react-router-dom"; +import type { + Branch, + Changeset, + PagedCollection, + Repository +} from "@scm-manager/ui-types"; import { fetchChangesets, getChangesets, @@ -11,9 +16,15 @@ import { selectListAsCollection } from "../modules/changesets"; -import {connect} from "react-redux"; -import {ErrorNotification, getPageFromMatch, LinkPaginator, ChangesetList, Loading} from "@scm-manager/ui-components"; -import {compose} from "redux"; +import { connect } from "react-redux"; +import { + ErrorNotification, + getPageFromMatch, + LinkPaginator, + ChangesetList, + Loading +} from "@scm-manager/ui-components"; +import { compose } from "redux"; type Props = { repository: Repository, @@ -64,13 +75,21 @@ class Changesets extends React.Component<Props> { renderList = () => { const { repository, changesets } = this.props; - return <ChangesetList repository={repository} changesets={changesets} />; + return ( + <div className="panel-block"> + <ChangesetList repository={repository} changesets={changesets} /> + </div> + ); }; renderPaginator = () => { const { page, list } = this.props; if (list) { - return <div className="panel-footer"><LinkPaginator page={page} collection={list} /></div>; + return ( + <div className="panel-footer"> + <LinkPaginator page={page} collection={list} /> + </div> + ); } return null; }; diff --git a/scm-ui/src/repos/sources/containers/HistoryView.js b/scm-ui/src/repos/sources/containers/HistoryView.js index c118ef9cea..d13b5904d2 100644 --- a/scm-ui/src/repos/sources/containers/HistoryView.js +++ b/scm-ui/src/repos/sources/containers/HistoryView.js @@ -79,7 +79,9 @@ class HistoryView extends React.Component<Props, State> { const currentPage = page + 1; return ( <> - <ChangesetList repository={repository} changesets={changesets} /> + <div className="panel-block"> + <ChangesetList repository={repository} changesets={changesets} /> + </div> <div className="panel-footer"> <StatePaginator page={currentPage} From b3a3162d34f25362f49ecb2398c8bba83e8f4d00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maren=20S=C3=BCwer?= <maren.suewer@cloudogu.com> Date: Fri, 1 Feb 2019 11:09:07 +0100 Subject: [PATCH 063/120] renaming --- .../packages/ui-components/src/modals/ConfirmAlert.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.js b/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.js index 13d5bae5f8..4192411eb7 100644 --- a/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.js +++ b/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.js @@ -3,7 +3,7 @@ import * as React from "react"; import ReactDOM from "react-dom"; import Modal from "./Modal"; -type ButtonType = { +type Button = { label: string, onClick: () => void | null }; @@ -11,11 +11,11 @@ type ButtonType = { type Props = { title: string, message: string, - buttons: ButtonType[] + buttons: Button[] }; class ConfirmAlert extends React.Component<Props> { - handleClickButton = (button: ButtonType) => { + handleClickButton = (button: Button) => { if (button.onClick) { button.onClick(); } From a09698d526fff63a1452e40c88ae24e3e9091a18 Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Fri, 1 Feb 2019 13:51:14 +0100 Subject: [PATCH 064/120] fixed smaller merge issues --- scm-ui/src/repos/containers/RepositoryRoot.js | 5 ----- scm-ui/src/users/components/UserForm.js | 4 ++-- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/scm-ui/src/repos/containers/RepositoryRoot.js b/scm-ui/src/repos/containers/RepositoryRoot.js index 2c2d4b4ace..d0d722e1fd 100644 --- a/scm-ui/src/repos/containers/RepositoryRoot.js +++ b/scm-ui/src/repos/containers/RepositoryRoot.js @@ -33,13 +33,8 @@ import ChangesetView from "./ChangesetView"; import PermissionsNavLink from "../components/PermissionsNavLink"; import Sources from "../sources/containers/Sources"; import RepositoryNavLink from "../components/RepositoryNavLink"; -<<<<<<< working copy -import { getRepositoriesLink } from "../../modules/indexResource"; -import { ExtensionPoint } from "@scm-manager/ui-extensions"; -======= import {getLinks, getRepositoriesLink} from "../../modules/indexResource"; import {ExtensionPoint} from "@scm-manager/ui-extensions"; ->>>>>>> merge rev type Props = { namespace: string, diff --git a/scm-ui/src/users/components/UserForm.js b/scm-ui/src/users/components/UserForm.js index d2d827aa93..c8fae1ff0a 100644 --- a/scm-ui/src/users/components/UserForm.js +++ b/scm-ui/src/users/components/UserForm.js @@ -141,9 +141,9 @@ class UserForm extends React.Component<Props, State> { <> {subtitle} <form onSubmit={this.submit}> - <div className="columns"> + <div className="columns is-multiline"> + {nameField} <div className="column is-half"> - {nameField} <InputField label={t("user.displayName")} onChange={this.handleDisplayNameChange} From de8b6dbd84c92b8fc3c3bb75ecb0e7774c1f4356 Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Fri, 1 Feb 2019 13:59:01 +0100 Subject: [PATCH 065/120] fixed smaller merge issues --- scm-ui/public/locales/en/repos.json | 2 +- scm-ui/public/locales/en/users.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scm-ui/public/locales/en/repos.json b/scm-ui/public/locales/en/repos.json index 305f182ccb..4aabcf6c72 100644 --- a/scm-ui/public/locales/en/repos.json +++ b/scm-ui/public/locales/en/repos.json @@ -111,7 +111,7 @@ "name-input-invalid": "Permission is not allowed to be empty! If it is not empty, your input name is invalid or it already exists!" }, "help": { - "groupPermissionHelpText": "States if a permission is a group permission.", + "groupPermissionHelpText": "States if a permission is a group permission. If this is not checked, it is a user permission.", "nameHelpText": "Manage permissions for a specific user or group.", "roleHelpText": "READ = read; WRITE = read and write; OWNER = read, write and also the ability to manage the properties and permissions. If nothing is selected here, use the 'Advanced' Button to see detailed permissions.", "permissionsHelpText": "Use this to specify your own set of permissions regardless of predefined roles." diff --git a/scm-ui/public/locales/en/users.json b/scm-ui/public/locales/en/users.json index f13f68df2c..2b72d85cbc 100644 --- a/scm-ui/public/locales/en/users.json +++ b/scm-ui/public/locales/en/users.json @@ -20,7 +20,7 @@ "displayNameHelpText": "Display name of the user.", "mailHelpText": "Email address of the user.", "adminHelpText": "An administrator is able to create, modify and delete repositories, groups and users.", - "activeHelpText": "Activate or deactive the user." + "activeHelpText": "Activate or deactivate the user." }, "users": { "title": "Users", From e8666fa3e861d9c9b1935603e7c9ff0158a6ac0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= <rene.pfeuffer@cloudogu.com> Date: Fri, 1 Feb 2019 15:53:13 +0100 Subject: [PATCH 066/120] Add disabled flag to download button --- .../packages/ui-components/src/buttons/DownloadButton.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/scm-ui-components/packages/ui-components/src/buttons/DownloadButton.js b/scm-ui-components/packages/ui-components/src/buttons/DownloadButton.js index 279c472f0e..ac24d68447 100644 --- a/scm-ui-components/packages/ui-components/src/buttons/DownloadButton.js +++ b/scm-ui-components/packages/ui-components/src/buttons/DownloadButton.js @@ -3,14 +3,15 @@ import React from "react"; type Props = { displayName: string, - url: string + url: string, + disabled: boolean }; class DownloadButton extends React.Component<Props> { render() { - const { displayName, url } = this.props; + const { displayName, url, disabled } = this.props; return ( - <a className="button is-large is-link" href={url}> + <a className="button is-large is-link" href={url} disabled={disabled}> <span className="icon is-medium"> <i className="fas fa-arrow-circle-down" /> </span> From 1c459e994473cc2381dfccab7899da6aa01264eb Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Fri, 1 Feb 2019 16:08:33 +0100 Subject: [PATCH 067/120] renamed editusernavlink back + implemented new extension point for repo --- .../src/config/ConfigurationBinder.js | 29 ++++++++++++++++++- ...avLink.test.js => EditUserNavLink.test.js} | 0 2 files changed, 28 insertions(+), 1 deletion(-) rename scm-ui/src/users/components/navLinks/{GeneralUserNavLink.test.js => EditUserNavLink.test.js} (100%) diff --git a/scm-ui-components/packages/ui-components/src/config/ConfigurationBinder.js b/scm-ui-components/packages/ui-components/src/config/ConfigurationBinder.js index 2454ad9cea..da1372c316 100644 --- a/scm-ui-components/packages/ui-components/src/config/ConfigurationBinder.js +++ b/scm-ui-components/packages/ui-components/src/config/ConfigurationBinder.js @@ -59,7 +59,7 @@ class ConfigurationBinder { }); // bind navigation link to extension point - binder.bind("repository.subnavigation", RepoNavLink, repoPredicate); + binder.bind("repository.navigation", RepoNavLink, repoPredicate); // route for global configuration, passes the current repository to component @@ -72,6 +72,33 @@ class ConfigurationBinder { binder.bind("repository.route", RepoRoute, repoPredicate); } + bindRepositorySub(to: string, labelI18nKey: string, linkName: string, RepositoryComponent: any) { + + // create predicate based on the link name of the current repository route + // if the linkname is not available, the navigation link and the route are not bound to the extension points + const repoPredicate = (props: Object) => { + return props.repository && props.repository._links && props.repository._links[linkName]; + }; + + // create NavigationLink with translated label + const RepoNavLink = translate(this.i18nNamespace)(({t, url}) => { + return this.navLink(url + "/settings" + to, labelI18nKey, t); + }); + + // bind navigation link to extension point + binder.bind("repository.subnavigation", RepoNavLink, repoPredicate); + + + // route for global configuration, passes the current repository to component + const RepoRoute = ({url, repository, ...additionalProps}) => { + const link = repository._links[linkName].href; + return this.route(url + "/settings" + to, <RepositoryComponent repository={repository} link={link} {...additionalProps}/>); + }; + + // bind config route to extension point + binder.bind("repository.route", RepoRoute, repoPredicate); + } + } export default new ConfigurationBinder(); diff --git a/scm-ui/src/users/components/navLinks/GeneralUserNavLink.test.js b/scm-ui/src/users/components/navLinks/EditUserNavLink.test.js similarity index 100% rename from scm-ui/src/users/components/navLinks/GeneralUserNavLink.test.js rename to scm-ui/src/users/components/navLinks/EditUserNavLink.test.js From 7baea043c1ec581e0156357e63bc79783b5ee48e Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Fri, 1 Feb 2019 16:13:28 +0100 Subject: [PATCH 068/120] fix for git plugin bind --- scm-plugins/scm-git-plugin/src/main/js/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scm-plugins/scm-git-plugin/src/main/js/index.js b/scm-plugins/scm-git-plugin/src/main/js/index.js index 5534d2061a..d3fe67476b 100644 --- a/scm-plugins/scm-git-plugin/src/main/js/index.js +++ b/scm-plugins/scm-git-plugin/src/main/js/index.js @@ -27,8 +27,8 @@ binder.bind( ); binder.bind("repos.repository-avatar", GitAvatar, gitPredicate); -cfgBinder.bindRepository( - "/settings/configuration", +cfgBinder.bindRepositorySub( + "/configuration", "scm-git-plugin.repo-config.link", "configuration", RepositoryConfig From ccb199abb75a79e884da88438beccfe63b87a859 Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Fri, 1 Feb 2019 17:03:00 +0100 Subject: [PATCH 069/120] renamed panel blocks --- scm-ui/src/repos/containers/ChangesetsRoot.js | 8 ++++---- scm-ui/src/repos/sources/containers/Content.js | 6 +++--- scm-ui/src/repos/sources/containers/Sources.js | 8 ++++---- scm-ui/styles/scm.scss | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/scm-ui/src/repos/containers/ChangesetsRoot.js b/scm-ui/src/repos/containers/ChangesetsRoot.js index d3d4635e18..0cbf4f1970 100644 --- a/scm-ui/src/repos/containers/ChangesetsRoot.js +++ b/scm-ui/src/repos/containers/ChangesetsRoot.js @@ -89,12 +89,12 @@ class BranchRoot extends React.Component<Props> { const changesets = <Changesets repository={repository} branch={branch} />; return ( - <nav className="panel"> - <article className="panel-heading"> + <div className="panel"> + <div className="panel-heading"> {this.renderBranchSelector()} - </article> + </div> <Route path={`${url}/:page?`} component={() => changesets} /> - </nav> + </div> ); } diff --git a/scm-ui/src/repos/sources/containers/Content.js b/scm-ui/src/repos/sources/containers/Content.js index 899e40b511..d1b8fc8021 100644 --- a/scm-ui/src/repos/sources/containers/Content.js +++ b/scm-ui/src/repos/sources/containers/Content.js @@ -176,11 +176,11 @@ class Content extends React.Component<Props, State> { return ( <div> - <nav className="panel"> - <article className="panel-heading">{header}</article> + <div className="panel"> + <div className="panel-heading">{header}</div> {moreInformation} {content} - </nav> + </div> </div> ); } diff --git a/scm-ui/src/repos/sources/containers/Sources.js b/scm-ui/src/repos/sources/containers/Sources.js index 7da024b450..0d038ffdbe 100644 --- a/scm-ui/src/repos/sources/containers/Sources.js +++ b/scm-ui/src/repos/sources/containers/Sources.js @@ -93,17 +93,17 @@ class Sources extends React.Component<Props> { if (currentFileIsDirectory) { return ( - <nav className="panel"> - <article className="panel-heading"> + <div className="panel"> + <div className="panel-heading"> {this.renderBranchSelector()} - </article> + </div> <FileTree repository={repository} revision={revision} path={path} baseUrl={baseUrl} /> - </nav> + </div> ); } else { return ( diff --git a/scm-ui/styles/scm.scss b/scm-ui/styles/scm.scss index 258c8a84a4..1feffc401f 100644 --- a/scm-ui/styles/scm.scss +++ b/scm-ui/styles/scm.scss @@ -186,7 +186,7 @@ $fa-font-path: "webfonts"; } //panels -nav.panel { +.panel { .panel-block { display: block; } From 6986ab86d28b80b10d36e27e3a80cd37909268b2 Mon Sep 17 00:00:00 2001 From: Mohamed Karray <mohamed.karray.sr@gmail.com> Date: Fri, 1 Feb 2019 17:22:10 +0100 Subject: [PATCH 070/120] add extension point for the first primary navigation menu --- .../ui-components/src/navigation/PrimaryNavigation.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/scm-ui-components/packages/ui-components/src/navigation/PrimaryNavigation.js b/scm-ui-components/packages/ui-components/src/navigation/PrimaryNavigation.js index f2401729d6..897c63138e 100644 --- a/scm-ui-components/packages/ui-components/src/navigation/PrimaryNavigation.js +++ b/scm-ui-components/packages/ui-components/src/navigation/PrimaryNavigation.js @@ -50,8 +50,19 @@ class PrimaryNavigation extends React.Component<Props> { createNavigationItems = () => { const navigationItems = []; + const { t, links } = this.props; + + const props = { + links, + label: t("primary-navigation.first-menu") + }; const append = this.createNavigationAppender(navigationItems); + if (binder.hasExtension("primary-navigation.first-menu", props)) { + navigationItems.push( + <ExtensionPoint name="primary-navigation.first-menu" props={props} /> + ); + } append("/repos", "/(repo|repos)", "primary-navigation.repositories", "repositories"); append("/users", "/(user|users)", "primary-navigation.users", "users"); append("/groups", "/(group|groups)", "primary-navigation.groups", "groups"); From 5432ee56dc00759629c58c400457f9df71358f17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= <rene.pfeuffer@cloudogu.com> Date: Fri, 1 Feb 2019 18:20:50 +0100 Subject: [PATCH 071/120] Add optional callback to download button --- .../packages/ui-components/src/buttons/DownloadButton.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/scm-ui-components/packages/ui-components/src/buttons/DownloadButton.js b/scm-ui-components/packages/ui-components/src/buttons/DownloadButton.js index ac24d68447..382bd500c5 100644 --- a/scm-ui-components/packages/ui-components/src/buttons/DownloadButton.js +++ b/scm-ui-components/packages/ui-components/src/buttons/DownloadButton.js @@ -4,14 +4,16 @@ import React from "react"; type Props = { displayName: string, url: string, - disabled: boolean + disabled: boolean, + onClick?: () => void }; class DownloadButton extends React.Component<Props> { render() { - const { displayName, url, disabled } = this.props; + const { displayName, url, disabled, onClick } = this.props; + const onClickOrDefault = !!onClick ? onClick : () => {}; return ( - <a className="button is-large is-link" href={url} disabled={disabled}> + <a className="button is-large is-link" href={url} disabled={disabled} onClick={onClickOrDefault}> <span className="icon is-medium"> <i className="fas fa-arrow-circle-down" /> </span> From 5ac065e5dac68e92eb8bf5e16a06435c857d8832 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra <sebastian.sdorra@cloudogu.com> Date: Mon, 4 Feb 2019 11:39:59 +0100 Subject: [PATCH 072/120] separate methods for reading modification from transaction and from revision --- .../spi/SvnModificationsCommand.java | 56 +++++++++++-------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnModificationsCommand.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnModificationsCommand.java index bbcaa9a9d5..580bc0b77d 100644 --- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnModificationsCommand.java +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnModificationsCommand.java @@ -21,41 +21,51 @@ public class SvnModificationsCommand extends AbstractSvnCommand implements Modif super(context, repository); } - @Override - @SuppressWarnings("unchecked") - public Modifications getModifications(String revision) { - final Modifications modifications = new Modifications(); - log.debug("get modifications {}", revision); + public Modifications getModifications(String revisionOrTransactionId) { + Modifications modifications; try { - if (SvnUtil.isTransactionEntryId(revision)) { - - SVNLookClient client = SVNClientManager.newInstance().getLookClient(); - client.doGetChanged(context.getDirectory(), SvnUtil.getTransactionId(revision), - e -> SvnUtil.appendModification(modifications, e.getType(), e.getPath()), true); - - return modifications; - + if (SvnUtil.isTransactionEntryId(revisionOrTransactionId)) { + modifications = getModificationsFromTransaction(SvnUtil.getTransactionId(revisionOrTransactionId)); } else { - - long revisionNumber = SvnUtil.getRevisionNumber(revision, repository); - SVNRepository repo = open(); - Collection<SVNLogEntry> entries = repo.log(null, null, revisionNumber, - revisionNumber, true, true); - if (Util.isNotEmpty(entries)) { - return SvnUtil.createModifications(entries.iterator().next(), revision); - } + modifications = getModificationFromRevision(revisionOrTransactionId); } + return modifications; } catch (SVNException ex) { - throw new InternalRepositoryException(repository, "could not open repository", ex); + throw new InternalRepositoryException( + repository, + "failed to get svn modifications for " + revisionOrTransactionId, + ex + ); + } + } + + @SuppressWarnings("unchecked") + private Modifications getModificationFromRevision(String revision) throws SVNException { + log.debug("get svn modifications from revision: {}", revision); + long revisionNumber = SvnUtil.getRevisionNumber(revision, repository); + SVNRepository repo = open(); + Collection<SVNLogEntry> entries = repo.log(null, null, revisionNumber, + revisionNumber, true, true); + if (Util.isNotEmpty(entries)) { + return SvnUtil.createModifications(entries.iterator().next(), revision); } return null; } + private Modifications getModificationsFromTransaction(String transaction) throws SVNException { + log.debug("get svn modifications from transaction: {}", transaction); + final Modifications modifications = new Modifications(); + SVNLookClient client = SVNClientManager.newInstance().getLookClient(); + client.doGetChanged(context.getDirectory(), transaction, + e -> SvnUtil.appendModification(modifications, e.getType(), e.getPath()), true); + + return modifications; + } + @Override public Modifications getModifications(ModificationsCommandRequest request) { return getModifications(request.getRevision()); } - } From 1dfbcc336f8ac9b9989446997475ed3fc99e8f8e Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra <sebastian.sdorra@cloudogu.com> Date: Mon, 4 Feb 2019 11:59:47 +0000 Subject: [PATCH 073/120] Close branch bugfix/get_modifications_for_svn From accd20538e7593ecef11f8c5683c23eed8513e4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= <rene.pfeuffer@cloudogu.com> Date: Mon, 4 Feb 2019 14:35:51 +0100 Subject: [PATCH 074/120] Fix class loader for creation of vcs version string Use UberClassLoader in AbstractSimpleRepositoryHandler#getStringFromResource Therefore pass through plugin loader --- .../scm/repository/AbstractSimpleRepositoryHandler.java | 8 ++++++-- .../java/sonia/scm/repository/GitRepositoryHandler.java | 6 ++++-- .../sonia/scm/repository/GitRepositoryHandlerTest.java | 4 ++-- .../java/sonia/scm/repository/HgRepositoryHandler.java | 6 ++++-- .../sonia/scm/repository/HgRepositoryHandlerTest.java | 4 ++-- .../src/test/java/sonia/scm/repository/HgTestUtil.java | 2 +- .../java/sonia/scm/repository/SvnRepositoryHandler.java | 7 ++++--- .../sonia/scm/repository/SvnRepositoryHandlerTest.java | 8 ++------ .../java/sonia/scm/repository/DummyRepositoryHandler.java | 2 +- 9 files changed, 26 insertions(+), 21 deletions(-) diff --git a/scm-core/src/main/java/sonia/scm/repository/AbstractSimpleRepositoryHandler.java b/scm-core/src/main/java/sonia/scm/repository/AbstractSimpleRepositoryHandler.java index 9941a4253b..663d053ca5 100644 --- a/scm-core/src/main/java/sonia/scm/repository/AbstractSimpleRepositoryHandler.java +++ b/scm-core/src/main/java/sonia/scm/repository/AbstractSimpleRepositoryHandler.java @@ -40,6 +40,7 @@ import org.slf4j.LoggerFactory; import sonia.scm.ConfigurationException; import sonia.scm.io.CommandResult; import sonia.scm.io.ExtendedCommand; +import sonia.scm.plugin.PluginLoader; import sonia.scm.store.ConfigurationStoreFactory; import java.io.File; @@ -67,11 +68,14 @@ public abstract class AbstractSimpleRepositoryHandler<C extends RepositoryConfig LoggerFactory.getLogger(AbstractSimpleRepositoryHandler.class); private final RepositoryLocationResolver repositoryLocationResolver; + private final PluginLoader pluginLoader; public AbstractSimpleRepositoryHandler(ConfigurationStoreFactory storeFactory, - RepositoryLocationResolver repositoryLocationResolver) { + RepositoryLocationResolver repositoryLocationResolver, + PluginLoader pluginLoader) { super(storeFactory); this.repositoryLocationResolver = repositoryLocationResolver; + this.pluginLoader = pluginLoader; } @Override @@ -155,7 +159,7 @@ public abstract class AbstractSimpleRepositoryHandler<C extends RepositoryConfig String content = defaultContent; try { - URL url = Resources.getResource(resource); + URL url = pluginLoader.getUberClassLoader().getResource(resource); if (url != null) { content = Resources.toString(url, Charsets.UTF_8); diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitRepositoryHandler.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitRepositoryHandler.java index 95225c9e30..63800e8a02 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitRepositoryHandler.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitRepositoryHandler.java @@ -44,6 +44,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sonia.scm.SCMContextProvider; import sonia.scm.plugin.Extension; +import sonia.scm.plugin.PluginLoader; import sonia.scm.repository.spi.GitRepositoryServiceProvider; import sonia.scm.schedule.Scheduler; import sonia.scm.schedule.Task; @@ -103,9 +104,10 @@ public class GitRepositoryHandler public GitRepositoryHandler(ConfigurationStoreFactory storeFactory, Scheduler scheduler, RepositoryLocationResolver repositoryLocationResolver, - GitWorkdirFactory workdirFactory) + GitWorkdirFactory workdirFactory, + PluginLoader pluginLoader) { - super(storeFactory, repositoryLocationResolver); + super(storeFactory, repositoryLocationResolver, pluginLoader); this.scheduler = scheduler; this.workdirFactory = workdirFactory; } diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitRepositoryHandlerTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitRepositoryHandlerTest.java index cb10e15271..66ec320067 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitRepositoryHandlerTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitRepositoryHandlerTest.java @@ -94,7 +94,7 @@ public class GitRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase { RepositoryLocationResolver locationResolver, File directory) { GitRepositoryHandler repositoryHandler = new GitRepositoryHandler(factory, - scheduler, locationResolver, gitWorkdirFactory); + scheduler, locationResolver, gitWorkdirFactory, null); repositoryHandler.init(contextProvider); GitConfig config = new GitConfig(); @@ -108,7 +108,7 @@ public class GitRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase { @Test public void getDirectory() { GitRepositoryHandler repositoryHandler = new GitRepositoryHandler(factory, - scheduler, locationResolver, gitWorkdirFactory); + scheduler, locationResolver, gitWorkdirFactory, null); GitConfig config = new GitConfig(); config.setDisabled(false); config.setGcExpression("gc exp"); diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryHandler.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryHandler.java index c2c0439fc1..7db9a8becb 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryHandler.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryHandler.java @@ -51,6 +51,7 @@ import sonia.scm.io.INIConfigurationReader; import sonia.scm.io.INIConfigurationWriter; import sonia.scm.io.INISection; import sonia.scm.plugin.Extension; +import sonia.scm.plugin.PluginLoader; import sonia.scm.repository.spi.HgRepositoryServiceProvider; import sonia.scm.store.ConfigurationStoreFactory; import sonia.scm.util.IOUtil; @@ -111,9 +112,10 @@ public class HgRepositoryHandler @Inject public HgRepositoryHandler(ConfigurationStoreFactory storeFactory, Provider<HgContext> hgContextProvider, - RepositoryLocationResolver repositoryLocationResolver) + RepositoryLocationResolver repositoryLocationResolver, + PluginLoader pluginLoader) { - super(storeFactory, repositoryLocationResolver); + super(storeFactory, repositoryLocationResolver, pluginLoader); this.hgContextProvider = hgContextProvider; try diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgRepositoryHandlerTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgRepositoryHandlerTest.java index c45d9ab358..ed222f5119 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgRepositoryHandlerTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgRepositoryHandlerTest.java @@ -77,7 +77,7 @@ public class HgRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase { @Override protected RepositoryHandler createRepositoryHandler(ConfigurationStoreFactory factory, RepositoryLocationResolver locationResolver, File directory) { - HgRepositoryHandler handler = new HgRepositoryHandler(factory, new HgContextProvider(), locationResolver); + HgRepositoryHandler handler = new HgRepositoryHandler(factory, new HgContextProvider(), locationResolver, null); handler.init(contextProvider); HgTestUtil.checkForSkip(handler); @@ -87,7 +87,7 @@ public class HgRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase { @Test public void getDirectory() { - HgRepositoryHandler repositoryHandler = new HgRepositoryHandler(factory, provider, locationResolver); + HgRepositoryHandler repositoryHandler = new HgRepositoryHandler(factory, provider, locationResolver, null); HgConfig hgConfig = new HgConfig(); hgConfig.setHgBinary("hg"); diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgTestUtil.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgTestUtil.java index 68f7e18a76..131dad0837 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgTestUtil.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgTestUtil.java @@ -105,7 +105,7 @@ public final class HgTestUtil RepositoryLocationResolver repositoryLocationResolver = new RepositoryLocationResolver(context, repoDao, new InitialRepositoryLocationResolver()); HgRepositoryHandler handler = - new HgRepositoryHandler(new InMemoryConfigurationStoreFactory(), new HgContextProvider(), repositoryLocationResolver); + new HgRepositoryHandler(new InMemoryConfigurationStoreFactory(), new HgContextProvider(), repositoryLocationResolver, null); Path repoDir = directory.toPath(); when(repoDao.getPath(any())).thenReturn(repoDir); handler.init(context); diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnRepositoryHandler.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnRepositoryHandler.java index 86f99cd517..639a16968c 100644 --- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnRepositoryHandler.java +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnRepositoryHandler.java @@ -53,9 +53,9 @@ import sonia.scm.io.INIConfigurationWriter; import sonia.scm.io.INISection; import sonia.scm.logging.SVNKitLogger; import sonia.scm.plugin.Extension; +import sonia.scm.plugin.PluginLoader; import sonia.scm.repository.spi.HookEventFacade; import sonia.scm.repository.spi.SvnRepositoryServiceProvider; -import sonia.scm.store.ConfigurationStore; import sonia.scm.store.ConfigurationStoreFactory; import sonia.scm.util.Util; @@ -97,9 +97,10 @@ public class SvnRepositoryHandler @Inject public SvnRepositoryHandler(ConfigurationStoreFactory storeFactory, HookEventFacade eventFacade, - RepositoryLocationResolver repositoryLocationResolver) + RepositoryLocationResolver repositoryLocationResolver, + PluginLoader pluginLoader) { - super(storeFactory, repositoryLocationResolver); + super(storeFactory, repositoryLocationResolver, pluginLoader); // register logger SVNDebugLog.setDefaultLog(new SVNKitLogger()); diff --git a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/SvnRepositoryHandlerTest.java b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/SvnRepositoryHandlerTest.java index 7b22e15c94..fc0a9e9ae0 100644 --- a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/SvnRepositoryHandlerTest.java +++ b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/SvnRepositoryHandlerTest.java @@ -32,14 +32,10 @@ package sonia.scm.repository; -import org.junit.Before; import org.junit.Test; -import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import sonia.scm.repository.api.HookContextFactory; import sonia.scm.repository.spi.HookEventFacade; -import sonia.scm.store.ConfigurationStore; import sonia.scm.store.ConfigurationStoreFactory; import java.io.File; @@ -93,7 +89,7 @@ public class SvnRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase { protected RepositoryHandler createRepositoryHandler(ConfigurationStoreFactory factory, RepositoryLocationResolver locationResolver, File directory) { - SvnRepositoryHandler handler = new SvnRepositoryHandler(factory, null, locationResolver); + SvnRepositoryHandler handler = new SvnRepositoryHandler(factory, null, locationResolver, null); handler.init(contextProvider); @@ -109,7 +105,7 @@ public class SvnRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase { public void getDirectory() { when(factory.withType(any())).thenCallRealMethod(); SvnRepositoryHandler repositoryHandler = new SvnRepositoryHandler(factory, - facade, locationResolver); + facade, locationResolver, null); SvnConfig svnConfig = new SvnConfig(); repositoryHandler.setConfig(svnConfig); diff --git a/scm-test/src/main/java/sonia/scm/repository/DummyRepositoryHandler.java b/scm-test/src/main/java/sonia/scm/repository/DummyRepositoryHandler.java index 3efe78c820..ccaeee8631 100644 --- a/scm-test/src/main/java/sonia/scm/repository/DummyRepositoryHandler.java +++ b/scm-test/src/main/java/sonia/scm/repository/DummyRepositoryHandler.java @@ -59,7 +59,7 @@ public class DummyRepositoryHandler private final Set<String> existingRepoNames = new HashSet<>(); public DummyRepositoryHandler(ConfigurationStoreFactory storeFactory, RepositoryLocationResolver repositoryLocationResolver) { - super(storeFactory, repositoryLocationResolver); + super(storeFactory, repositoryLocationResolver, null); } @Override From a180f9b79526052efcc194f201755900625e826e Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra <sebastian.sdorra@cloudogu.com> Date: Mon, 4 Feb 2019 14:39:29 +0100 Subject: [PATCH 075/120] rename LinkEnricher to HalEnricher --- .../scm/api/v2/resources/BaseMapper.java | 2 +- .../{LinkAppender.java => HalAppender.java} | 4 +- ...nderMapper.java => HalAppenderMapper.java} | 14 ++-- .../{LinkEnricher.java => HalEnricher.java} | 12 ++-- ...erContext.java => HalEnricherContext.java} | 12 ++-- ...Registry.java => HalEnricherRegistry.java} | 14 ++-- .../sonia/scm/api/v2/resources/Index.java | 2 +- .../java/sonia/scm/api/v2/resources/Me.java | 2 +- ...erTest.java => HalAppenderMapperTest.java} | 12 ++-- ...tTest.java => HalEnricherContextTest.java} | 12 ++-- ...Test.java => HalEnricherRegistryTest.java} | 26 ++++---- .../v2/resources/BranchToBranchDtoMapper.java | 4 +- .../ChangesetToChangesetDtoMapper.java | 4 +- ...nkAppender.java => EdisonHalAppender.java} | 4 +- .../FileObjectToFileObjectDtoMapper.java | 7 +- .../v2/resources/GroupToGroupDtoMapper.java | 2 +- .../api/v2/resources/IndexDtoGenerator.java | 4 +- .../LinkEnricherAutoRegistration.java | 12 ++-- .../scm/api/v2/resources/MeDtoFactory.java | 4 +- .../RepositoryToRepositoryDtoMapper.java | 2 +- .../api/v2/resources/TagToTagDtoMapper.java | 4 +- .../api/v2/resources/UserToUserDtoMapper.java | 2 +- .../BranchToBranchDtoMapperTest.java | 2 +- ...erTest.java => EdisonHalAppenderTest.java} | 6 +- .../FileObjectToFileObjectDtoMapperTest.java | 2 +- .../resources/GroupToGroupDtoMapperTest.java | 3 +- .../HalEnricherAutoRegistrationTest.java | 64 +++++++++++++++++++ .../LinkEnricherAutoRegistrationTest.java | 64 ------------------- .../api/v2/resources/MeDtoFactoryTest.java | 3 +- .../RepositoryToRepositoryDtoMapperTest.java | 2 +- .../v2/resources/TagToTagDtoMapperTest.java | 2 +- .../v2/resources/UserToUserDtoMapperTest.java | 2 +- 32 files changed, 153 insertions(+), 158 deletions(-) rename scm-core/src/main/java/sonia/scm/api/v2/resources/{LinkAppender.java => HalAppender.java} (85%) rename scm-core/src/main/java/sonia/scm/api/v2/resources/{LinkAppenderMapper.java => HalAppenderMapper.java} (56%) rename scm-core/src/main/java/sonia/scm/api/v2/resources/{LinkEnricher.java => HalEnricher.java} (51%) rename scm-core/src/main/java/sonia/scm/api/v2/resources/{LinkEnricherContext.java => HalEnricherContext.java} (80%) rename scm-core/src/main/java/sonia/scm/api/v2/resources/{LinkEnricherRegistry.java => HalEnricherRegistry.java} (53%) rename scm-core/src/test/java/sonia/scm/api/v2/resources/{LinkAppenderMapperTest.java => HalAppenderMapperTest.java} (89%) rename scm-core/src/test/java/sonia/scm/api/v2/resources/{LinkEnricherContextTest.java => HalEnricherContextTest.java} (72%) rename scm-core/src/test/java/sonia/scm/api/v2/resources/{LinkEnricherRegistryTest.java => HalEnricherRegistryTest.java} (53%) rename scm-webapp/src/main/java/sonia/scm/api/v2/resources/{EdisonLinkAppender.java => EdisonHalAppender.java} (91%) rename scm-webapp/src/test/java/sonia/scm/api/v2/resources/{EdisonLinkAppenderTest.java => EdisonHalAppenderTest.java} (88%) create mode 100644 scm-webapp/src/test/java/sonia/scm/api/v2/resources/HalEnricherAutoRegistrationTest.java delete mode 100644 scm-webapp/src/test/java/sonia/scm/api/v2/resources/LinkEnricherAutoRegistrationTest.java diff --git a/scm-core/src/main/java/sonia/scm/api/v2/resources/BaseMapper.java b/scm-core/src/main/java/sonia/scm/api/v2/resources/BaseMapper.java index 89cc893131..eba8173de1 100644 --- a/scm-core/src/main/java/sonia/scm/api/v2/resources/BaseMapper.java +++ b/scm-core/src/main/java/sonia/scm/api/v2/resources/BaseMapper.java @@ -3,7 +3,7 @@ package sonia.scm.api.v2.resources; import de.otto.edison.hal.HalRepresentation; import org.mapstruct.Mapping; -public abstract class BaseMapper<T, D extends HalRepresentation> extends LinkAppenderMapper implements InstantAttributeMapper { +public abstract class BaseMapper<T, D extends HalRepresentation> extends HalAppenderMapper implements InstantAttributeMapper { @Mapping(target = "attributes", ignore = true) // We do not map HAL attributes public abstract D map(T modelObject); diff --git a/scm-core/src/main/java/sonia/scm/api/v2/resources/LinkAppender.java b/scm-core/src/main/java/sonia/scm/api/v2/resources/HalAppender.java similarity index 85% rename from scm-core/src/main/java/sonia/scm/api/v2/resources/LinkAppender.java rename to scm-core/src/main/java/sonia/scm/api/v2/resources/HalAppender.java index d3864dc798..14cd5153e6 100644 --- a/scm-core/src/main/java/sonia/scm/api/v2/resources/LinkAppender.java +++ b/scm-core/src/main/java/sonia/scm/api/v2/resources/HalAppender.java @@ -1,12 +1,12 @@ package sonia.scm.api.v2.resources; /** - * The {@link LinkAppender} can be used within an {@link LinkEnricher} to append hateoas links to a json response. + * The {@link HalAppender} can be used within an {@link HalEnricher} to append hateoas links to a json response. * * @author Sebastian Sdorra * @since 2.0.0 */ -public interface LinkAppender { +public interface HalAppender { /** * Appends one link to the json response. diff --git a/scm-core/src/main/java/sonia/scm/api/v2/resources/LinkAppenderMapper.java b/scm-core/src/main/java/sonia/scm/api/v2/resources/HalAppenderMapper.java similarity index 56% rename from scm-core/src/main/java/sonia/scm/api/v2/resources/LinkAppenderMapper.java rename to scm-core/src/main/java/sonia/scm/api/v2/resources/HalAppenderMapper.java index 7843491b71..04310efad2 100644 --- a/scm-core/src/main/java/sonia/scm/api/v2/resources/LinkAppenderMapper.java +++ b/scm-core/src/main/java/sonia/scm/api/v2/resources/HalAppenderMapper.java @@ -4,17 +4,17 @@ import com.google.common.annotations.VisibleForTesting; import javax.inject.Inject; -public class LinkAppenderMapper { +public class HalAppenderMapper { @Inject - private LinkEnricherRegistry registry; + private HalEnricherRegistry registry; @VisibleForTesting - void setRegistry(LinkEnricherRegistry registry) { + void setRegistry(HalEnricherRegistry registry) { this.registry = registry; } - protected void appendLinks(LinkAppender appender, Object source, Object... contextEntries) { + protected void appendLinks(HalAppender appender, Object source, Object... contextEntries) { // null check is only their to not break existing tests if (registry != null) { @@ -24,10 +24,10 @@ public class LinkAppenderMapper { ctx[i + 1] = contextEntries[i]; } - LinkEnricherContext context = LinkEnricherContext.of(ctx); + HalEnricherContext context = HalEnricherContext.of(ctx); - Iterable<LinkEnricher> enrichers = registry.allByType(source.getClass()); - for (LinkEnricher enricher : enrichers) { + Iterable<HalEnricher> enrichers = registry.allByType(source.getClass()); + for (HalEnricher enricher : enrichers) { enricher.enrich(context, appender); } } diff --git a/scm-core/src/main/java/sonia/scm/api/v2/resources/LinkEnricher.java b/scm-core/src/main/java/sonia/scm/api/v2/resources/HalEnricher.java similarity index 51% rename from scm-core/src/main/java/sonia/scm/api/v2/resources/LinkEnricher.java rename to scm-core/src/main/java/sonia/scm/api/v2/resources/HalEnricher.java index c16d6f6482..647a1cf74e 100644 --- a/scm-core/src/main/java/sonia/scm/api/v2/resources/LinkEnricher.java +++ b/scm-core/src/main/java/sonia/scm/api/v2/resources/HalEnricher.java @@ -3,8 +3,8 @@ package sonia.scm.api.v2.resources; import sonia.scm.plugin.ExtensionPoint; /** - * A {@link LinkEnricher} can be used to append hateoas links to a specific json response. - * To register an enricher use the {@link Enrich} annotation or the {@link LinkEnricherRegistry} which is available + * A {@link HalEnricher} can be used to append hal specific attributes, such as links, to the json response. + * To register an enricher use the {@link Enrich} annotation or the {@link HalEnricherRegistry} which is available * via injection. * * <b>Warning:</b> enrichers are always registered as singletons. @@ -14,13 +14,13 @@ import sonia.scm.plugin.ExtensionPoint; */ @ExtensionPoint @FunctionalInterface -public interface LinkEnricher { +public interface HalEnricher { /** - * Enriches the response with hateoas links. + * Enriches the response with hal specific attributes. * * @param context contains the source for the json mapping and related objects - * @param appender can be used to append links to the json response + * @param appender can be used to append links or embedded objects to the json response */ - void enrich(LinkEnricherContext context, LinkAppender appender); + void enrich(HalEnricherContext context, HalAppender appender); } diff --git a/scm-core/src/main/java/sonia/scm/api/v2/resources/LinkEnricherContext.java b/scm-core/src/main/java/sonia/scm/api/v2/resources/HalEnricherContext.java similarity index 80% rename from scm-core/src/main/java/sonia/scm/api/v2/resources/LinkEnricherContext.java rename to scm-core/src/main/java/sonia/scm/api/v2/resources/HalEnricherContext.java index 2808a923e9..36128087b8 100644 --- a/scm-core/src/main/java/sonia/scm/api/v2/resources/LinkEnricherContext.java +++ b/scm-core/src/main/java/sonia/scm/api/v2/resources/HalEnricherContext.java @@ -7,17 +7,17 @@ import java.util.NoSuchElementException; import java.util.Optional; /** - * Context object for the {@link LinkEnricher}. The context holds the source object for the json and all related - * objects, which can be useful for the link creation. + * Context object for the {@link HalEnricher}. The context holds the source object for the json and all related + * objects, which can be useful for the enrichment. * * @author Sebastian Sdorra * @since 2.0.0 */ -public final class LinkEnricherContext { +public final class HalEnricherContext { private final Map<Class, Object> instanceMap; - private LinkEnricherContext(Map<Class,Object> instanceMap) { + private HalEnricherContext(Map<Class,Object> instanceMap) { this.instanceMap = instanceMap; } @@ -28,12 +28,12 @@ public final class LinkEnricherContext { * * @return context of given entries */ - public static LinkEnricherContext of(Object... instances) { + public static HalEnricherContext of(Object... instances) { ImmutableMap.Builder<Class, Object> builder = ImmutableMap.builder(); for (Object instance : instances) { builder.put(instance.getClass(), instance); } - return new LinkEnricherContext(builder.build()); + return new HalEnricherContext(builder.build()); } /** diff --git a/scm-core/src/main/java/sonia/scm/api/v2/resources/LinkEnricherRegistry.java b/scm-core/src/main/java/sonia/scm/api/v2/resources/HalEnricherRegistry.java similarity index 53% rename from scm-core/src/main/java/sonia/scm/api/v2/resources/LinkEnricherRegistry.java rename to scm-core/src/main/java/sonia/scm/api/v2/resources/HalEnricherRegistry.java index cd95a62ec3..3fadbfa388 100644 --- a/scm-core/src/main/java/sonia/scm/api/v2/resources/LinkEnricherRegistry.java +++ b/scm-core/src/main/java/sonia/scm/api/v2/resources/HalEnricherRegistry.java @@ -7,34 +7,34 @@ import sonia.scm.plugin.Extension; import javax.inject.Singleton; /** - * The {@link LinkEnricherRegistry} is responsible for binding {@link LinkEnricher} instances to their source types. + * The {@link HalEnricherRegistry} is responsible for binding {@link HalEnricher} instances to their source types. * * @author Sebastian Sdorra * @since 2.0.0 */ @Extension @Singleton -public final class LinkEnricherRegistry { +public final class HalEnricherRegistry { - private final Multimap<Class, LinkEnricher> enrichers = HashMultimap.create(); + private final Multimap<Class, HalEnricher> enrichers = HashMultimap.create(); /** - * Registers a new {@link LinkEnricher} for the given source type. + * Registers a new {@link HalEnricher} for the given source type. * * @param sourceType type of json mapping source * @param enricher link enricher instance */ - public void register(Class sourceType, LinkEnricher enricher) { + public void register(Class sourceType, HalEnricher enricher) { enrichers.put(sourceType, enricher); } /** - * Returns all registered {@link LinkEnricher} for the given type. + * Returns all registered {@link HalEnricher} for the given type. * * @param sourceType type of json mapping source * @return all registered enrichers */ - public Iterable<LinkEnricher> allByType(Class sourceType) { + public Iterable<HalEnricher> allByType(Class sourceType) { return enrichers.get(sourceType); } } diff --git a/scm-core/src/main/java/sonia/scm/api/v2/resources/Index.java b/scm-core/src/main/java/sonia/scm/api/v2/resources/Index.java index bf20f26a7a..346ce83816 100644 --- a/scm-core/src/main/java/sonia/scm/api/v2/resources/Index.java +++ b/scm-core/src/main/java/sonia/scm/api/v2/resources/Index.java @@ -1,7 +1,7 @@ package sonia.scm.api.v2.resources; /** - * The {@link Index} object can be used to register a {@link LinkEnricher} for the index resource. + * The {@link Index} object can be used to register a {@link HalEnricher} for the index resource. * * @author Sebastian Sdorra * @since 2.0.0 diff --git a/scm-core/src/main/java/sonia/scm/api/v2/resources/Me.java b/scm-core/src/main/java/sonia/scm/api/v2/resources/Me.java index f8f82804a6..a027a78d79 100644 --- a/scm-core/src/main/java/sonia/scm/api/v2/resources/Me.java +++ b/scm-core/src/main/java/sonia/scm/api/v2/resources/Me.java @@ -1,7 +1,7 @@ package sonia.scm.api.v2.resources; /** - * The {@link Me} object can be used to register a {@link LinkEnricher} for the me resource. + * The {@link Me} object can be used to register a {@link HalEnricher} for the me resource. * * @author Sebastian Sdorra * @since 2.0.0 diff --git a/scm-core/src/test/java/sonia/scm/api/v2/resources/LinkAppenderMapperTest.java b/scm-core/src/test/java/sonia/scm/api/v2/resources/HalAppenderMapperTest.java similarity index 89% rename from scm-core/src/test/java/sonia/scm/api/v2/resources/LinkAppenderMapperTest.java rename to scm-core/src/test/java/sonia/scm/api/v2/resources/HalAppenderMapperTest.java index 557eac2020..639671f423 100644 --- a/scm-core/src/test/java/sonia/scm/api/v2/resources/LinkAppenderMapperTest.java +++ b/scm-core/src/test/java/sonia/scm/api/v2/resources/HalAppenderMapperTest.java @@ -11,18 +11,18 @@ import java.util.Optional; import static org.mockito.Mockito.verify; @ExtendWith(MockitoExtension.class) -class LinkAppenderMapperTest { +class HalAppenderMapperTest { @Mock - private LinkAppender appender; + private HalAppender appender; - private LinkEnricherRegistry registry; - private LinkAppenderMapper mapper; + private HalEnricherRegistry registry; + private HalAppenderMapper mapper; @BeforeEach void beforeEach() { - registry = new LinkEnricherRegistry(); - mapper = new LinkAppenderMapper(); + registry = new HalEnricherRegistry(); + mapper = new HalAppenderMapper(); mapper.setRegistry(registry); } diff --git a/scm-core/src/test/java/sonia/scm/api/v2/resources/LinkEnricherContextTest.java b/scm-core/src/test/java/sonia/scm/api/v2/resources/HalEnricherContextTest.java similarity index 72% rename from scm-core/src/test/java/sonia/scm/api/v2/resources/LinkEnricherContextTest.java rename to scm-core/src/test/java/sonia/scm/api/v2/resources/HalEnricherContextTest.java index 6eb7bb4c84..1aecb5ad46 100644 --- a/scm-core/src/test/java/sonia/scm/api/v2/resources/LinkEnricherContextTest.java +++ b/scm-core/src/test/java/sonia/scm/api/v2/resources/HalEnricherContextTest.java @@ -7,17 +7,17 @@ import org.junit.jupiter.api.Test; import java.util.NoSuchElementException; -class LinkEnricherContextTest { +class HalEnricherContextTest { @Test void shouldCreateContextFromSingleObject() { - LinkEnricherContext context = LinkEnricherContext.of("hello"); + HalEnricherContext context = HalEnricherContext.of("hello"); assertThat(context.oneByType(String.class)).contains("hello"); } @Test void shouldCreateContextFromMultipleObjects() { - LinkEnricherContext context = LinkEnricherContext.of("hello", Integer.valueOf(42), Long.valueOf(21L)); + HalEnricherContext context = HalEnricherContext.of("hello", Integer.valueOf(42), Long.valueOf(21L)); assertThat(context.oneByType(String.class)).contains("hello"); assertThat(context.oneByType(Integer.class)).contains(42); assertThat(context.oneByType(Long.class)).contains(21L); @@ -25,19 +25,19 @@ class LinkEnricherContextTest { @Test void shouldReturnEmptyOptionalForUnknownTypes() { - LinkEnricherContext context = LinkEnricherContext.of(); + HalEnricherContext context = HalEnricherContext.of(); assertThat(context.oneByType(String.class)).isNotPresent(); } @Test void shouldReturnRequiredObject() { - LinkEnricherContext context = LinkEnricherContext.of("hello"); + HalEnricherContext context = HalEnricherContext.of("hello"); assertThat(context.oneRequireByType(String.class)).isEqualTo("hello"); } @Test void shouldThrowAnNoSuchElementExceptionForUnknownTypes() { - LinkEnricherContext context = LinkEnricherContext.of(); + HalEnricherContext context = HalEnricherContext.of(); assertThrows(NoSuchElementException.class, () -> context.oneRequireByType(String.class)); } diff --git a/scm-core/src/test/java/sonia/scm/api/v2/resources/LinkEnricherRegistryTest.java b/scm-core/src/test/java/sonia/scm/api/v2/resources/HalEnricherRegistryTest.java similarity index 53% rename from scm-core/src/test/java/sonia/scm/api/v2/resources/LinkEnricherRegistryTest.java rename to scm-core/src/test/java/sonia/scm/api/v2/resources/HalEnricherRegistryTest.java index 07441003d7..6a863d2f04 100644 --- a/scm-core/src/test/java/sonia/scm/api/v2/resources/LinkEnricherRegistryTest.java +++ b/scm-core/src/test/java/sonia/scm/api/v2/resources/HalEnricherRegistryTest.java @@ -5,54 +5,54 @@ import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; -class LinkEnricherRegistryTest { +class HalEnricherRegistryTest { - private LinkEnricherRegistry registry; + private HalEnricherRegistry registry; @BeforeEach void setUpObjectUnderTest() { - registry = new LinkEnricherRegistry(); + registry = new HalEnricherRegistry(); } @Test void shouldRegisterTheEnricher() { - SampleLinkEnricher enricher = new SampleLinkEnricher(); + SampleHalEnricher enricher = new SampleHalEnricher(); registry.register(String.class, enricher); - Iterable<LinkEnricher> enrichers = registry.allByType(String.class); + Iterable<HalEnricher> enrichers = registry.allByType(String.class); assertThat(enrichers).containsOnly(enricher); } @Test void shouldRegisterMultipleEnrichers() { - SampleLinkEnricher one = new SampleLinkEnricher(); + SampleHalEnricher one = new SampleHalEnricher(); registry.register(String.class, one); - SampleLinkEnricher two = new SampleLinkEnricher(); + SampleHalEnricher two = new SampleHalEnricher(); registry.register(String.class, two); - Iterable<LinkEnricher> enrichers = registry.allByType(String.class); + Iterable<HalEnricher> enrichers = registry.allByType(String.class); assertThat(enrichers).containsOnly(one, two); } @Test void shouldRegisterEnrichersForDifferentTypes() { - SampleLinkEnricher one = new SampleLinkEnricher(); + SampleHalEnricher one = new SampleHalEnricher(); registry.register(String.class, one); - SampleLinkEnricher two = new SampleLinkEnricher(); + SampleHalEnricher two = new SampleHalEnricher(); registry.register(Integer.class, two); - Iterable<LinkEnricher> enrichers = registry.allByType(String.class); + Iterable<HalEnricher> enrichers = registry.allByType(String.class); assertThat(enrichers).containsOnly(one); enrichers = registry.allByType(Integer.class); assertThat(enrichers).containsOnly(two); } - private static class SampleLinkEnricher implements LinkEnricher { + private static class SampleHalEnricher implements HalEnricher { @Override - public void enrich(LinkEnricherContext context, LinkAppender appender) { + public void enrich(HalEnricherContext context, HalAppender appender) { } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchToBranchDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchToBranchDtoMapper.java index 7e6f0c074c..0fdd16fa56 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchToBranchDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchToBranchDtoMapper.java @@ -15,7 +15,7 @@ import static de.otto.edison.hal.Link.linkBuilder; import static de.otto.edison.hal.Links.linkingTo; @Mapper -public abstract class BranchToBranchDtoMapper extends LinkAppenderMapper { +public abstract class BranchToBranchDtoMapper extends HalAppenderMapper { @Inject private ResourceLinks resourceLinks; @@ -31,7 +31,7 @@ public abstract class BranchToBranchDtoMapper extends LinkAppenderMapper { .single(linkBuilder("changeset", resourceLinks.changeset().changeset(namespaceAndName.getNamespace(), namespaceAndName.getName(), target.getRevision())).build()) .single(linkBuilder("source", resourceLinks.source().self(namespaceAndName.getNamespace(), namespaceAndName.getName(), target.getRevision())).build()); - appendLinks(new EdisonLinkAppender(linksBuilder), source, namespaceAndName); + appendLinks(new EdisonHalAppender(linksBuilder), source, namespaceAndName); target.add(linksBuilder.build()); } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetToChangesetDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetToChangesetDtoMapper.java index 219062d320..25bd0ef661 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetToChangesetDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetToChangesetDtoMapper.java @@ -23,7 +23,7 @@ import static de.otto.edison.hal.Link.link; import static de.otto.edison.hal.Links.linkingTo; @Mapper -public abstract class ChangesetToChangesetDtoMapper extends LinkAppenderMapper implements InstantAttributeMapper { +public abstract class ChangesetToChangesetDtoMapper extends HalAppenderMapper implements InstantAttributeMapper { @Inject private RepositoryServiceFactory serviceFactory; @@ -68,7 +68,7 @@ public abstract class ChangesetToChangesetDtoMapper extends LinkAppenderMapper i .single(link("diff", resourceLinks.diff().self(namespace, name, target.getId()))) .single(link("modifications", resourceLinks.modifications().self(namespace, name, target.getId()))); - appendLinks(new EdisonLinkAppender(linksBuilder), source, repository); + appendLinks(new EdisonHalAppender(linksBuilder), source, repository); target.add(linksBuilder.build()); } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/EdisonLinkAppender.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/EdisonHalAppender.java similarity index 91% rename from scm-webapp/src/main/java/sonia/scm/api/v2/resources/EdisonLinkAppender.java rename to scm-webapp/src/main/java/sonia/scm/api/v2/resources/EdisonHalAppender.java index c4e699cb58..14db542976 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/EdisonLinkAppender.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/EdisonHalAppender.java @@ -6,11 +6,11 @@ import de.otto.edison.hal.Links; import java.util.ArrayList; import java.util.List; -class EdisonLinkAppender implements LinkAppender { +class EdisonHalAppender implements HalAppender { private final Links.Builder builder; - EdisonLinkAppender(Links.Builder builder) { + EdisonHalAppender(Links.Builder builder) { this.builder = builder; } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapper.java index 2432d5168c..132abce71b 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapper.java @@ -12,13 +12,10 @@ import sonia.scm.repository.SubRepository; import javax.inject.Inject; -import java.util.List; -import java.util.stream.Collectors; - import static de.otto.edison.hal.Link.link; @Mapper -public abstract class FileObjectToFileObjectDtoMapper extends LinkAppenderMapper implements InstantAttributeMapper { +public abstract class FileObjectToFileObjectDtoMapper extends HalAppenderMapper implements InstantAttributeMapper { @Inject private ResourceLinks resourceLinks; @@ -39,7 +36,7 @@ public abstract class FileObjectToFileObjectDtoMapper extends LinkAppenderMapper links.single(link("history", resourceLinks.fileHistory().self(namespaceAndName.getNamespace(), namespaceAndName.getName(), revision, path))); } - appendLinks(new EdisonLinkAppender(links), fileObject, namespaceAndName, revision); + appendLinks(new EdisonHalAppender(links), fileObject, namespaceAndName, revision); dto.add(links.build()); } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupToGroupDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupToGroupDtoMapper.java index bf866af350..a728632395 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupToGroupDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupToGroupDtoMapper.java @@ -36,7 +36,7 @@ public abstract class GroupToGroupDtoMapper extends BaseMapper<Group, GroupDto> linksBuilder.single(link("permissions", resourceLinks.groupPermissions().permissions(target.getName()))); } - appendLinks(new EdisonLinkAppender(linksBuilder), group); + appendLinks(new EdisonHalAppender(linksBuilder), group); target.add(linksBuilder.build()); } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexDtoGenerator.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexDtoGenerator.java index 3eff661385..ed848c4311 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexDtoGenerator.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexDtoGenerator.java @@ -15,7 +15,7 @@ import java.util.List; import static de.otto.edison.hal.Link.link; -public class IndexDtoGenerator extends LinkAppenderMapper { +public class IndexDtoGenerator extends HalAppenderMapper { private final ResourceLinks resourceLinks; private final SCMContextProvider scmContextProvider; @@ -61,7 +61,7 @@ public class IndexDtoGenerator extends LinkAppenderMapper { builder.single(link("login", resourceLinks.authentication().jsonLogin())); } - appendLinks(new EdisonLinkAppender(builder), new Index()); + appendLinks(new EdisonHalAppender(builder), new Index()); return new IndexDto(scmContextProvider.getVersion(), builder.build()); } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/LinkEnricherAutoRegistration.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/LinkEnricherAutoRegistration.java index 890e268ed5..8472eb9fc1 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/LinkEnricherAutoRegistration.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/LinkEnricherAutoRegistration.java @@ -10,30 +10,30 @@ import javax.servlet.ServletContextListener; import java.util.Set; /** - * Registers every {@link LinkEnricher} which is annotated with an {@link Enrich} annotation. + * Registers every {@link HalEnricher} which is annotated with an {@link Enrich} annotation. */ @Extension public class LinkEnricherAutoRegistration implements ServletContextListener { private static final Logger LOG = LoggerFactory.getLogger(LinkEnricherAutoRegistration.class); - private final LinkEnricherRegistry registry; - private final Set<LinkEnricher> enrichers; + private final HalEnricherRegistry registry; + private final Set<HalEnricher> enrichers; @Inject - public LinkEnricherAutoRegistration(LinkEnricherRegistry registry, Set<LinkEnricher> enrichers) { + public LinkEnricherAutoRegistration(HalEnricherRegistry registry, Set<HalEnricher> enrichers) { this.registry = registry; this.enrichers = enrichers; } @Override public void contextInitialized(ServletContextEvent sce) { - for (LinkEnricher enricher : enrichers) { + for (HalEnricher enricher : enrichers) { Enrich annotation = enricher.getClass().getAnnotation(Enrich.class); if (annotation != null) { registry.register(annotation.value(), enricher); } else { - LOG.warn("found LinkEnricher extension {} without Enrich annotation", enricher.getClass()); + LOG.warn("found HalEnricher extension {} without Enrich annotation", enricher.getClass()); } } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MeDtoFactory.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MeDtoFactory.java index 082db7fd94..8763c54565 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MeDtoFactory.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MeDtoFactory.java @@ -16,7 +16,7 @@ import java.util.Collections; import static de.otto.edison.hal.Link.link; import static de.otto.edison.hal.Links.linkingTo; -public class MeDtoFactory extends LinkAppenderMapper { +public class MeDtoFactory extends HalAppenderMapper { private final ResourceLinks resourceLinks; private final UserManager userManager; @@ -73,7 +73,7 @@ public class MeDtoFactory extends LinkAppenderMapper { linksBuilder.single(link("password", resourceLinks.me().passwordChange())); } - appendLinks(new EdisonLinkAppender(linksBuilder), new Me(), user); + appendLinks(new EdisonHalAppender(linksBuilder), new Me(), user); target.add(linksBuilder.build()); } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapper.java index 19929b63ba..de03f1aa05 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapper.java @@ -70,7 +70,7 @@ public abstract class RepositoryToRepositoryDtoMapper extends BaseMapper<Reposit linksBuilder.single(link("changesets", resourceLinks.changeset().all(target.getNamespace(), target.getName()))); linksBuilder.single(link("sources", resourceLinks.source().selfWithoutRevision(target.getNamespace(), target.getName()))); - appendLinks(new EdisonLinkAppender(linksBuilder), repository); + appendLinks(new EdisonHalAppender(linksBuilder), repository); target.add(linksBuilder.build()); } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/TagToTagDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/TagToTagDtoMapper.java index 5ede1cb55b..4e0518a006 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/TagToTagDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/TagToTagDtoMapper.java @@ -15,7 +15,7 @@ import static de.otto.edison.hal.Link.link; import static de.otto.edison.hal.Links.linkingTo; @Mapper -public abstract class TagToTagDtoMapper extends LinkAppenderMapper { +public abstract class TagToTagDtoMapper extends HalAppenderMapper { @Inject private ResourceLinks resourceLinks; @@ -30,7 +30,7 @@ public abstract class TagToTagDtoMapper extends LinkAppenderMapper { .single(link("sources", resourceLinks.source().self(namespaceAndName.getNamespace(), namespaceAndName.getName(), target.getRevision()))) .single(link("changeset", resourceLinks.changeset().self(namespaceAndName.getNamespace(), namespaceAndName.getName(), target.getRevision()))); - appendLinks(new EdisonLinkAppender(linksBuilder), tag, namespaceAndName); + appendLinks(new EdisonHalAppender(linksBuilder), tag, namespaceAndName); target.add(linksBuilder.build()); } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserToUserDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserToUserDtoMapper.java index 3c7e9fd7f1..2465293356 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserToUserDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserToUserDtoMapper.java @@ -47,7 +47,7 @@ public abstract class UserToUserDtoMapper extends BaseMapper<User, UserDto> { linksBuilder.single(link("permissions", resourceLinks.userPermissions().permissions(target.getName()))); } - appendLinks(new EdisonLinkAppender(linksBuilder), user); + appendLinks(new EdisonHalAppender(linksBuilder), user); target.add(linksBuilder.build()); } diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/BranchToBranchDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/BranchToBranchDtoMapperTest.java index d2e202576a..7b19603218 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/BranchToBranchDtoMapperTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/BranchToBranchDtoMapperTest.java @@ -24,7 +24,7 @@ class BranchToBranchDtoMapperTest { @Test void shouldAppendLinks() { - LinkEnricherRegistry registry = new LinkEnricherRegistry(); + HalEnricherRegistry registry = new HalEnricherRegistry(); registry.register(Branch.class, (ctx, appender) -> { NamespaceAndName namespaceAndName = ctx.oneRequireByType(NamespaceAndName.class); Branch branch = ctx.oneRequireByType(Branch.class); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/EdisonLinkAppenderTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/EdisonHalAppenderTest.java similarity index 88% rename from scm-webapp/src/test/java/sonia/scm/api/v2/resources/EdisonLinkAppenderTest.java rename to scm-webapp/src/test/java/sonia/scm/api/v2/resources/EdisonHalAppenderTest.java index e97415cc09..8b326b9f05 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/EdisonLinkAppenderTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/EdisonHalAppenderTest.java @@ -10,15 +10,15 @@ import java.util.List; import static de.otto.edison.hal.Links.linkingTo; import static org.assertj.core.api.Assertions.assertThat; -class EdisonLinkAppenderTest { +class EdisonHalAppenderTest { private Links.Builder builder; - private EdisonLinkAppender appender; + private EdisonHalAppender appender; @BeforeEach void prepare() { builder = linkingTo(); - appender = new EdisonLinkAppender(builder); + appender = new EdisonHalAppender(builder); } @Test diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapperTest.java index b25410210f..9f399fe93b 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapperTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapperTest.java @@ -73,7 +73,7 @@ public class FileObjectToFileObjectDtoMapperTest { @Test public void shouldAppendLinks() { - LinkEnricherRegistry registry = new LinkEnricherRegistry(); + HalEnricherRegistry registry = new HalEnricherRegistry(); registry.register(FileObject.class, (ctx, appender) -> { NamespaceAndName repository = ctx.oneRequireByType(NamespaceAndName.class); FileObject fo = ctx.oneRequireByType(FileObject.class); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/GroupToGroupDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/GroupToGroupDtoMapperTest.java index b681dff21f..bb1e9144b2 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/GroupToGroupDtoMapperTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/GroupToGroupDtoMapperTest.java @@ -11,7 +11,6 @@ import org.mockito.InjectMocks; import sonia.scm.group.Group; import java.net.URI; -import java.net.URISyntaxException; import java.util.stream.IntStream; import static java.util.stream.Collectors.toList; @@ -91,7 +90,7 @@ public class GroupToGroupDtoMapperTest { @Test public void shouldAppendLinks() { - LinkEnricherRegistry registry = new LinkEnricherRegistry(); + HalEnricherRegistry registry = new HalEnricherRegistry(); registry.register(Group.class, (ctx, appender) -> { Group group = ctx.oneRequireByType(Group.class); appender.appendOne("some", "http://" + group.getName()); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/HalEnricherAutoRegistrationTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/HalEnricherAutoRegistrationTest.java new file mode 100644 index 0000000000..314dcf11c2 --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/HalEnricherAutoRegistrationTest.java @@ -0,0 +1,64 @@ +package sonia.scm.api.v2.resources; + +import com.google.common.collect.ImmutableSet; +import org.junit.jupiter.api.Test; + +import java.util.Set; + +import static org.assertj.core.api.Java6Assertions.assertThat; + +class HalEnricherAutoRegistrationTest { + + @Test + void shouldRegisterAllAvailableLinkEnrichers() { + HalEnricher one = new One(); + HalEnricher two = new Two(); + HalEnricher three = new Three(); + HalEnricher four = new Four(); + Set<HalEnricher> enrichers = ImmutableSet.of(one, two, three, four); + + HalEnricherRegistry registry = new HalEnricherRegistry(); + + LinkEnricherAutoRegistration autoRegistration = new LinkEnricherAutoRegistration(registry, enrichers); + autoRegistration.contextInitialized(null); + + assertThat(registry.allByType(String.class)).containsOnly(one, two); + assertThat(registry.allByType(Integer.class)).containsOnly(three); + } + + @Enrich(String.class) + public static class One implements HalEnricher { + + @Override + public void enrich(HalEnricherContext context, HalAppender appender) { + + } + } + + @Enrich(String.class) + public static class Two implements HalEnricher { + + @Override + public void enrich(HalEnricherContext context, HalAppender appender) { + + } + } + + @Enrich(Integer.class) + public static class Three implements HalEnricher { + + @Override + public void enrich(HalEnricherContext context, HalAppender appender) { + + } + } + + public static class Four implements HalEnricher { + + @Override + public void enrich(HalEnricherContext context, HalAppender appender) { + + } + } + +} diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/LinkEnricherAutoRegistrationTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/LinkEnricherAutoRegistrationTest.java deleted file mode 100644 index a2b72abc49..0000000000 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/LinkEnricherAutoRegistrationTest.java +++ /dev/null @@ -1,64 +0,0 @@ -package sonia.scm.api.v2.resources; - -import com.google.common.collect.ImmutableSet; -import org.junit.jupiter.api.Test; - -import java.util.Set; - -import static org.assertj.core.api.Java6Assertions.assertThat; - -class LinkEnricherAutoRegistrationTest { - - @Test - void shouldRegisterAllAvailableLinkEnrichers() { - LinkEnricher one = new One(); - LinkEnricher two = new Two(); - LinkEnricher three = new Three(); - LinkEnricher four = new Four(); - Set<LinkEnricher> enrichers = ImmutableSet.of(one, two, three, four); - - LinkEnricherRegistry registry = new LinkEnricherRegistry(); - - LinkEnricherAutoRegistration autoRegistration = new LinkEnricherAutoRegistration(registry, enrichers); - autoRegistration.contextInitialized(null); - - assertThat(registry.allByType(String.class)).containsOnly(one, two); - assertThat(registry.allByType(Integer.class)).containsOnly(three); - } - - @Enrich(String.class) - public static class One implements LinkEnricher { - - @Override - public void enrich(LinkEnricherContext context, LinkAppender appender) { - - } - } - - @Enrich(String.class) - public static class Two implements LinkEnricher { - - @Override - public void enrich(LinkEnricherContext context, LinkAppender appender) { - - } - } - - @Enrich(Integer.class) - public static class Three implements LinkEnricher { - - @Override - public void enrich(LinkEnricherContext context, LinkAppender appender) { - - } - } - - public static class Four implements LinkEnricher { - - @Override - public void enrich(LinkEnricherContext context, LinkAppender appender) { - - } - } - -} diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MeDtoFactoryTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MeDtoFactoryTest.java index 138387938b..226a09231d 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MeDtoFactoryTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MeDtoFactoryTest.java @@ -15,7 +15,6 @@ import org.mockito.quality.Strictness; import sonia.scm.group.GroupNames; import sonia.scm.user.User; import sonia.scm.user.UserManager; -import sonia.scm.user.UserPermissions; import sonia.scm.user.UserTestData; import java.net.URI; @@ -170,7 +169,7 @@ class MeDtoFactoryTest { void shouldAppendLinks() { prepareSubject(UserTestData.createTrillian()); - LinkEnricherRegistry registry = new LinkEnricherRegistry(); + HalEnricherRegistry registry = new HalEnricherRegistry(); meDtoFactory.setRegistry(registry); registry.register(Me.class, (ctx, appender) -> { diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapperTest.java index 8469e966c8..9a478d1ecb 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapperTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapperTest.java @@ -211,7 +211,7 @@ public class RepositoryToRepositoryDtoMapperTest { @Test public void shouldAppendLinks() { - LinkEnricherRegistry registry = new LinkEnricherRegistry(); + HalEnricherRegistry registry = new HalEnricherRegistry(); registry.register(Repository.class, (ctx, appender) -> { Repository repository = ctx.oneRequireByType(Repository.class); appender.appendOne("id", "http://" + repository.getId()); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/TagToTagDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/TagToTagDtoMapperTest.java index aa8eb3e7ab..03e726a197 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/TagToTagDtoMapperTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/TagToTagDtoMapperTest.java @@ -22,7 +22,7 @@ class TagToTagDtoMapperTest { @Test void shouldAppendLinks() { - LinkEnricherRegistry registry = new LinkEnricherRegistry(); + HalEnricherRegistry registry = new HalEnricherRegistry(); registry.register(Tag.class, (ctx, appender) -> { NamespaceAndName repository = ctx.oneRequireByType(NamespaceAndName.class); Tag tag = ctx.oneRequireByType(Tag.class); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserToUserDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserToUserDtoMapperTest.java index 9924dae81b..708b9e1e72 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserToUserDtoMapperTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserToUserDtoMapperTest.java @@ -155,7 +155,7 @@ public class UserToUserDtoMapperTest { public void shouldAppendLink() { User trillian = UserTestData.createTrillian(); - LinkEnricherRegistry registry = new LinkEnricherRegistry(); + HalEnricherRegistry registry = new HalEnricherRegistry(); registry.register(User.class, (ctx, appender) -> appender.appendOne("sample", "http://" + ctx.oneByType(User.class).get().getName())); mapper.setRegistry(registry); From 462cccb443f275c99242810ea868b361d0d00884 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra <sebastian.sdorra@cloudogu.com> Date: Mon, 4 Feb 2019 14:41:07 +0100 Subject: [PATCH 076/120] rename HalAppender methods to make clear, that they target links --- .../scm/api/v2/resources/HalAppender.java | 4 ++-- .../v2/resources/HalAppenderMapperTest.java | 20 +++++++++---------- .../api/v2/resources/EdisonHalAppender.java | 4 ++-- .../BranchToBranchDtoMapperTest.java | 2 +- .../v2/resources/EdisonHalAppenderTest.java | 4 ++-- .../FileObjectToFileObjectDtoMapperTest.java | 2 +- .../resources/GroupToGroupDtoMapperTest.java | 2 +- .../api/v2/resources/MeDtoFactoryTest.java | 2 +- .../RepositoryToRepositoryDtoMapperTest.java | 2 +- .../v2/resources/TagToTagDtoMapperTest.java | 2 +- .../v2/resources/UserToUserDtoMapperTest.java | 2 +- 11 files changed, 23 insertions(+), 23 deletions(-) diff --git a/scm-core/src/main/java/sonia/scm/api/v2/resources/HalAppender.java b/scm-core/src/main/java/sonia/scm/api/v2/resources/HalAppender.java index 14cd5153e6..4b7687133f 100644 --- a/scm-core/src/main/java/sonia/scm/api/v2/resources/HalAppender.java +++ b/scm-core/src/main/java/sonia/scm/api/v2/resources/HalAppender.java @@ -14,7 +14,7 @@ public interface HalAppender { * @param rel name of relation * @param href link uri */ - void appendOne(String rel, String href); + void appendLink(String rel, String href); /** * Returns a builder which is able to append an array of links to the resource. @@ -22,7 +22,7 @@ public interface HalAppender { * @param rel name of link relation * @return multi link builder */ - LinkArrayBuilder arrayBuilder(String rel); + LinkArrayBuilder linkArrayBuilder(String rel); /** diff --git a/scm-core/src/test/java/sonia/scm/api/v2/resources/HalAppenderMapperTest.java b/scm-core/src/test/java/sonia/scm/api/v2/resources/HalAppenderMapperTest.java index 639671f423..2505131a9a 100644 --- a/scm-core/src/test/java/sonia/scm/api/v2/resources/HalAppenderMapperTest.java +++ b/scm-core/src/test/java/sonia/scm/api/v2/resources/HalAppenderMapperTest.java @@ -28,34 +28,34 @@ class HalAppenderMapperTest { @Test void shouldAppendSimpleLink() { - registry.register(String.class, (ctx, appender) -> appender.appendOne("42", "https://hitchhiker.com")); + registry.register(String.class, (ctx, appender) -> appender.appendLink("42", "https://hitchhiker.com")); mapper.appendLinks(appender, "hello"); - verify(appender).appendOne("42", "https://hitchhiker.com"); + verify(appender).appendLink("42", "https://hitchhiker.com"); } @Test void shouldCallMultipleEnrichers() { - registry.register(String.class, (ctx, appender) -> appender.appendOne("42", "https://hitchhiker.com")); - registry.register(String.class, (ctx, appender) -> appender.appendOne("21", "https://scm.hitchhiker.com")); + registry.register(String.class, (ctx, appender) -> appender.appendLink("42", "https://hitchhiker.com")); + registry.register(String.class, (ctx, appender) -> appender.appendLink("21", "https://scm.hitchhiker.com")); mapper.appendLinks(appender, "hello"); - verify(appender).appendOne("42", "https://hitchhiker.com"); - verify(appender).appendOne("21", "https://scm.hitchhiker.com"); + verify(appender).appendLink("42", "https://hitchhiker.com"); + verify(appender).appendLink("21", "https://scm.hitchhiker.com"); } @Test void shouldAppendLinkByUsingSourceFromContext() { registry.register(String.class, (ctx, appender) -> { Optional<String> rel = ctx.oneByType(String.class); - appender.appendOne(rel.get(), "https://hitchhiker.com"); + appender.appendLink(rel.get(), "https://hitchhiker.com"); }); mapper.appendLinks(appender, "42"); - verify(appender).appendOne("42", "https://hitchhiker.com"); + verify(appender).appendLink("42", "https://hitchhiker.com"); } @Test @@ -63,12 +63,12 @@ class HalAppenderMapperTest { registry.register(Integer.class, (ctx, appender) -> { Optional<Integer> rel = ctx.oneByType(Integer.class); Optional<String> href = ctx.oneByType(String.class); - appender.appendOne(String.valueOf(rel.get()), href.get()); + appender.appendLink(String.valueOf(rel.get()), href.get()); }); mapper.appendLinks(appender, Integer.valueOf(42), "https://hitchhiker.com"); - verify(appender).appendOne("42", "https://hitchhiker.com"); + verify(appender).appendLink("42", "https://hitchhiker.com"); } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/EdisonHalAppender.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/EdisonHalAppender.java index 14db542976..7d25f26e53 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/EdisonHalAppender.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/EdisonHalAppender.java @@ -15,12 +15,12 @@ class EdisonHalAppender implements HalAppender { } @Override - public void appendOne(String rel, String href) { + public void appendLink(String rel, String href) { builder.single(Link.link(rel, href)); } @Override - public LinkArrayBuilder arrayBuilder(String rel) { + public LinkArrayBuilder linkArrayBuilder(String rel) { return new EdisonLinkArrayBuilder(builder, rel); } diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/BranchToBranchDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/BranchToBranchDtoMapperTest.java index 7b19603218..3e64ab95b6 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/BranchToBranchDtoMapperTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/BranchToBranchDtoMapperTest.java @@ -29,7 +29,7 @@ class BranchToBranchDtoMapperTest { NamespaceAndName namespaceAndName = ctx.oneRequireByType(NamespaceAndName.class); Branch branch = ctx.oneRequireByType(Branch.class); - appender.appendOne("ka", "http://" + namespaceAndName.logString() + "/" + branch.getName()); + appender.appendLink("ka", "http://" + namespaceAndName.logString() + "/" + branch.getName()); }); mapper.setRegistry(registry); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/EdisonHalAppenderTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/EdisonHalAppenderTest.java index 8b326b9f05..766cd2f863 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/EdisonHalAppenderTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/EdisonHalAppenderTest.java @@ -23,7 +23,7 @@ class EdisonHalAppenderTest { @Test void shouldAppendOneLink() { - appender.appendOne("self", "https://scm.hitchhiker.com"); + appender.appendLink("self", "https://scm.hitchhiker.com"); Links links = builder.build(); assertThat(links.getLinkBy("self").get().getHref()).isEqualTo("https://scm.hitchhiker.com"); @@ -31,7 +31,7 @@ class EdisonHalAppenderTest { @Test void shouldAppendMultipleLinks() { - appender.arrayBuilder("items") + appender.linkArrayBuilder("items") .append("one", "http://one") .append("two", "http://two") .build(); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapperTest.java index 9f399fe93b..55058a1684 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapperTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapperTest.java @@ -79,7 +79,7 @@ public class FileObjectToFileObjectDtoMapperTest { FileObject fo = ctx.oneRequireByType(FileObject.class); String rev = ctx.oneRequireByType(String.class); - appender.appendOne("hog", "http://" + repository.logString() + "/" + fo.getName() + "/" + rev); + appender.appendLink("hog", "http://" + repository.logString() + "/" + fo.getName() + "/" + rev); }); mapper.setRegistry(registry); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/GroupToGroupDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/GroupToGroupDtoMapperTest.java index bb1e9144b2..045124ad91 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/GroupToGroupDtoMapperTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/GroupToGroupDtoMapperTest.java @@ -93,7 +93,7 @@ public class GroupToGroupDtoMapperTest { HalEnricherRegistry registry = new HalEnricherRegistry(); registry.register(Group.class, (ctx, appender) -> { Group group = ctx.oneRequireByType(Group.class); - appender.appendOne("some", "http://" + group.getName()); + appender.appendLink("some", "http://" + group.getName()); }); mapper.setRegistry(registry); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MeDtoFactoryTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MeDtoFactoryTest.java index 226a09231d..8a00c69229 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MeDtoFactoryTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MeDtoFactoryTest.java @@ -174,7 +174,7 @@ class MeDtoFactoryTest { registry.register(Me.class, (ctx, appender) -> { User user = ctx.oneRequireByType(User.class); - appender.appendOne("profile", "http://hitchhiker.com/users/" + user.getName()); + appender.appendLink("profile", "http://hitchhiker.com/users/" + user.getName()); }); MeDto dto = meDtoFactory.create(); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapperTest.java index 9a478d1ecb..9df7273680 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapperTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapperTest.java @@ -214,7 +214,7 @@ public class RepositoryToRepositoryDtoMapperTest { HalEnricherRegistry registry = new HalEnricherRegistry(); registry.register(Repository.class, (ctx, appender) -> { Repository repository = ctx.oneRequireByType(Repository.class); - appender.appendOne("id", "http://" + repository.getId()); + appender.appendLink("id", "http://" + repository.getId()); }); mapper.setRegistry(registry); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/TagToTagDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/TagToTagDtoMapperTest.java index 03e726a197..cd3c18de27 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/TagToTagDtoMapperTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/TagToTagDtoMapperTest.java @@ -26,7 +26,7 @@ class TagToTagDtoMapperTest { registry.register(Tag.class, (ctx, appender) -> { NamespaceAndName repository = ctx.oneRequireByType(NamespaceAndName.class); Tag tag = ctx.oneRequireByType(Tag.class); - appender.appendOne("yo", "http://" + repository.logString() + "/" + tag.getName()); + appender.appendLink("yo", "http://" + repository.logString() + "/" + tag.getName()); }); mapper.setRegistry(registry); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserToUserDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserToUserDtoMapperTest.java index 708b9e1e72..ae1d75dddf 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserToUserDtoMapperTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserToUserDtoMapperTest.java @@ -156,7 +156,7 @@ public class UserToUserDtoMapperTest { User trillian = UserTestData.createTrillian(); HalEnricherRegistry registry = new HalEnricherRegistry(); - registry.register(User.class, (ctx, appender) -> appender.appendOne("sample", "http://" + ctx.oneByType(User.class).get().getName())); + registry.register(User.class, (ctx, appender) -> appender.appendLink("sample", "http://" + ctx.oneByType(User.class).get().getName())); mapper.setRegistry(registry); UserDto userDto = mapper.map(trillian); From e6335367e756f6c2e88e97f1c75cae2f1cb001ca Mon Sep 17 00:00:00 2001 From: Mohamed Karray <mohamed.karray.sr@gmail.com> Date: Mon, 4 Feb 2019 15:20:34 +0100 Subject: [PATCH 077/120] add extension point for the redirect route --- scm-ui/src/containers/Main.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/scm-ui/src/containers/Main.js b/scm-ui/src/containers/Main.js index d2bc50faf2..a8b6c056b6 100644 --- a/scm-ui/src/containers/Main.js +++ b/scm-ui/src/containers/Main.js @@ -35,7 +35,12 @@ class Main extends React.Component<Props> { return ( <div className="main"> <Switch> - <Redirect exact path="/" to="/repos" /> + <ExtensionPoint + name="redirect-route" + props={{authenticated, links}} + > + <Redirect exact path="/" to="/repos"/> + </ExtensionPoint> <Route exact path="/login" component={Login} /> <Route path="/logout" component={Logout} /> <ProtectedRoute From 21441aed45884181d838ed9c93b8ba78162244d0 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra <sebastian.sdorra@cloudogu.com> Date: Mon, 4 Feb 2019 16:18:47 +0100 Subject: [PATCH 078/120] added appendEmbedded method to HalAppender With this change we could not longer use @AfterMapping from MapStruct. We should use @ObjectFactory instead and create Links and Embedded before. --- .../scm/api/v2/resources/HalAppender.java | 9 +++++ .../api/v2/resources/HalAppenderMapper.java | 2 +- .../v2/resources/HalAppenderMapperTest.java | 8 ++-- .../sonia/scm/api/v2/resources/BranchDto.java | 6 +-- .../v2/resources/BranchToBranchDtoMapper.java | 21 +++++----- .../scm/api/v2/resources/ChangesetDto.java | 15 ++------ .../ChangesetToChangesetDtoMapper.java | 29 +++++++------- .../api/v2/resources/EdisonHalAppender.java | 19 +++++++--- .../scm/api/v2/resources/FileObjectDto.java | 7 ++-- .../FileObjectToFileObjectDtoMapper.java | 16 ++++---- .../sonia/scm/api/v2/resources/GroupDto.java | 11 ++---- .../v2/resources/GroupToGroupDtoMapper.java | 31 ++++++++------- .../sonia/scm/api/v2/resources/IndexDto.java | 5 ++- .../api/v2/resources/IndexDtoGenerator.java | 7 +++- .../sonia/scm/api/v2/resources/MeDto.java | 7 ++-- .../scm/api/v2/resources/MeDtoFactory.java | 19 +++++----- .../scm/api/v2/resources/RepositoryDto.java | 10 ++--- .../RepositoryToRepositoryDtoMapper.java | 38 ++++++++++--------- .../sonia/scm/api/v2/resources/TagDto.java | 7 ++-- .../api/v2/resources/TagToTagDtoMapper.java | 20 +++++----- .../sonia/scm/api/v2/resources/UserDto.java | 7 ++-- .../api/v2/resources/UserToUserDtoMapper.java | 24 ++++++------ .../v2/resources/EdisonHalAppenderTest.java | 28 +++++++++++--- .../test/java/sonia/scm/it/GitLfsITCase.java | 11 ++++-- 24 files changed, 196 insertions(+), 161 deletions(-) diff --git a/scm-core/src/main/java/sonia/scm/api/v2/resources/HalAppender.java b/scm-core/src/main/java/sonia/scm/api/v2/resources/HalAppender.java index 4b7687133f..a7beaf1f6e 100644 --- a/scm-core/src/main/java/sonia/scm/api/v2/resources/HalAppender.java +++ b/scm-core/src/main/java/sonia/scm/api/v2/resources/HalAppender.java @@ -1,5 +1,7 @@ package sonia.scm.api.v2.resources; +import de.otto.edison.hal.HalRepresentation; + /** * The {@link HalAppender} can be used within an {@link HalEnricher} to append hateoas links to a json response. * @@ -24,6 +26,13 @@ public interface HalAppender { */ LinkArrayBuilder linkArrayBuilder(String rel); + /** + * Appends one embedded to the json response. + * + * @param rel name of relation + * @param embeddedItem embedded object + */ + void appendEmbedded(String rel, HalRepresentation embeddedItem); /** * Builder for link arrays. diff --git a/scm-core/src/main/java/sonia/scm/api/v2/resources/HalAppenderMapper.java b/scm-core/src/main/java/sonia/scm/api/v2/resources/HalAppenderMapper.java index 04310efad2..dd49b765bc 100644 --- a/scm-core/src/main/java/sonia/scm/api/v2/resources/HalAppenderMapper.java +++ b/scm-core/src/main/java/sonia/scm/api/v2/resources/HalAppenderMapper.java @@ -14,7 +14,7 @@ public class HalAppenderMapper { this.registry = registry; } - protected void appendLinks(HalAppender appender, Object source, Object... contextEntries) { + protected void applyEnrichers(HalAppender appender, Object source, Object... contextEntries) { // null check is only their to not break existing tests if (registry != null) { diff --git a/scm-core/src/test/java/sonia/scm/api/v2/resources/HalAppenderMapperTest.java b/scm-core/src/test/java/sonia/scm/api/v2/resources/HalAppenderMapperTest.java index 2505131a9a..ff658cc26a 100644 --- a/scm-core/src/test/java/sonia/scm/api/v2/resources/HalAppenderMapperTest.java +++ b/scm-core/src/test/java/sonia/scm/api/v2/resources/HalAppenderMapperTest.java @@ -30,7 +30,7 @@ class HalAppenderMapperTest { void shouldAppendSimpleLink() { registry.register(String.class, (ctx, appender) -> appender.appendLink("42", "https://hitchhiker.com")); - mapper.appendLinks(appender, "hello"); + mapper.applyEnrichers(appender, "hello"); verify(appender).appendLink("42", "https://hitchhiker.com"); } @@ -40,7 +40,7 @@ class HalAppenderMapperTest { registry.register(String.class, (ctx, appender) -> appender.appendLink("42", "https://hitchhiker.com")); registry.register(String.class, (ctx, appender) -> appender.appendLink("21", "https://scm.hitchhiker.com")); - mapper.appendLinks(appender, "hello"); + mapper.applyEnrichers(appender, "hello"); verify(appender).appendLink("42", "https://hitchhiker.com"); verify(appender).appendLink("21", "https://scm.hitchhiker.com"); @@ -53,7 +53,7 @@ class HalAppenderMapperTest { appender.appendLink(rel.get(), "https://hitchhiker.com"); }); - mapper.appendLinks(appender, "42"); + mapper.applyEnrichers(appender, "42"); verify(appender).appendLink("42", "https://hitchhiker.com"); } @@ -66,7 +66,7 @@ class HalAppenderMapperTest { appender.appendLink(String.valueOf(rel.get()), href.get()); }); - mapper.appendLinks(appender, Integer.valueOf(42), "https://hitchhiker.com"); + mapper.applyEnrichers(appender, Integer.valueOf(42), "https://hitchhiker.com"); verify(appender).appendLink("42", "https://hitchhiker.com"); } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchDto.java index f5bdc850ab..343d9c8bc8 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchDto.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchDto.java @@ -1,5 +1,6 @@ package sonia.scm.api.v2.resources; +import de.otto.edison.hal.Embedded; import de.otto.edison.hal.HalRepresentation; import de.otto.edison.hal.Links; import lombok.Getter; @@ -12,8 +13,7 @@ public class BranchDto extends HalRepresentation { private String name; private String revision; - @Override - protected HalRepresentation add(Links links) { - return super.add(links); + BranchDto(Links links, Embedded embedded) { + super(links, embedded); } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchToBranchDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchToBranchDtoMapper.java index 0fdd16fa56..c940b1ffd9 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchToBranchDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchToBranchDtoMapper.java @@ -1,11 +1,11 @@ package sonia.scm.api.v2.resources; +import de.otto.edison.hal.Embedded; import de.otto.edison.hal.Links; -import org.mapstruct.AfterMapping; import org.mapstruct.Context; import org.mapstruct.Mapper; import org.mapstruct.Mapping; -import org.mapstruct.MappingTarget; +import org.mapstruct.ObjectFactory; import sonia.scm.repository.Branch; import sonia.scm.repository.NamespaceAndName; @@ -23,16 +23,17 @@ public abstract class BranchToBranchDtoMapper extends HalAppenderMapper { @Mapping(target = "attributes", ignore = true) // We do not map HAL attributes public abstract BranchDto map(Branch branch, @Context NamespaceAndName namespaceAndName); - @AfterMapping - void appendLinks(Branch source, @MappingTarget BranchDto target, @Context NamespaceAndName namespaceAndName) { + @ObjectFactory + BranchDto createDto(@Context NamespaceAndName namespaceAndName, Branch branch) { Links.Builder linksBuilder = linkingTo() - .self(resourceLinks.branch().self(namespaceAndName, target.getName())) - .single(linkBuilder("history", resourceLinks.branch().history(namespaceAndName, target.getName())).build()) - .single(linkBuilder("changeset", resourceLinks.changeset().changeset(namespaceAndName.getNamespace(), namespaceAndName.getName(), target.getRevision())).build()) - .single(linkBuilder("source", resourceLinks.source().self(namespaceAndName.getNamespace(), namespaceAndName.getName(), target.getRevision())).build()); + .self(resourceLinks.branch().self(namespaceAndName, branch.getName())) + .single(linkBuilder("history", resourceLinks.branch().history(namespaceAndName, branch.getName())).build()) + .single(linkBuilder("changeset", resourceLinks.changeset().changeset(namespaceAndName.getNamespace(), namespaceAndName.getName(), branch.getRevision())).build()) + .single(linkBuilder("source", resourceLinks.source().self(namespaceAndName.getNamespace(), namespaceAndName.getName(), branch.getRevision())).build()); - appendLinks(new EdisonHalAppender(linksBuilder), source, namespaceAndName); + Embedded.Builder embeddedBuilder = Embedded.embeddedBuilder(); + applyEnrichers(new EdisonHalAppender(linksBuilder, embeddedBuilder), branch, namespaceAndName); - target.add(linksBuilder.build()); + return new BranchDto(linksBuilder.build(), embeddedBuilder.build()); } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetDto.java index 162d5d6699..6759f5cb8c 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetDto.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetDto.java @@ -1,5 +1,6 @@ package sonia.scm.api.v2.resources; +import de.otto.edison.hal.Embedded; import de.otto.edison.hal.HalRepresentation; import de.otto.edison.hal.Links; import lombok.Getter; @@ -7,7 +8,6 @@ import lombok.NoArgsConstructor; import lombok.Setter; import java.time.Instant; -import java.util.List; @Getter @Setter @@ -34,16 +34,7 @@ public class ChangesetDto extends HalRepresentation { */ private String description; - @Override - @SuppressWarnings("squid:S1185") // We want to have this method available in this package - protected HalRepresentation add(Links links) { - return super.add(links); + public ChangesetDto(Links links, Embedded embedded) { + super(links, embedded); } - - @SuppressWarnings("squid:S1185") // We want to have this method available in this package - protected HalRepresentation withEmbedded(String rel, List<? extends HalRepresentation> halRepresentations) { - return super.withEmbedded(rel, halRepresentations); - } - - } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetToChangesetDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetToChangesetDtoMapper.java index 25bd0ef661..16e33aac5f 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetToChangesetDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetToChangesetDtoMapper.java @@ -1,11 +1,11 @@ package sonia.scm.api.v2.resources; +import de.otto.edison.hal.Embedded; import de.otto.edison.hal.Links; -import org.mapstruct.AfterMapping; import org.mapstruct.Context; import org.mapstruct.Mapper; import org.mapstruct.Mapping; -import org.mapstruct.MappingTarget; +import org.mapstruct.ObjectFactory; import sonia.scm.repository.Branch; import sonia.scm.repository.Changeset; import sonia.scm.repository.Repository; @@ -19,6 +19,7 @@ import java.util.List; import java.util.function.Function; import java.util.stream.Collectors; +import static de.otto.edison.hal.Embedded.embeddedBuilder; import static de.otto.edison.hal.Link.link; import static de.otto.edison.hal.Links.linkingTo; @@ -31,7 +32,6 @@ public abstract class ChangesetToChangesetDtoMapper extends HalAppenderMapper im @Inject private ResourceLinks resourceLinks; - @Inject private BranchCollectionToDtoMapper branchCollectionToDtoMapper; @@ -46,31 +46,34 @@ public abstract class ChangesetToChangesetDtoMapper extends HalAppenderMapper im public abstract ChangesetDto map(Changeset changeset, @Context Repository repository); - @AfterMapping - void appendLinks(Changeset source, @MappingTarget ChangesetDto target, @Context Repository repository) { + @ObjectFactory + ChangesetDto createDto(@Context Repository repository, Changeset source) { String namespace = repository.getNamespace(); String name = repository.getName(); + Embedded.Builder embeddedBuilder = embeddedBuilder(); + try (RepositoryService repositoryService = serviceFactory.create(repository)) { if (repositoryService.isSupported(Command.TAGS)) { - target.withEmbedded("tags", tagCollectionToDtoMapper.getTagDtoList(namespace, name, + embeddedBuilder.with("tags", tagCollectionToDtoMapper.getTagDtoList(namespace, name, getListOfObjects(source.getTags(), tagName -> new Tag(tagName, source.getId())))); } if (repositoryService.isSupported(Command.BRANCHES)) { - target.withEmbedded("branches", branchCollectionToDtoMapper.getBranchDtoList(namespace, name, + embeddedBuilder.with("branches", branchCollectionToDtoMapper.getBranchDtoList(namespace, name, getListOfObjects(source.getBranches(), branchName -> new Branch(branchName, source.getId())))); } } - target.withEmbedded("parents", getListOfObjects(source.getParents(), parent -> changesetToParentDtoMapper.map(new Changeset(parent, 0L, null), repository))); + embeddedBuilder.with("parents", getListOfObjects(source.getParents(), parent -> changesetToParentDtoMapper.map(new Changeset(parent, 0L, null), repository))); Links.Builder linksBuilder = linkingTo() - .self(resourceLinks.changeset().self(repository.getNamespace(), repository.getName(), target.getId())) - .single(link("diff", resourceLinks.diff().self(namespace, name, target.getId()))) - .single(link("modifications", resourceLinks.modifications().self(namespace, name, target.getId()))); + .self(resourceLinks.changeset().self(repository.getNamespace(), repository.getName(), source.getId())) + .single(link("diff", resourceLinks.diff().self(namespace, name, source.getId()))) + .single(link("modifications", resourceLinks.modifications().self(namespace, name, source.getId()))); - appendLinks(new EdisonHalAppender(linksBuilder), source, repository); - target.add(linksBuilder.build()); + applyEnrichers(new EdisonHalAppender(linksBuilder, embeddedBuilder), source, repository); + + return new ChangesetDto(linksBuilder.build(), embeddedBuilder.build()); } private <T> List<T> getListOfObjects(List<String> list, Function<String, T> mapFunction) { diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/EdisonHalAppender.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/EdisonHalAppender.java index 7d25f26e53..769de2b705 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/EdisonHalAppender.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/EdisonHalAppender.java @@ -1,5 +1,7 @@ package sonia.scm.api.v2.resources; +import de.otto.edison.hal.Embedded; +import de.otto.edison.hal.HalRepresentation; import de.otto.edison.hal.Link; import de.otto.edison.hal.Links; @@ -8,20 +10,27 @@ import java.util.List; class EdisonHalAppender implements HalAppender { - private final Links.Builder builder; + private final Links.Builder linkBuilder; + private final Embedded.Builder embeddedBuilder; - EdisonHalAppender(Links.Builder builder) { - this.builder = builder; + EdisonHalAppender(Links.Builder linkBuilder, Embedded.Builder embeddedBuilder) { + this.linkBuilder = linkBuilder; + this.embeddedBuilder = embeddedBuilder; } @Override public void appendLink(String rel, String href) { - builder.single(Link.link(rel, href)); + linkBuilder.single(Link.link(rel, href)); } @Override public LinkArrayBuilder linkArrayBuilder(String rel) { - return new EdisonLinkArrayBuilder(builder, rel); + return new EdisonLinkArrayBuilder(linkBuilder, rel); + } + + @Override + public void appendEmbedded(String rel, HalRepresentation embedded) { + embeddedBuilder.with(rel, embedded); } private static class EdisonLinkArrayBuilder implements LinkArrayBuilder { diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectDto.java index c183d731c6..0bce564e35 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectDto.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectDto.java @@ -1,6 +1,7 @@ package sonia.scm.api.v2.resources; import com.fasterxml.jackson.annotation.JsonInclude; +import de.otto.edison.hal.Embedded; import de.otto.edison.hal.HalRepresentation; import de.otto.edison.hal.Links; import lombok.Getter; @@ -27,10 +28,8 @@ public class FileObjectDto extends HalRepresentation { @JsonInclude(JsonInclude.Include.NON_EMPTY) private String revision; - @Override - @SuppressWarnings("squid:S1185") // We want to have this method available in this package - protected HalRepresentation add(Links links) { - return super.add(links); + public FileObjectDto(Links links, Embedded embedded) { + super(links, embedded); } public void setChildren(List<FileObjectDto> children) { diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapper.java index 132abce71b..608dea9f26 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapper.java @@ -1,17 +1,18 @@ package sonia.scm.api.v2.resources; +import de.otto.edison.hal.Embedded; import de.otto.edison.hal.Links; -import org.mapstruct.AfterMapping; import org.mapstruct.Context; import org.mapstruct.Mapper; import org.mapstruct.Mapping; -import org.mapstruct.MappingTarget; +import org.mapstruct.ObjectFactory; import sonia.scm.repository.FileObject; import sonia.scm.repository.NamespaceAndName; import sonia.scm.repository.SubRepository; import javax.inject.Inject; +import static de.otto.edison.hal.Embedded.embeddedBuilder; import static de.otto.edison.hal.Link.link; @Mapper @@ -25,20 +26,21 @@ public abstract class FileObjectToFileObjectDtoMapper extends HalAppenderMapper abstract SubRepositoryDto mapSubrepository(SubRepository subRepository); - @AfterMapping - void addLinks(FileObject fileObject, @MappingTarget FileObjectDto dto, @Context NamespaceAndName namespaceAndName, @Context String revision) { + @ObjectFactory + FileObjectDto createDto(@Context NamespaceAndName namespaceAndName, @Context String revision, FileObject fileObject) { String path = removeFirstSlash(fileObject.getPath()); Links.Builder links = Links.linkingTo(); - if (dto.isDirectory()) { + if (fileObject.isDirectory()) { links.self(resourceLinks.source().sourceWithPath(namespaceAndName.getNamespace(), namespaceAndName.getName(), revision, path)); } else { links.self(resourceLinks.source().content(namespaceAndName.getNamespace(), namespaceAndName.getName(), revision, path)); links.single(link("history", resourceLinks.fileHistory().self(namespaceAndName.getNamespace(), namespaceAndName.getName(), revision, path))); } - appendLinks(new EdisonHalAppender(links), fileObject, namespaceAndName, revision); + Embedded.Builder embeddedBuilder = embeddedBuilder(); + applyEnrichers(new EdisonHalAppender(links, embeddedBuilder), fileObject, namespaceAndName, revision); - dto.add(links.build()); + return new FileObjectDto(links.build(), embeddedBuilder.build()); } private String removeFirstSlash(String source) { 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 760beab1da..2ccfcff38e 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 @@ -1,6 +1,7 @@ package sonia.scm.api.v2.resources; import com.fasterxml.jackson.annotation.JsonInclude; +import de.otto.edison.hal.Embedded; import de.otto.edison.hal.HalRepresentation; import de.otto.edison.hal.Links; import lombok.Getter; @@ -28,13 +29,7 @@ public class GroupDto extends HalRepresentation { private Map<String, String> properties; private List<String> members; - @Override - @SuppressWarnings("squid:S1185") // We want to have this method available in this package - protected HalRepresentation add(Links links) { - return super.add(links); - } - - public HalRepresentation withMembers(List<MemberDto> members) { - return super.withEmbedded("members", members); + GroupDto(Links links, Embedded embedded) { + super(links, embedded); } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupToGroupDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupToGroupDtoMapper.java index a728632395..7d5ddae548 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupToGroupDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupToGroupDtoMapper.java @@ -1,9 +1,9 @@ package sonia.scm.api.v2.resources; +import de.otto.edison.hal.Embedded; import de.otto.edison.hal.Links; -import org.mapstruct.AfterMapping; import org.mapstruct.Mapper; -import org.mapstruct.MappingTarget; +import org.mapstruct.ObjectFactory; import sonia.scm.group.Group; import sonia.scm.group.GroupPermissions; import sonia.scm.security.PermissionPermissions; @@ -12,6 +12,7 @@ import javax.inject.Inject; import java.util.List; import java.util.stream.Collectors; +import static de.otto.edison.hal.Embedded.embeddedBuilder; import static de.otto.edison.hal.Link.link; import static de.otto.edison.hal.Links.linkingTo; @@ -23,28 +24,26 @@ public abstract class GroupToGroupDtoMapper extends BaseMapper<Group, GroupDto> @Inject private ResourceLinks resourceLinks; - @AfterMapping - void appendLinks(Group group, @MappingTarget GroupDto target) { - Links.Builder linksBuilder = linkingTo().self(resourceLinks.group().self(target.getName())); + @ObjectFactory + GroupDto createDto(Group group) { + Links.Builder linksBuilder = linkingTo().self(resourceLinks.group().self(group.getName())); if (GroupPermissions.delete(group).isPermitted()) { - linksBuilder.single(link("delete", resourceLinks.group().delete(target.getName()))); + linksBuilder.single(link("delete", resourceLinks.group().delete(group.getName()))); } if (GroupPermissions.modify(group).isPermitted()) { - linksBuilder.single(link("update", resourceLinks.group().update(target.getName()))); + linksBuilder.single(link("update", resourceLinks.group().update(group.getName()))); } if (PermissionPermissions.read().isPermitted()) { - linksBuilder.single(link("permissions", resourceLinks.groupPermissions().permissions(target.getName()))); + linksBuilder.single(link("permissions", resourceLinks.groupPermissions().permissions(group.getName()))); } - appendLinks(new EdisonHalAppender(linksBuilder), group); - - target.add(linksBuilder.build()); - } - - @AfterMapping - void mapMembers(Group group, @MappingTarget GroupDto target) { + Embedded.Builder embeddedBuilder = embeddedBuilder(); List<MemberDto> memberDtos = group.getMembers().stream().map(this::createMember).collect(Collectors.toList()); - target.withMembers(memberDtos); + embeddedBuilder.with("members", memberDtos); + + applyEnrichers(new EdisonHalAppender(linksBuilder, embeddedBuilder), group); + + return new GroupDto(linksBuilder.build(), embeddedBuilder.build()); } private MemberDto createMember(String name) { diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexDto.java index 9346420f58..16f945332d 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexDto.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexDto.java @@ -1,5 +1,6 @@ package sonia.scm.api.v2.resources; +import de.otto.edison.hal.Embedded; import de.otto.edison.hal.HalRepresentation; import de.otto.edison.hal.Links; import lombok.Getter; @@ -9,8 +10,8 @@ public class IndexDto extends HalRepresentation { private final String version; - IndexDto(String version, Links links) { - super(links); + IndexDto(Links links, Embedded embedded, String version) { + super(links, embedded); this.version = version; } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexDtoGenerator.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexDtoGenerator.java index ed848c4311..90445bcdc2 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexDtoGenerator.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexDtoGenerator.java @@ -1,6 +1,7 @@ package sonia.scm.api.v2.resources; import com.google.common.collect.Lists; +import de.otto.edison.hal.Embedded; import de.otto.edison.hal.Link; import de.otto.edison.hal.Links; import org.apache.shiro.SecurityUtils; @@ -13,6 +14,7 @@ import sonia.scm.user.UserPermissions; import javax.inject.Inject; import java.util.List; +import static de.otto.edison.hal.Embedded.embeddedBuilder; import static de.otto.edison.hal.Link.link; public class IndexDtoGenerator extends HalAppenderMapper { @@ -61,8 +63,9 @@ public class IndexDtoGenerator extends HalAppenderMapper { builder.single(link("login", resourceLinks.authentication().jsonLogin())); } - appendLinks(new EdisonHalAppender(builder), new Index()); + Embedded.Builder embeddedBuilder = embeddedBuilder(); + applyEnrichers(new EdisonHalAppender(builder, embeddedBuilder), new Index()); - return new IndexDto(scmContextProvider.getVersion(), builder.build()); + return new IndexDto(builder.build(), embeddedBuilder.build(), scmContextProvider.getVersion()); } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MeDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MeDto.java index 5488faca28..84fbbfe290 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MeDto.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MeDto.java @@ -1,5 +1,6 @@ package sonia.scm.api.v2.resources; +import de.otto.edison.hal.Embedded; import de.otto.edison.hal.HalRepresentation; import de.otto.edison.hal.Links; import lombok.Getter; @@ -18,9 +19,7 @@ public class MeDto extends HalRepresentation { private String mail; private List<String> groups; - @Override - @SuppressWarnings("squid:S1185") // We want to have this method available in this package - protected HalRepresentation add(Links links) { - return super.add(links); + MeDto(Links links, Embedded embedded) { + super(links, embedded); } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MeDtoFactory.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MeDtoFactory.java index 8763c54565..b5e1998066 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MeDtoFactory.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MeDtoFactory.java @@ -1,6 +1,7 @@ package sonia.scm.api.v2.resources; import com.google.common.collect.ImmutableList; +import de.otto.edison.hal.Embedded; import de.otto.edison.hal.Links; import org.apache.shiro.SecurityUtils; import org.apache.shiro.subject.PrincipalCollection; @@ -13,6 +14,7 @@ import sonia.scm.user.UserPermissions; import javax.inject.Inject; import java.util.Collections; +import static de.otto.edison.hal.Embedded.embeddedBuilder; import static de.otto.edison.hal.Link.link; import static de.otto.edison.hal.Links.linkingTo; @@ -29,15 +31,11 @@ public class MeDtoFactory extends HalAppenderMapper { public MeDto create() { PrincipalCollection principals = getPrincipalCollection(); - - MeDto dto = new MeDto(); - User user = principals.oneByType(User.class); + MeDto dto = createDto(user); mapUserProperties(user, dto); mapGroups(principals, dto); - - appendLinks(user, dto); return dto; } @@ -61,21 +59,22 @@ public class MeDtoFactory extends HalAppenderMapper { } - private void appendLinks(User user, MeDto target) { + private MeDto createDto(User user) { Links.Builder linksBuilder = linkingTo().self(resourceLinks.me().self()); if (UserPermissions.delete(user).isPermitted()) { - linksBuilder.single(link("delete", resourceLinks.me().delete(target.getName()))); + linksBuilder.single(link("delete", resourceLinks.me().delete(user.getName()))); } if (UserPermissions.modify(user).isPermitted()) { - linksBuilder.single(link("update", resourceLinks.me().update(target.getName()))); + linksBuilder.single(link("update", resourceLinks.me().update(user.getName()))); } if (userManager.isTypeDefault(user) && UserPermissions.changePassword(user).isPermitted()) { linksBuilder.single(link("password", resourceLinks.me().passwordChange())); } - appendLinks(new EdisonHalAppender(linksBuilder), new Me(), user); + Embedded.Builder embeddedBuilder = embeddedBuilder(); + applyEnrichers(new EdisonHalAppender(linksBuilder, embeddedBuilder), new Me(), user); - target.add(linksBuilder.build()); + return new MeDto(linksBuilder.build(), embeddedBuilder.build()); } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryDto.java index ddfe432d73..8b48311bba 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryDto.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryDto.java @@ -1,9 +1,11 @@ package sonia.scm.api.v2.resources; import com.fasterxml.jackson.annotation.JsonInclude; +import de.otto.edison.hal.Embedded; import de.otto.edison.hal.HalRepresentation; import de.otto.edison.hal.Links; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.Setter; import org.hibernate.validator.constraints.Email; import org.hibernate.validator.constraints.NotEmpty; @@ -13,7 +15,7 @@ import java.time.Instant; import java.util.List; import java.util.Map; -@Getter @Setter +@Getter @Setter @NoArgsConstructor public class RepositoryDto extends HalRepresentation { @Email @@ -31,9 +33,7 @@ public class RepositoryDto extends HalRepresentation { private String type; protected Map<String, String> properties; - @Override - @SuppressWarnings("squid:S1185") // We want to have this method available in this package - protected HalRepresentation add(Links links) { - return super.add(links); + RepositoryDto(Links links, Embedded embedded) { + super(links, embedded); } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapper.java index de03f1aa05..9e680b7e5c 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapper.java @@ -1,11 +1,11 @@ package sonia.scm.api.v2.resources; import com.google.inject.Inject; +import de.otto.edison.hal.Embedded; import de.otto.edison.hal.Link; import de.otto.edison.hal.Links; -import org.mapstruct.AfterMapping; import org.mapstruct.Mapper; -import org.mapstruct.MappingTarget; +import org.mapstruct.ObjectFactory; import sonia.scm.repository.Feature; import sonia.scm.repository.HealthCheckFailure; import sonia.scm.repository.Repository; @@ -17,6 +17,7 @@ import sonia.scm.repository.api.ScmProtocol; import java.util.List; +import static de.otto.edison.hal.Embedded.embeddedBuilder; import static de.otto.edison.hal.Link.link; import static de.otto.edison.hal.Links.linkingTo; import static java.util.stream.Collectors.toList; @@ -33,17 +34,17 @@ public abstract class RepositoryToRepositoryDtoMapper extends BaseMapper<Reposit abstract HealthCheckFailureDto toDto(HealthCheckFailure failure); - @AfterMapping - void appendLinks(Repository repository, @MappingTarget RepositoryDto target) { - Links.Builder linksBuilder = linkingTo().self(resourceLinks.repository().self(target.getNamespace(), target.getName())); + @ObjectFactory + RepositoryDto createDto(Repository repository) { + Links.Builder linksBuilder = linkingTo().self(resourceLinks.repository().self(repository.getNamespace(), repository.getName())); if (RepositoryPermissions.delete(repository).isPermitted()) { - linksBuilder.single(link("delete", resourceLinks.repository().delete(target.getNamespace(), target.getName()))); + linksBuilder.single(link("delete", resourceLinks.repository().delete(repository.getNamespace(), repository.getName()))); } if (RepositoryPermissions.modify(repository).isPermitted()) { - linksBuilder.single(link("update", resourceLinks.repository().update(target.getNamespace(), target.getName()))); + linksBuilder.single(link("update", resourceLinks.repository().update(repository.getNamespace(), repository.getName()))); } if (RepositoryPermissions.permissionRead(repository).isPermitted()) { - linksBuilder.single(link("permissions", resourceLinks.repositoryPermission().all(target.getNamespace(), target.getName()))); + linksBuilder.single(link("permissions", resourceLinks.repositoryPermission().all(repository.getNamespace(), repository.getName()))); } try (RepositoryService repositoryService = serviceFactory.create(repository)) { if (RepositoryPermissions.pull(repository).isPermitted()) { @@ -53,26 +54,27 @@ public abstract class RepositoryToRepositoryDtoMapper extends BaseMapper<Reposit linksBuilder.array(protocolLinks); } if (repositoryService.isSupported(Command.TAGS)) { - linksBuilder.single(link("tags", resourceLinks.tag().all(target.getNamespace(), target.getName()))); + linksBuilder.single(link("tags", resourceLinks.tag().all(repository.getNamespace(), repository.getName()))); } if (repositoryService.isSupported(Command.BRANCHES)) { - linksBuilder.single(link("branches", resourceLinks.branchCollection().self(target.getNamespace(), target.getName()))); + linksBuilder.single(link("branches", resourceLinks.branchCollection().self(repository.getNamespace(), repository.getName()))); } if (repositoryService.isSupported(Feature.INCOMING_REVISION)) { - linksBuilder.single(link("incomingChangesets", resourceLinks.incoming().changesets(target.getNamespace(), target.getName()))); - linksBuilder.single(link("incomingDiff", resourceLinks.incoming().diff(target.getNamespace(), target.getName()))); + linksBuilder.single(link("incomingChangesets", resourceLinks.incoming().changesets(repository.getNamespace(), repository.getName()))); + linksBuilder.single(link("incomingDiff", resourceLinks.incoming().diff(repository.getNamespace(), repository.getName()))); } if (repositoryService.isSupported(Command.MERGE)) { - linksBuilder.single(link("merge", resourceLinks.merge().merge(target.getNamespace(), target.getName()))); - linksBuilder.single(link("mergeDryRun", resourceLinks.merge().dryRun(target.getNamespace(), target.getName()))); + linksBuilder.single(link("merge", resourceLinks.merge().merge(repository.getNamespace(), repository.getName()))); + linksBuilder.single(link("mergeDryRun", resourceLinks.merge().dryRun(repository.getNamespace(), repository.getName()))); } } - linksBuilder.single(link("changesets", resourceLinks.changeset().all(target.getNamespace(), target.getName()))); - linksBuilder.single(link("sources", resourceLinks.source().selfWithoutRevision(target.getNamespace(), target.getName()))); + linksBuilder.single(link("changesets", resourceLinks.changeset().all(repository.getNamespace(), repository.getName()))); + linksBuilder.single(link("sources", resourceLinks.source().selfWithoutRevision(repository.getNamespace(), repository.getName()))); - appendLinks(new EdisonHalAppender(linksBuilder), repository); + Embedded.Builder embeddedBuilder = embeddedBuilder(); + applyEnrichers(new EdisonHalAppender(linksBuilder, embeddedBuilder), repository); - target.add(linksBuilder.build()); + return new RepositoryDto(linksBuilder.build(), embeddedBuilder.build()); } private Link createProtocolLink(ScmProtocol protocol) { diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/TagDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/TagDto.java index 8af036f5a3..a3d4c5d17e 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/TagDto.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/TagDto.java @@ -1,5 +1,6 @@ package sonia.scm.api.v2.resources; +import de.otto.edison.hal.Embedded; import de.otto.edison.hal.HalRepresentation; import de.otto.edison.hal.Links; import lombok.Getter; @@ -15,10 +16,8 @@ public class TagDto extends HalRepresentation { private String revision; - @Override - @SuppressWarnings("squid:S1185") // We want to have this method available in this package - protected HalRepresentation add(Links links) { - return super.add(links); + TagDto(Links links, Embedded embedded) { + super(links, embedded); } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/TagToTagDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/TagToTagDtoMapper.java index 4e0518a006..3a7faf3155 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/TagToTagDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/TagToTagDtoMapper.java @@ -1,16 +1,17 @@ package sonia.scm.api.v2.resources; +import de.otto.edison.hal.Embedded; import de.otto.edison.hal.Links; -import org.mapstruct.AfterMapping; import org.mapstruct.Context; import org.mapstruct.Mapper; import org.mapstruct.Mapping; -import org.mapstruct.MappingTarget; +import org.mapstruct.ObjectFactory; import sonia.scm.repository.NamespaceAndName; import sonia.scm.repository.Tag; import javax.inject.Inject; +import static de.otto.edison.hal.Embedded.embeddedBuilder; import static de.otto.edison.hal.Link.link; import static de.otto.edison.hal.Links.linkingTo; @@ -23,15 +24,16 @@ public abstract class TagToTagDtoMapper extends HalAppenderMapper { @Mapping(target = "attributes", ignore = true) // We do not map HAL attributes public abstract TagDto map(Tag tag, @Context NamespaceAndName namespaceAndName); - @AfterMapping - void appendLinks(Tag tag, @MappingTarget TagDto target, @Context NamespaceAndName namespaceAndName) { + @ObjectFactory + TagDto createDto(@Context NamespaceAndName namespaceAndName, Tag tag) { Links.Builder linksBuilder = linkingTo() - .self(resourceLinks.tag().self(namespaceAndName.getNamespace(), namespaceAndName.getName(), target.getName())) - .single(link("sources", resourceLinks.source().self(namespaceAndName.getNamespace(), namespaceAndName.getName(), target.getRevision()))) - .single(link("changeset", resourceLinks.changeset().self(namespaceAndName.getNamespace(), namespaceAndName.getName(), target.getRevision()))); + .self(resourceLinks.tag().self(namespaceAndName.getNamespace(), namespaceAndName.getName(), tag.getName())) + .single(link("sources", resourceLinks.source().self(namespaceAndName.getNamespace(), namespaceAndName.getName(), tag.getRevision()))) + .single(link("changeset", resourceLinks.changeset().self(namespaceAndName.getNamespace(), namespaceAndName.getName(), tag.getRevision()))); - appendLinks(new EdisonHalAppender(linksBuilder), tag, namespaceAndName); + Embedded.Builder embeddedBuilder = embeddedBuilder(); + applyEnrichers(new EdisonHalAppender(linksBuilder, embeddedBuilder), tag, namespaceAndName); - target.add(linksBuilder.build()); + return new TagDto(linksBuilder.build(), embeddedBuilder.build()); } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserDto.java index 9dc5b850bd..0ee7b2f82c 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserDto.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserDto.java @@ -1,6 +1,7 @@ package sonia.scm.api.v2.resources; import com.fasterxml.jackson.annotation.JsonInclude; +import de.otto.edison.hal.Embedded; import de.otto.edison.hal.HalRepresentation; import de.otto.edison.hal.Links; import lombok.Getter; @@ -33,9 +34,7 @@ public class UserDto extends HalRepresentation { private String type; private Map<String, String> properties; - @Override - @SuppressWarnings("squid:S1185") // We want to have this method available in this package - protected HalRepresentation add(Links links) { - return super.add(links); + UserDto(Links links, Embedded embedded) { + super(links, embedded); } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserToUserDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserToUserDtoMapper.java index 2465293356..ac641e3e66 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserToUserDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserToUserDtoMapper.java @@ -1,10 +1,10 @@ package sonia.scm.api.v2.resources; +import de.otto.edison.hal.Embedded; import de.otto.edison.hal.Links; -import org.mapstruct.AfterMapping; import org.mapstruct.Mapper; import org.mapstruct.Mapping; -import org.mapstruct.MappingTarget; +import org.mapstruct.ObjectFactory; import sonia.scm.security.PermissionPermissions; import sonia.scm.user.User; import sonia.scm.user.UserManager; @@ -12,6 +12,7 @@ import sonia.scm.user.UserPermissions; import javax.inject.Inject; +import static de.otto.edison.hal.Embedded.embeddedBuilder; import static de.otto.edison.hal.Link.link; import static de.otto.edison.hal.Links.linkingTo; @@ -31,25 +32,26 @@ public abstract class UserToUserDtoMapper extends BaseMapper<User, UserDto> { @Inject private ResourceLinks resourceLinks; - @AfterMapping - protected void appendLinks(User user, @MappingTarget UserDto target) { - Links.Builder linksBuilder = linkingTo().self(resourceLinks.user().self(target.getName())); + @ObjectFactory + UserDto createDto(User user) { + Links.Builder linksBuilder = linkingTo().self(resourceLinks.user().self(user.getName())); if (UserPermissions.delete(user).isPermitted()) { - linksBuilder.single(link("delete", resourceLinks.user().delete(target.getName()))); + linksBuilder.single(link("delete", resourceLinks.user().delete(user.getName()))); } if (UserPermissions.modify(user).isPermitted()) { - linksBuilder.single(link("update", resourceLinks.user().update(target.getName()))); + linksBuilder.single(link("update", resourceLinks.user().update(user.getName()))); if (userManager.isTypeDefault(user)) { - linksBuilder.single(link("password", resourceLinks.user().passwordChange(target.getName()))); + linksBuilder.single(link("password", resourceLinks.user().passwordChange(user.getName()))); } } if (PermissionPermissions.read().isPermitted()) { - linksBuilder.single(link("permissions", resourceLinks.userPermissions().permissions(target.getName()))); + linksBuilder.single(link("permissions", resourceLinks.userPermissions().permissions(user.getName()))); } - appendLinks(new EdisonHalAppender(linksBuilder), user); + Embedded.Builder embeddedBuilder = embeddedBuilder(); + applyEnrichers(new EdisonHalAppender(linksBuilder, embeddedBuilder), user); - target.add(linksBuilder.build()); + return new UserDto(linksBuilder.build(), embeddedBuilder.build()); } } diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/EdisonHalAppenderTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/EdisonHalAppenderTest.java index 766cd2f863..ff149c5dc5 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/EdisonHalAppenderTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/EdisonHalAppenderTest.java @@ -1,5 +1,7 @@ package sonia.scm.api.v2.resources; +import de.otto.edison.hal.Embedded; +import de.otto.edison.hal.HalRepresentation; import de.otto.edison.hal.Link; import de.otto.edison.hal.Links; import org.junit.jupiter.api.BeforeEach; @@ -7,25 +9,28 @@ import org.junit.jupiter.api.Test; import java.util.List; +import static de.otto.edison.hal.Embedded.embeddedBuilder; import static de.otto.edison.hal.Links.linkingTo; import static org.assertj.core.api.Assertions.assertThat; class EdisonHalAppenderTest { - private Links.Builder builder; + private Links.Builder linksBuilder; + private Embedded.Builder embeddedBuilder; private EdisonHalAppender appender; @BeforeEach void prepare() { - builder = linkingTo(); - appender = new EdisonHalAppender(builder); + linksBuilder = linkingTo(); + embeddedBuilder = embeddedBuilder(); + appender = new EdisonHalAppender(linksBuilder, embeddedBuilder); } @Test void shouldAppendOneLink() { appender.appendLink("self", "https://scm.hitchhiker.com"); - Links links = builder.build(); + Links links = linksBuilder.build(); assertThat(links.getLinkBy("self").get().getHref()).isEqualTo("https://scm.hitchhiker.com"); } @@ -36,8 +41,21 @@ class EdisonHalAppenderTest { .append("two", "http://two") .build(); - List<Link> items = builder.build().getLinksBy("items"); + List<Link> items = linksBuilder.build().getLinksBy("items"); assertThat(items).hasSize(2); } + @Test + void shouldAppendEmbedded() { + HalRepresentation one = new HalRepresentation(); + appender.appendEmbedded("one", one); + + HalRepresentation two = new HalRepresentation(); + appender.appendEmbedded("two", new HalRepresentation()); + + Embedded embedded = embeddedBuilder.build(); + assertThat(embedded.getItemsBy("one")).containsOnly(one); + assertThat(embedded.getItemsBy("two")).containsOnly(two); + } + } diff --git a/scm-webapp/src/test/java/sonia/scm/it/GitLfsITCase.java b/scm-webapp/src/test/java/sonia/scm/it/GitLfsITCase.java index 8cdb162740..a367d171a1 100644 --- a/scm-webapp/src/test/java/sonia/scm/it/GitLfsITCase.java +++ b/scm-webapp/src/test/java/sonia/scm/it/GitLfsITCase.java @@ -136,10 +136,13 @@ public class GitLfsITCase { } private void createUser(User user) { - UserDto dto = new UserToUserDtoMapperImpl(){ - @Override - protected void appendLinks(User user, UserDto target) {} - }.map(user); + UserDto dto = new UserDto(); + dto.setName(user.getName()); + dto.setMail(user.getMail()); + dto.setDisplayName(user.getDisplayName()); + dto.setType(user.getType()); + dto.setActive(user.isActive()); + dto.setAdmin(user.isAdmin()); dto.setPassword(user.getPassword()); createResource(adminClient, "users") .accept("*/*") From 7369f1cfced2c258ad21778cb3c234a1e91032be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= <rene.pfeuffer@cloudogu.com> Date: Mon, 4 Feb 2019 16:33:03 +0100 Subject: [PATCH 079/120] Ensure that verbs occur only once in the collection This is necessary for equals to work correctly --- .../java/sonia/scm/repository/RepositoryPermission.java | 9 +++++---- .../sonia/scm/repository/RepositoryPermissionTest.java | 9 +++++++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/scm-core/src/main/java/sonia/scm/repository/RepositoryPermission.java b/scm-core/src/main/java/sonia/scm/repository/RepositoryPermission.java index 5fd14f2e84..54163e0393 100644 --- a/scm-core/src/main/java/sonia/scm/repository/RepositoryPermission.java +++ b/scm-core/src/main/java/sonia/scm/repository/RepositoryPermission.java @@ -46,9 +46,10 @@ import javax.xml.bind.annotation.XmlRootElement; import java.io.Serializable; import java.util.Collection; import java.util.LinkedHashSet; +import java.util.Set; import static java.util.Collections.emptyList; -import static java.util.Collections.unmodifiableCollection; +import static java.util.Collections.unmodifiableSet; //~--- JDK imports ------------------------------------------------------------ @@ -67,7 +68,7 @@ public class RepositoryPermission implements PermissionObject, Serializable private boolean groupPermission = false; private String name; @XmlElement(name = "verb") - private Collection<String> verbs; + private Set<String> verbs; /** * Constructs a new {@link RepositoryPermission}. @@ -78,7 +79,7 @@ public class RepositoryPermission implements PermissionObject, Serializable public RepositoryPermission(String name, Collection<String> verbs, boolean groupPermission) { this.name = name; - this.verbs = unmodifiableCollection(new LinkedHashSet<>(verbs)); + this.verbs = unmodifiableSet(new LinkedHashSet<>(verbs)); this.groupPermission = groupPermission; } @@ -209,6 +210,6 @@ public class RepositoryPermission implements PermissionObject, Serializable */ public void setVerbs(Collection<String> verbs) { - this.verbs = verbs; + this.verbs = unmodifiableSet(new LinkedHashSet<>(verbs)); } } diff --git a/scm-core/src/test/java/sonia/scm/repository/RepositoryPermissionTest.java b/scm-core/src/test/java/sonia/scm/repository/RepositoryPermissionTest.java index 2e9383b2e2..d65358a66e 100644 --- a/scm-core/src/test/java/sonia/scm/repository/RepositoryPermissionTest.java +++ b/scm-core/src/test/java/sonia/scm/repository/RepositoryPermissionTest.java @@ -46,4 +46,13 @@ class RepositoryPermissionTest { assertThat(permission1).isNotEqualTo(permission2); } + + @Test + void shouldBeEqualWithRedundantVerbs() { + RepositoryPermission permission1 = new RepositoryPermission("name1", asList("one", "two"), false); + RepositoryPermission permission2 = new RepositoryPermission("name1", asList("one", "two"), false); + permission2.setVerbs(asList("one", "two", "two")); + + assertThat(permission1).isEqualTo(permission2); + } } From f93a35d97073bb70ff982a835be1d418b79aba30 Mon Sep 17 00:00:00 2001 From: Philipp Czora <philipp.czora@cloudogu.com> Date: Mon, 4 Feb 2019 16:26:00 +0000 Subject: [PATCH 080/120] Close branch feature/embedded_enricher From 9c3639409f13bebd999118abe97493e74f2b301a Mon Sep 17 00:00:00 2001 From: Mohamed Karray <mohamed.karray.sr@gmail.com> Date: Tue, 5 Feb 2019 08:45:03 +0100 Subject: [PATCH 081/120] fix redirect route --- scm-ui/src/containers/Main.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/scm-ui/src/containers/Main.js b/scm-ui/src/containers/Main.js index a8b6c056b6..e963fb00f9 100644 --- a/scm-ui/src/containers/Main.js +++ b/scm-ui/src/containers/Main.js @@ -10,7 +10,7 @@ import Login from "../containers/Login"; import Logout from "../containers/Logout"; import { ProtectedRoute } from "@scm-manager/ui-components"; -import { ExtensionPoint } from "@scm-manager/ui-extensions"; +import {binder, ExtensionPoint } from "@scm-manager/ui-extensions"; import AddUser from "../users/containers/AddUser"; import SingleUser from "../users/containers/SingleUser"; @@ -32,15 +32,15 @@ type Props = { class Main extends React.Component<Props> { render() { const { authenticated, links } = this.props; + const redirectUrlFactory = binder.getExtension("main.redirect", this.props); + let url ="/repos"; + if (redirectUrlFactory){ + url = redirectUrlFactory(this.props); + } return ( <div className="main"> <Switch> - <ExtensionPoint - name="redirect-route" - props={{authenticated, links}} - > - <Redirect exact path="/" to="/repos"/> - </ExtensionPoint> + <Redirect exact path="/" to={url}/> <Route exact path="/login" component={Login} /> <Route path="/logout" component={Logout} /> <ProtectedRoute From 1af2035ffad04d29de87577fa25d0063c362b81c Mon Sep 17 00:00:00 2001 From: Mohamed Karray <mohamed.karray.sr@gmail.com> Date: Tue, 5 Feb 2019 09:45:09 +0100 Subject: [PATCH 082/120] merge --- .../DefaultChangesetToChangesetDtoMapper.java | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/DefaultChangesetToChangesetDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/DefaultChangesetToChangesetDtoMapper.java index 924209d3da..b04d45047a 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/DefaultChangesetToChangesetDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/DefaultChangesetToChangesetDtoMapper.java @@ -1,11 +1,11 @@ package sonia.scm.api.v2.resources; +import de.otto.edison.hal.Embedded; import de.otto.edison.hal.Links; -import org.mapstruct.AfterMapping; import org.mapstruct.Context; import org.mapstruct.Mapper; import org.mapstruct.Mapping; -import org.mapstruct.MappingTarget; +import org.mapstruct.ObjectFactory; import sonia.scm.repository.Branch; import sonia.scm.repository.Changeset; import sonia.scm.repository.Repository; @@ -19,11 +19,12 @@ import java.util.List; import java.util.function.Function; import java.util.stream.Collectors; +import static de.otto.edison.hal.Embedded.embeddedBuilder; import static de.otto.edison.hal.Link.link; import static de.otto.edison.hal.Links.linkingTo; @Mapper -public abstract class DefaultChangesetToChangesetDtoMapper extends LinkAppenderMapper implements InstantAttributeMapper , ChangesetToChangesetDtoMapper { +public abstract class DefaultChangesetToChangesetDtoMapper extends HalAppenderMapper implements InstantAttributeMapper, ChangesetToChangesetDtoMapper{ @Inject private RepositoryServiceFactory serviceFactory; @@ -31,7 +32,6 @@ public abstract class DefaultChangesetToChangesetDtoMapper extends LinkAppenderM @Inject private ResourceLinks resourceLinks; - @Inject private BranchCollectionToDtoMapper branchCollectionToDtoMapper; @@ -46,31 +46,34 @@ public abstract class DefaultChangesetToChangesetDtoMapper extends LinkAppenderM public abstract ChangesetDto map(Changeset changeset, @Context Repository repository); - @AfterMapping - void appendLinks(Changeset source, @MappingTarget ChangesetDto target, @Context Repository repository) { + @ObjectFactory + ChangesetDto createDto(@Context Repository repository, Changeset source) { String namespace = repository.getNamespace(); String name = repository.getName(); + Embedded.Builder embeddedBuilder = embeddedBuilder(); + try (RepositoryService repositoryService = serviceFactory.create(repository)) { if (repositoryService.isSupported(Command.TAGS)) { - target.withEmbedded("tags", tagCollectionToDtoMapper.getTagDtoList(namespace, name, + embeddedBuilder.with("tags", tagCollectionToDtoMapper.getTagDtoList(namespace, name, getListOfObjects(source.getTags(), tagName -> new Tag(tagName, source.getId())))); } if (repositoryService.isSupported(Command.BRANCHES)) { - target.withEmbedded("branches", branchCollectionToDtoMapper.getBranchDtoList(namespace, name, + embeddedBuilder.with("branches", branchCollectionToDtoMapper.getBranchDtoList(namespace, name, getListOfObjects(source.getBranches(), branchName -> new Branch(branchName, source.getId())))); } } - target.withEmbedded("parents", getListOfObjects(source.getParents(), parent -> changesetToParentDtoMapper.map(new Changeset(parent, 0L, null), repository))); + embeddedBuilder.with("parents", getListOfObjects(source.getParents(), parent -> changesetToParentDtoMapper.map(new Changeset(parent, 0L, null), repository))); Links.Builder linksBuilder = linkingTo() - .self(resourceLinks.changeset().self(repository.getNamespace(), repository.getName(), target.getId())) - .single(link("diff", resourceLinks.diff().self(namespace, name, target.getId()))) - .single(link("modifications", resourceLinks.modifications().self(namespace, name, target.getId()))); + .self(resourceLinks.changeset().self(repository.getNamespace(), repository.getName(), source.getId())) + .single(link("diff", resourceLinks.diff().self(namespace, name, source.getId()))) + .single(link("modifications", resourceLinks.modifications().self(namespace, name, source.getId()))); - appendLinks(new EdisonLinkAppender(linksBuilder), source, repository); - target.add(linksBuilder.build()); + applyEnrichers(new EdisonHalAppender(linksBuilder, embeddedBuilder), source, repository); + + return new ChangesetDto(linksBuilder.build(), embeddedBuilder.build()); } private <T> List<T> getListOfObjects(List<String> list, Function<String, T> mapFunction) { From 8d4f06d7b1b900ad76a819d17b6f6095bdb12721 Mon Sep 17 00:00:00 2001 From: Mohamed Karray <mohamed.karray.sr@gmail.com> Date: Tue, 5 Feb 2019 13:38:21 +0100 Subject: [PATCH 083/120] add the sources link to the changeset dto --- .../api/v2/resources/DefaultChangesetToChangesetDtoMapper.java | 1 + 1 file changed, 1 insertion(+) diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/DefaultChangesetToChangesetDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/DefaultChangesetToChangesetDtoMapper.java index b04d45047a..479a43aef1 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/DefaultChangesetToChangesetDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/DefaultChangesetToChangesetDtoMapper.java @@ -68,6 +68,7 @@ public abstract class DefaultChangesetToChangesetDtoMapper extends HalAppenderMa Links.Builder linksBuilder = linkingTo() .self(resourceLinks.changeset().self(repository.getNamespace(), repository.getName(), source.getId())) .single(link("diff", resourceLinks.diff().self(namespace, name, source.getId()))) + .single(link("sources", resourceLinks.source().self(namespace, name, source.getId()))) .single(link("modifications", resourceLinks.modifications().self(namespace, name, source.getId()))); From 6415efe9bf32770e3546d512c143d093f8cd5521 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= <rene.pfeuffer@cloudogu.com> Date: Tue, 5 Feb 2019 14:35:50 +0000 Subject: [PATCH 084/120] Close branch feature/move_changesetdto From 18b64ebde2e34b36f484de186a4c980bb9899547 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra <sebastian.sdorra@cloudogu.com> Date: Tue, 5 Feb 2019 16:47:22 +0100 Subject: [PATCH 085/120] fix GZipResponseFilter b using the HttpServletRequest to check if the client supports gzip --- .../sonia/scm/filter/GZipResponseFilter.java | 19 ++++++++++++++----- .../scm/filter/GZipResponseFilterTest.java | 16 +++++++++++----- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/scm-core/src/main/java/sonia/scm/filter/GZipResponseFilter.java b/scm-core/src/main/java/sonia/scm/filter/GZipResponseFilter.java index 92dd033af0..ef0ec8a5ef 100644 --- a/scm-core/src/main/java/sonia/scm/filter/GZipResponseFilter.java +++ b/scm-core/src/main/java/sonia/scm/filter/GZipResponseFilter.java @@ -3,9 +3,11 @@ package sonia.scm.filter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.inject.Inject; +import javax.inject.Provider; +import javax.servlet.http.HttpServletRequest; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.ext.Provider; import javax.ws.rs.ext.WriterInterceptor; import javax.ws.rs.ext.WriterInterceptorContext; import java.io.IOException; @@ -13,14 +15,21 @@ import java.io.OutputStream; import java.util.Locale; import java.util.zip.GZIPOutputStream; -@Provider +@javax.ws.rs.ext.Provider public class GZipResponseFilter implements WriterInterceptor { private static final Logger LOG = LoggerFactory.getLogger(GZipResponseFilter.class); + private final Provider<HttpServletRequest> requestProvider; + + @Inject + public GZipResponseFilter(Provider<HttpServletRequest> requestProvider) { + this.requestProvider = requestProvider; + } + @Override public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException { - if (isGZipSupported(context)) { + if (isGZipSupported()) { LOG.trace("compress output with gzip"); encodeWithGZip(context); } else { @@ -43,8 +52,8 @@ public class GZipResponseFilter implements WriterInterceptor { } } - private boolean isGZipSupported(WriterInterceptorContext context) { - Object encoding = context.getHeaders().getFirst(HttpHeaders.ACCEPT_ENCODING); + private boolean isGZipSupported() { + Object encoding = requestProvider.get().getHeader(HttpHeaders.ACCEPT_ENCODING); return encoding != null && encoding.toString().toLowerCase(Locale.ENGLISH).contains("gzip"); } } diff --git a/scm-core/src/test/java/sonia/scm/filter/GZipResponseFilterTest.java b/scm-core/src/test/java/sonia/scm/filter/GZipResponseFilterTest.java index b0a704c765..c3648fd4b8 100644 --- a/scm-core/src/test/java/sonia/scm/filter/GZipResponseFilterTest.java +++ b/scm-core/src/test/java/sonia/scm/filter/GZipResponseFilterTest.java @@ -1,5 +1,6 @@ package sonia.scm.filter; +import com.google.inject.util.Providers; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -7,6 +8,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import javax.servlet.http.HttpServletRequest; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.ext.WriterInterceptorContext; @@ -20,22 +22,25 @@ import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) class GZipResponseFilterTest { + @Mock + private HttpServletRequest request; + @Mock private WriterInterceptorContext context; @Mock private MultivaluedMap<String,Object> headers; - private final GZipResponseFilter filter = new GZipResponseFilter(); + private GZipResponseFilter filter; @BeforeEach - void setUpContext() { - when(context.getHeaders()).thenReturn(headers); + void setupObjectUnderTest() { + filter = new GZipResponseFilter(Providers.of(request)); } @Test void shouldSkipGZipCompression() throws IOException { - when(headers.getFirst(HttpHeaders.ACCEPT_ENCODING)).thenReturn("deflate, br"); + when(request.getHeader(HttpHeaders.ACCEPT_ENCODING)).thenReturn("deflate, br"); filter.aroundWriteTo(context); @@ -60,7 +65,8 @@ class GZipResponseFilterTest { @BeforeEach void setUpContext() { - when(headers.getFirst(HttpHeaders.ACCEPT_ENCODING)).thenReturn("gzip, deflate, br"); + when(request.getHeader(HttpHeaders.ACCEPT_ENCODING)).thenReturn("gzip, deflate, br"); + when(context.getHeaders()).thenReturn(headers); when(context.getOutputStream()).thenReturn(new ByteArrayOutputStream()); } From 0a9abe629c89ce5e033f79cc7ddd972d782acacd Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra <sebastian.sdorra@cloudogu.com> Date: Tue, 5 Feb 2019 16:47:46 +0100 Subject: [PATCH 086/120] name variable by role and not by type --- scm-core/src/main/java/sonia/scm/util/Comparables.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scm-core/src/main/java/sonia/scm/util/Comparables.java b/scm-core/src/main/java/sonia/scm/util/Comparables.java index b760021c07..1fb0c5e358 100644 --- a/scm-core/src/main/java/sonia/scm/util/Comparables.java +++ b/scm-core/src/main/java/sonia/scm/util/Comparables.java @@ -53,11 +53,11 @@ public final class Comparables { private static PropertyDescriptor findPropertyDescriptor(String sortBy, BeanInfo info) { PropertyDescriptor[] propertyDescriptors = info.getPropertyDescriptors(); - Optional<PropertyDescriptor> optional = Arrays.stream(propertyDescriptors) + Optional<PropertyDescriptor> sortByPropertyDescriptor = Arrays.stream(propertyDescriptors) .filter(p -> p.getName().equals(sortBy)) .findFirst(); - return optional.orElseThrow(() -> new IllegalArgumentException("could not find property " + sortBy)); + return sortByPropertyDescriptor.orElseThrow(() -> new IllegalArgumentException("could not find property " + sortBy)); } private static <T> BeanInfo createBeanInfo(Class<T> type) { From 7fed62ff8ab1f18a9e5bc1e92ed2fcce873b6a3d Mon Sep 17 00:00:00 2001 From: Philipp Czora <philipp.czora@cloudogu.com> Date: Tue, 5 Feb 2019 18:36:26 +0100 Subject: [PATCH 087/120] Added extension point for author metadata --- .../scm/api/v2/resources/HalAppender.java | 4 ++-- .../src/repos/changesets/ChangesetAuthor.js | 18 ++++++++++++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/scm-core/src/main/java/sonia/scm/api/v2/resources/HalAppender.java b/scm-core/src/main/java/sonia/scm/api/v2/resources/HalAppender.java index a7beaf1f6e..6afb542646 100644 --- a/scm-core/src/main/java/sonia/scm/api/v2/resources/HalAppender.java +++ b/scm-core/src/main/java/sonia/scm/api/v2/resources/HalAppender.java @@ -27,7 +27,7 @@ public interface HalAppender { LinkArrayBuilder linkArrayBuilder(String rel); /** - * Appends one embedded to the json response. + * Appends one embedded object to the json response. * * @param rel name of relation * @param embeddedItem embedded object @@ -40,7 +40,7 @@ public interface HalAppender { interface LinkArrayBuilder { /** - * Append an link to the array. + * Append a link to the array. * * @param name name of link * @param href link target diff --git a/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetAuthor.js b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetAuthor.js index 5bb6437575..bba29a1da2 100644 --- a/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetAuthor.js +++ b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetAuthor.js @@ -1,6 +1,7 @@ //@flow import React from "react"; -import type {Changeset} from "@scm-manager/ui-types"; +import type { Changeset } from "@scm-manager/ui-types"; +import { ExtensionPoint } from "@scm-manager/ui-extensions"; type Props = { changeset: Changeset @@ -16,11 +17,24 @@ class ChangesetAuthor extends React.Component<Props> { const { name } = changeset.author; return ( <> - {name} {this.renderMail()} + {name} {this.renderMail()} {this.renderAuthorMetadataExtensionPoint()} </> ); } + renderAuthorMetadataExtensionPoint = () => { + const { changeset } = this.props; + return ( + <ExtensionPoint + name="changesets.changeset.author.metadata" + props={{ changeset }} + renderAll={true} + > + asas + </ExtensionPoint> + ); + }; + renderMail() { const { mail } = this.props.changeset.author; if (mail) { From 32aef466f5380d3e05306e33b616ac1355f091f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= <rene.pfeuffer@cloudogu.com> Date: Tue, 5 Feb 2019 20:47:19 +0000 Subject: [PATCH 088/120] Close branch feature/dependency_updates_and_lifecycle From 1c0f417f9af7b57e5f2d698abbd18c26f476fea6 Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Wed, 6 Feb 2019 09:51:31 +0100 Subject: [PATCH 089/120] added extension point for default branch + renamed bindrepositorysetting --- .../src/main/js/RepositoryConfig.js | 1 + .../scm-git-plugin/src/main/js/index.js | 9 ++---- .../src/config/ConfigurationBinder.js | 2 +- scm-ui/src/repos/containers/EditRepo.js | 28 ++++++++++++++++++- 4 files changed, 31 insertions(+), 9 deletions(-) diff --git a/scm-plugins/scm-git-plugin/src/main/js/RepositoryConfig.js b/scm-plugins/scm-git-plugin/src/main/js/RepositoryConfig.js index e34a0ef96f..80817664d5 100644 --- a/scm-plugins/scm-git-plugin/src/main/js/RepositoryConfig.js +++ b/scm-plugins/scm-git-plugin/src/main/js/RepositoryConfig.js @@ -127,6 +127,7 @@ class RepositoryConfig extends React.Component<Props, State> { disabled={!this.state.selectedBranchName} /> </form> + <hr /> </> ); } else { diff --git a/scm-plugins/scm-git-plugin/src/main/js/index.js b/scm-plugins/scm-git-plugin/src/main/js/index.js index d3fe67476b..43e3950beb 100644 --- a/scm-plugins/scm-git-plugin/src/main/js/index.js +++ b/scm-plugins/scm-git-plugin/src/main/js/index.js @@ -27,14 +27,9 @@ binder.bind( ); binder.bind("repos.repository-avatar", GitAvatar, gitPredicate); -cfgBinder.bindRepositorySub( - "/configuration", - "scm-git-plugin.repo-config.link", - "configuration", - RepositoryConfig -); -// global config +binder.bind("repo-config.route", RepositoryConfig, gitPredicate); +// global config cfgBinder.bindGlobal( "/git", "scm-git-plugin.config.link", diff --git a/scm-ui-components/packages/ui-components/src/config/ConfigurationBinder.js b/scm-ui-components/packages/ui-components/src/config/ConfigurationBinder.js index da1372c316..56964b016b 100644 --- a/scm-ui-components/packages/ui-components/src/config/ConfigurationBinder.js +++ b/scm-ui-components/packages/ui-components/src/config/ConfigurationBinder.js @@ -72,7 +72,7 @@ class ConfigurationBinder { binder.bind("repository.route", RepoRoute, repoPredicate); } - bindRepositorySub(to: string, labelI18nKey: string, linkName: string, RepositoryComponent: any) { + bindRepositorySetting(to: string, labelI18nKey: string, linkName: string, RepositoryComponent: any) { // create predicate based on the link name of the current repository route // if the linkname is not available, the navigation link and the route are not bound to the extension points diff --git a/scm-ui/src/repos/containers/EditRepo.js b/scm-ui/src/repos/containers/EditRepo.js index 072a45b670..a013a226c3 100644 --- a/scm-ui/src/repos/containers/EditRepo.js +++ b/scm-ui/src/repos/containers/EditRepo.js @@ -14,6 +14,7 @@ import { } from "../modules/repos"; import type { History } from "history"; import { ErrorNotification } from "@scm-manager/ui-components"; +import { ExtensionPoint } from "@scm-manager/ui-extensions"; type Props = { loading: boolean, @@ -25,7 +26,8 @@ type Props = { // context props repository: Repository, - history: History + history: History, + match: any }; class EditRepo extends React.Component<Props> { @@ -47,8 +49,27 @@ class EditRepo extends React.Component<Props> { this.props.deleteRepo(repository, this.deleted); }; + stripEndingSlash = (url: string) => { + if (url.endsWith("/")) { + return url.substring(0, url.length - 2); + } + return url; + }; + + matchedUrl = () => { + return this.stripEndingSlash(this.props.match.url); + }; + render() { const { loading, error, repository } = this.props; + + const url = this.matchedUrl(); + + const extensionProps = { + repository, + url + }; + return ( <div> <ErrorNotification error={error} /> @@ -60,6 +81,11 @@ class EditRepo extends React.Component<Props> { }} /> <hr /> + <ExtensionPoint + name="repo-config.route" + props={extensionProps} + renderAll={true} + /> <DeleteRepo repository={repository} delete={this.delete} /> </div> ); From ac6baa55eb18e45ad6fa557ee5fc45350f2f1b4c Mon Sep 17 00:00:00 2001 From: Mohamed Karray <mohamed.karray.sr@gmail.com> Date: Wed, 6 Feb 2019 09:02:03 +0000 Subject: [PATCH 090/120] Close branch feature/enhance_download_button From 0140c15216e096274ea72e9a9e8d3a8c5b7780d5 Mon Sep 17 00:00:00 2001 From: Mohamed Karray <mohamed.karray.sr@gmail.com> Date: Wed, 6 Feb 2019 09:04:04 +0000 Subject: [PATCH 091/120] Close branch bugfix/read_vcs_versions From 6f1615ef0a67db69f58961d8bde24fb44a77f04f Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Wed, 6 Feb 2019 10:42:25 +0100 Subject: [PATCH 092/120] added loading and error state --- scm-ui/src/groups/components/DeleteGroup.js | 26 +++++++++++++++++--- scm-ui/src/groups/containers/EditGroup.js | 12 +++------ scm-ui/src/repos/components/DeleteRepo.js | 27 ++++++++++++++++++--- scm-ui/src/users/components/DeleteUser.js | 26 +++++++++++++++++--- scm-ui/src/users/containers/EditUser.js | 4 +-- 5 files changed, 76 insertions(+), 19 deletions(-) diff --git a/scm-ui/src/groups/components/DeleteGroup.js b/scm-ui/src/groups/components/DeleteGroup.js index c32983ff94..5604685ae5 100644 --- a/scm-ui/src/groups/components/DeleteGroup.js +++ b/scm-ui/src/groups/components/DeleteGroup.js @@ -2,9 +2,18 @@ import React from "react"; import { translate } from "react-i18next"; import type { Group } from "@scm-manager/ui-types"; -import { Subtitle, DeleteButton, confirmAlert } from "@scm-manager/ui-components"; +import { + Subtitle, + DeleteButton, + confirmAlert +} from "@scm-manager/ui-components"; +import { getDeleteGroupFailure, isDeleteGroupPending } from "../modules/groups"; +import { connect } from "react-redux"; +import { ErrorNotification } from "@scm-manager/ui-components"; type Props = { + loading: boolean, + error: Error, group: Group, confirmDialog?: boolean, deleteGroup: (group: Group) => void, @@ -43,7 +52,7 @@ export class DeleteGroup extends React.Component<Props> { }; render() { - const { confirmDialog, t } = this.props; + const { loading, error, confirmDialog, t } = this.props; const action = confirmDialog ? this.confirmDelete : this.deleteGroup; if (!this.isDeletable()) { @@ -53,11 +62,13 @@ export class DeleteGroup extends React.Component<Props> { return ( <> <Subtitle subtitle={t("deleteGroup.subtitle")} /> + <ErrorNotification error={error} /> <div className="columns"> <div className="column"> <DeleteButton label={t("deleteGroup.button")} action={action} + loading={loading} /> </div> </div> @@ -66,4 +77,13 @@ export class DeleteGroup extends React.Component<Props> { } } -export default translate("groups")(DeleteGroup); +const mapStateToProps = (state, ownProps) => { + const loading = isDeleteGroupPending(state, ownProps.group.name); + const error = getDeleteGroupFailure(state, ownProps.group.name); + return { + loading, + error + }; +}; + +export default connect(mapStateToProps)(translate("groups")(DeleteGroup)); diff --git a/scm-ui/src/groups/containers/EditGroup.js b/scm-ui/src/groups/containers/EditGroup.js index 241a61356a..3a83728dde 100644 --- a/scm-ui/src/groups/containers/EditGroup.js +++ b/scm-ui/src/groups/containers/EditGroup.js @@ -7,8 +7,6 @@ import { deleteGroup, getModifyGroupFailure, isModifyGroupPending, - getDeleteGroupFailure, - isDeleteGroupPending, modifyGroupReset } from "../modules/groups"; import type { History } from "history"; @@ -67,7 +65,7 @@ class EditGroup extends React.Component<Props> { }; render() { - const { group, loading, error } = this.props; + const { loading, error, group } = this.props; return ( <div> <ErrorNotification error={error} /> @@ -80,17 +78,15 @@ class EditGroup extends React.Component<Props> { loadUserSuggestions={this.loadUserAutocompletion} /> <hr /> - <DeleteGroup - group={group} - deleteGroup={this.deleteGroup} /> + <DeleteGroup group={group} deleteGroup={this.deleteGroup} /> </div> ); } } const mapStateToProps = (state, ownProps) => { - const loading = isModifyGroupPending(state, ownProps.group.name) || isDeleteGroupPending(state, ownProps.group.name); - const error = getModifyGroupFailure(state, ownProps.group.name) || getDeleteGroupFailure(state, ownProps.group.name); + const loading = isModifyGroupPending(state, ownProps.group.name); + const error = getModifyGroupFailure(state, ownProps.group.name); const autocompleteLink = getUserAutoCompleteLink(state); return { loading, diff --git a/scm-ui/src/repos/components/DeleteRepo.js b/scm-ui/src/repos/components/DeleteRepo.js index 3ba7b08d48..4d1b9f3c7c 100644 --- a/scm-ui/src/repos/components/DeleteRepo.js +++ b/scm-ui/src/repos/components/DeleteRepo.js @@ -2,9 +2,18 @@ import React from "react"; import { translate } from "react-i18next"; import type { Repository } from "@scm-manager/ui-types"; -import { Subtitle, DeleteButton, confirmAlert } from "@scm-manager/ui-components"; +import { + Subtitle, + DeleteButton, + confirmAlert +} from "@scm-manager/ui-components"; +import { getDeleteRepoFailure, isDeleteRepoPending } from "../modules/repos"; +import { connect } from "react-redux"; +import { ErrorNotification } from "@scm-manager/ui-components"; type Props = { + loading: boolean, + error: Error, repository: Repository, confirmDialog?: boolean, @@ -47,7 +56,7 @@ class DeleteRepo extends React.Component<Props> { }; render() { - const { confirmDialog, t } = this.props; + const { loading, error, confirmDialog, t } = this.props; const action = confirmDialog ? this.confirmDelete : this.deleteRepo; if (!this.isDeletable()) { @@ -57,11 +66,13 @@ class DeleteRepo extends React.Component<Props> { return ( <> <Subtitle subtitle={t("deleteRepo.subtitle")} /> + <ErrorNotification error={error} /> <div className="columns"> <div className="column"> <DeleteButton label={t("deleteRepo.button")} action={action} + loading={loading} /> </div> </div> @@ -70,4 +81,14 @@ class DeleteRepo extends React.Component<Props> { } } -export default translate("repos")(DeleteRepo); +const mapStateToProps = (state, ownProps) => { + const { namespace, name } = ownProps.repository; + const loading = isDeleteRepoPending(state, namespace, name); + const error = getDeleteRepoFailure(state, namespace, name); + return { + loading, + error + }; +}; + +export default connect(mapStateToProps)(translate("repos")(DeleteRepo)); diff --git a/scm-ui/src/users/components/DeleteUser.js b/scm-ui/src/users/components/DeleteUser.js index b8523a375e..91868e44fb 100644 --- a/scm-ui/src/users/components/DeleteUser.js +++ b/scm-ui/src/users/components/DeleteUser.js @@ -2,9 +2,18 @@ import React from "react"; import { translate } from "react-i18next"; import type { User } from "@scm-manager/ui-types"; -import { Subtitle, DeleteButton, confirmAlert } from "@scm-manager/ui-components"; +import { + Subtitle, + DeleteButton, + confirmAlert +} from "@scm-manager/ui-components"; +import { getDeleteUserFailure, isDeleteUserPending } from "../modules/users"; +import { connect } from "react-redux"; +import { ErrorNotification } from "@scm-manager/ui-components"; type Props = { + loading: boolean, + error: Error, user: User, confirmDialog?: boolean, @@ -47,7 +56,7 @@ class DeleteUser extends React.Component<Props> { }; render() { - const { confirmDialog, t } = this.props; + const { loading, error, confirmDialog, t } = this.props; const action = confirmDialog ? this.confirmDelete : this.deleteUser; if (!this.isDeletable()) { @@ -57,11 +66,13 @@ class DeleteUser extends React.Component<Props> { return ( <> <Subtitle subtitle={t("deleteUser.subtitle")} /> + <ErrorNotification error={error} /> <div className="columns"> <div className="column"> <DeleteButton label={t("deleteUser.button")} action={action} + loading={loading} /> </div> </div> @@ -70,4 +81,13 @@ class DeleteUser extends React.Component<Props> { } } -export default translate("users")(DeleteUser); +const mapStateToProps = (state, ownProps) => { + const loading = isDeleteUserPending(state, ownProps.user.name); + const error = getDeleteUserFailure(state, ownProps.user.name); + return { + loading, + error + }; +}; + +export default connect(mapStateToProps)(translate("users")(DeleteUser)); diff --git a/scm-ui/src/users/containers/EditUser.js b/scm-ui/src/users/containers/EditUser.js index 6b061c48bf..4198c97447 100644 --- a/scm-ui/src/users/containers/EditUser.js +++ b/scm-ui/src/users/containers/EditUser.js @@ -71,8 +71,8 @@ class EditUser extends React.Component<Props> { } const mapStateToProps = (state, ownProps) => { - const loading = isModifyUserPending(state, ownProps.user.name) || isDeleteUserPending(state, ownProps.user.name); - const error = getModifyUserFailure(state, ownProps.user.name) || getDeleteUserFailure(state, ownProps.user.name); + const loading = isModifyUserPending(state, ownProps.user.name); + const error = getModifyUserFailure(state, ownProps.user.name); return { loading, error From 2a830e4bc4aaaaaa0fc8ec77a79ec4afe44f3fb3 Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Wed, 6 Feb 2019 11:17:06 +0100 Subject: [PATCH 093/120] fixed deleterepo test --- scm-ui/src/repos/components/DeleteRepo.js | 4 +-- .../src/repos/components/DeleteRepo.test.js | 25 +++++++++++++------ 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/scm-ui/src/repos/components/DeleteRepo.js b/scm-ui/src/repos/components/DeleteRepo.js index 4d1b9f3c7c..cca0944f1b 100644 --- a/scm-ui/src/repos/components/DeleteRepo.js +++ b/scm-ui/src/repos/components/DeleteRepo.js @@ -5,11 +5,11 @@ import type { Repository } from "@scm-manager/ui-types"; import { Subtitle, DeleteButton, - confirmAlert + confirmAlert, + ErrorNotification } from "@scm-manager/ui-components"; import { getDeleteRepoFailure, isDeleteRepoPending } from "../modules/repos"; import { connect } from "react-redux"; -import { ErrorNotification } from "@scm-manager/ui-components"; type Props = { loading: boolean, diff --git a/scm-ui/src/repos/components/DeleteRepo.test.js b/scm-ui/src/repos/components/DeleteRepo.test.js index 136cec6c03..3ef7368508 100644 --- a/scm-ui/src/repos/components/DeleteRepo.test.js +++ b/scm-ui/src/repos/components/DeleteRepo.test.js @@ -1,30 +1,40 @@ import React from "react"; -import { mount, shallow } from "enzyme"; +import { mount } from "enzyme"; import ReactRouterEnzymeContext from "react-router-enzyme-context"; +import configureStore from "redux-mock-store"; import "../../tests/enzyme"; import "../../tests/i18n"; import DeleteRepo from "./DeleteRepo"; import { confirmAlert } from "@scm-manager/ui-components"; + jest.mock("@scm-manager/ui-components", () => ({ confirmAlert: jest.fn(), Subtitle: require.requireActual("@scm-manager/ui-components").Subtitle, - DeleteButton: require.requireActual("@scm-manager/ui-components").DeleteButton + DeleteButton: require.requireActual("@scm-manager/ui-components").DeleteButton, + ErrorNotification: ({err}) => err ? err.message : null })); const options = new ReactRouterEnzymeContext(); describe("DeleteRepo", () => { + + let store; + + beforeEach(() => { + store = configureStore()({}); + }); + it("should render nothing, if the delete link is missing", () => { const repository = { _links: {} }; - const navLink = shallow( - <DeleteRepo repository={repository} delete={() => {}} /> + const navLink = mount( + <DeleteRepo repository={repository} delete={() => {}} store={store} /> ); - expect(navLink.text()).toBe(""); + expect(navLink.text()).toBeNull(); }); it("should render the navLink", () => { @@ -37,7 +47,7 @@ describe("DeleteRepo", () => { }; const navLink = mount( - <DeleteRepo repository={repository} delete={() => {}} />, + <DeleteRepo repository={repository} delete={() => {}} store={store} />, options.get() ); expect(navLink.text()).not.toBe(""); @@ -53,7 +63,7 @@ describe("DeleteRepo", () => { }; const navLink = mount( - <DeleteRepo repository={repository} delete={() => {}} />, + <DeleteRepo repository={repository} delete={() => {}} store={store} />, options.get() ); navLink.find("button").simulate("click"); @@ -80,6 +90,7 @@ describe("DeleteRepo", () => { repository={repository} confirmDialog={false} delete={capture} + store={store} />, options.get() ); From 1c03c91e21f06ab656990d188db20d993b7871be Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Wed, 6 Feb 2019 11:44:03 +0100 Subject: [PATCH 094/120] moved edit and delete funcs --- .../{components => containers}/DeleteGroup.js | 33 +++++++++++++++---- .../DeleteGroup.test.js | 0 scm-ui/src/groups/containers/EditGroup.js | 17 ++-------- .../{components => containers}/DeleteRepo.js | 32 ++++++++++++++---- .../DeleteRepo.test.js | 12 ++----- scm-ui/src/repos/containers/EditRepo.js | 17 ++-------- .../{components => containers}/DeleteUser.js | 31 ++++++++++++----- .../DeleteUser.test.js | 0 scm-ui/src/users/containers/EditUser.js | 19 ++--------- 9 files changed, 84 insertions(+), 77 deletions(-) rename scm-ui/src/groups/{components => containers}/DeleteGroup.js (71%) rename scm-ui/src/groups/{components => containers}/DeleteGroup.test.js (100%) rename scm-ui/src/repos/{components => containers}/DeleteRepo.js (74%) rename scm-ui/src/repos/{components => containers}/DeleteRepo.test.js (84%) rename scm-ui/src/users/{components => containers}/DeleteUser.js (72%) rename scm-ui/src/users/{components => containers}/DeleteUser.test.js (100%) diff --git a/scm-ui/src/groups/components/DeleteGroup.js b/scm-ui/src/groups/containers/DeleteGroup.js similarity index 71% rename from scm-ui/src/groups/components/DeleteGroup.js rename to scm-ui/src/groups/containers/DeleteGroup.js index 5604685ae5..b9a453618a 100644 --- a/scm-ui/src/groups/components/DeleteGroup.js +++ b/scm-ui/src/groups/containers/DeleteGroup.js @@ -5,18 +5,27 @@ import type { Group } from "@scm-manager/ui-types"; import { Subtitle, DeleteButton, - confirmAlert + confirmAlert, + ErrorNotification } from "@scm-manager/ui-components"; -import { getDeleteGroupFailure, isDeleteGroupPending } from "../modules/groups"; +import { + deleteGroup, + getDeleteGroupFailure, + isDeleteGroupPending, +} from "../modules/groups"; import { connect } from "react-redux"; -import { ErrorNotification } from "@scm-manager/ui-components"; +import {withRouter} from "react-router-dom"; +import type {History} from "history"; type Props = { loading: boolean, error: Error, group: Group, confirmDialog?: boolean, - deleteGroup: (group: Group) => void, + deleteGroup: (group: Group, callback?: () => void) => void, + + // context props + history: History, t: string => string }; @@ -26,7 +35,11 @@ export class DeleteGroup extends React.Component<Props> { }; deleteGroup = () => { - this.props.deleteGroup(this.props.group); + this.props.deleteGroup(this.props.group, this.groupDeleted); + }; + + groupDeleted = () => { + this.props.history.push("/groups"); }; confirmDelete = () => { @@ -86,4 +99,12 @@ const mapStateToProps = (state, ownProps) => { }; }; -export default connect(mapStateToProps)(translate("groups")(DeleteGroup)); +const mapDispatchToProps = dispatch => { + return { + deleteGroup: (group: Group, callback?: () => void) => { + dispatch(deleteGroup(group, callback)); + } + }; +}; + +export default connect(mapStateToProps, mapDispatchToProps)(withRouter(translate("groups")(DeleteGroup))); diff --git a/scm-ui/src/groups/components/DeleteGroup.test.js b/scm-ui/src/groups/containers/DeleteGroup.test.js similarity index 100% rename from scm-ui/src/groups/components/DeleteGroup.test.js rename to scm-ui/src/groups/containers/DeleteGroup.test.js diff --git a/scm-ui/src/groups/containers/EditGroup.js b/scm-ui/src/groups/containers/EditGroup.js index 3a83728dde..a1f9f31e46 100644 --- a/scm-ui/src/groups/containers/EditGroup.js +++ b/scm-ui/src/groups/containers/EditGroup.js @@ -4,7 +4,6 @@ import { connect } from "react-redux"; import GroupForm from "../components/GroupForm"; import { modifyGroup, - deleteGroup, getModifyGroupFailure, isModifyGroupPending, modifyGroupReset @@ -14,14 +13,13 @@ import { withRouter } from "react-router-dom"; import type { Group } from "@scm-manager/ui-types"; import { ErrorNotification } from "@scm-manager/ui-components"; import { getUserAutoCompleteLink } from "../../modules/indexResource"; -import DeleteGroup from "../components/DeleteGroup"; +import DeleteGroup from "./DeleteGroup"; type Props = { group: Group, fetchGroup: (name: string) => void, modifyGroup: (group: Group, callback?: () => void) => void, modifyGroupReset: Group => void, - deleteGroup: (group: Group, callback?: () => void) => void, autocompleteLink: string, history: History, loading?: boolean, @@ -42,14 +40,6 @@ class EditGroup extends React.Component<Props> { this.props.modifyGroup(group, this.groupModified(group)); }; - deleteGroup = (group: Group) => { - this.props.deleteGroup(group, this.groupDeleted); - }; - - groupDeleted = () => { - this.props.history.push("/groups"); - }; - loadUserAutocompletion = (inputValue: string) => { const url = this.props.autocompleteLink + "?q="; return fetch(url + inputValue) @@ -78,7 +68,7 @@ class EditGroup extends React.Component<Props> { loadUserSuggestions={this.loadUserAutocompletion} /> <hr /> - <DeleteGroup group={group} deleteGroup={this.deleteGroup} /> + <DeleteGroup group={group} /> </div> ); } @@ -102,9 +92,6 @@ const mapDispatchToProps = dispatch => { }, modifyGroupReset: (group: Group) => { dispatch(modifyGroupReset(group)); - }, - deleteGroup: (group: Group, callback?: () => void) => { - dispatch(deleteGroup(group, callback)); } }; }; diff --git a/scm-ui/src/repos/components/DeleteRepo.js b/scm-ui/src/repos/containers/DeleteRepo.js similarity index 74% rename from scm-ui/src/repos/components/DeleteRepo.js rename to scm-ui/src/repos/containers/DeleteRepo.js index cca0944f1b..9875f84aaa 100644 --- a/scm-ui/src/repos/components/DeleteRepo.js +++ b/scm-ui/src/repos/containers/DeleteRepo.js @@ -8,19 +8,24 @@ import { confirmAlert, ErrorNotification } from "@scm-manager/ui-components"; -import { getDeleteRepoFailure, isDeleteRepoPending } from "../modules/repos"; +import { + deleteRepo, + getDeleteRepoFailure, + isDeleteRepoPending, +} from "../modules/repos"; import { connect } from "react-redux"; +import {withRouter} from "react-router-dom"; +import type {History} from "history"; type Props = { loading: boolean, error: Error, repository: Repository, confirmDialog?: boolean, - - // dispatcher functions - delete: Repository => void, + deleteRepo: (Repository, () => void) => void, // context props + history: History, t: string => string }; @@ -29,8 +34,12 @@ class DeleteRepo extends React.Component<Props> { confirmDialog: true }; + deleted = () => { + this.props.history.push("/repos"); + }; + deleteRepo = () => { - this.props.delete(this.props.repository); + this.props.deleteRepo(this.props.repository, this.deleted); }; confirmDelete = () => { @@ -91,4 +100,15 @@ const mapStateToProps = (state, ownProps) => { }; }; -export default connect(mapStateToProps)(translate("repos")(DeleteRepo)); +const mapDispatchToProps = dispatch => { + return { + deleteRepo: (repo: Repository, callback: () => void) => { + dispatch(deleteRepo(repo, callback)); + } + }; +}; + +export default connect( + mapStateToProps, + mapDispatchToProps +)(withRouter(translate("repos")(DeleteRepo))); diff --git a/scm-ui/src/repos/components/DeleteRepo.test.js b/scm-ui/src/repos/containers/DeleteRepo.test.js similarity index 84% rename from scm-ui/src/repos/components/DeleteRepo.test.js rename to scm-ui/src/repos/containers/DeleteRepo.test.js index 3ef7368508..5b37e63763 100644 --- a/scm-ui/src/repos/components/DeleteRepo.test.js +++ b/scm-ui/src/repos/containers/DeleteRepo.test.js @@ -32,7 +32,7 @@ describe("DeleteRepo", () => { }; const navLink = mount( - <DeleteRepo repository={repository} delete={() => {}} store={store} /> + <DeleteRepo repository={repository} store={store} /> ); expect(navLink.text()).toBeNull(); }); @@ -47,7 +47,7 @@ describe("DeleteRepo", () => { }; const navLink = mount( - <DeleteRepo repository={repository} delete={() => {}} store={store} />, + <DeleteRepo repository={repository} store={store} />, options.get() ); expect(navLink.text()).not.toBe(""); @@ -63,7 +63,7 @@ describe("DeleteRepo", () => { }; const navLink = mount( - <DeleteRepo repository={repository} delete={() => {}} store={store} />, + <DeleteRepo repository={repository} store={store} />, options.get() ); navLink.find("button").simulate("click"); @@ -80,16 +80,10 @@ describe("DeleteRepo", () => { } }; - let calledUrl = null; - function capture(repository) { - calledUrl = repository._links.delete.href; - } - const navLink = mount( <DeleteRepo repository={repository} confirmDialog={false} - delete={capture} store={store} />, options.get() diff --git a/scm-ui/src/repos/containers/EditRepo.js b/scm-ui/src/repos/containers/EditRepo.js index a013a226c3..2e5bb69172 100644 --- a/scm-ui/src/repos/containers/EditRepo.js +++ b/scm-ui/src/repos/containers/EditRepo.js @@ -3,11 +3,10 @@ import React from "react"; import { connect } from "react-redux"; import { withRouter } from "react-router-dom"; import RepositoryForm from "../components/form"; -import DeleteRepo from "../components/DeleteRepo"; +import DeleteRepo from "./DeleteRepo"; import type { Repository } from "@scm-manager/ui-types"; import { modifyRepo, - deleteRepo, isModifyRepoPending, getModifyRepoFailure, modifyRepoReset @@ -22,7 +21,6 @@ type Props = { modifyRepo: (Repository, () => void) => void, modifyRepoReset: Repository => void, - deleteRepo: (Repository, () => void) => void, // context props repository: Repository, @@ -41,14 +39,6 @@ class EditRepo extends React.Component<Props> { history.push(`/repo/${repository.namespace}/${repository.name}`); }; - deleted = () => { - this.props.history.push("/repos"); - }; - - delete = (repository: Repository) => { - this.props.deleteRepo(repository, this.deleted); - }; - stripEndingSlash = (url: string) => { if (url.endsWith("/")) { return url.substring(0, url.length - 2); @@ -86,7 +76,7 @@ class EditRepo extends React.Component<Props> { props={extensionProps} renderAll={true} /> - <DeleteRepo repository={repository} delete={this.delete} /> + <DeleteRepo repository={repository} /> </div> ); } @@ -109,9 +99,6 @@ const mapDispatchToProps = dispatch => { }, modifyRepoReset: (repo: Repository) => { dispatch(modifyRepoReset(repo)); - }, - deleteRepo: (repo: Repository, callback: () => void) => { - dispatch(deleteRepo(repo, callback)); } }; }; diff --git a/scm-ui/src/users/components/DeleteUser.js b/scm-ui/src/users/containers/DeleteUser.js similarity index 72% rename from scm-ui/src/users/components/DeleteUser.js rename to scm-ui/src/users/containers/DeleteUser.js index 91868e44fb..44ad087fe8 100644 --- a/scm-ui/src/users/components/DeleteUser.js +++ b/scm-ui/src/users/containers/DeleteUser.js @@ -5,22 +5,23 @@ import type { User } from "@scm-manager/ui-types"; import { Subtitle, DeleteButton, - confirmAlert + confirmAlert, + ErrorNotification } from "@scm-manager/ui-components"; -import { getDeleteUserFailure, isDeleteUserPending } from "../modules/users"; +import {deleteUser, getDeleteUserFailure, isDeleteUserPending} from "../modules/users"; import { connect } from "react-redux"; -import { ErrorNotification } from "@scm-manager/ui-components"; +import {withRouter} from "react-router-dom"; +import type {History} from "history"; type Props = { loading: boolean, error: Error, user: User, confirmDialog?: boolean, + deleteUser: (user: User, callback?: () => void) => void, - // dispatcher functions - deleteUser: (user: User) => void, - - // context objects + // context props + history: History, t: string => string }; @@ -29,8 +30,12 @@ class DeleteUser extends React.Component<Props> { confirmDialog: true }; + userDeleted = () => { + this.props.history.push("/users"); + }; + deleteUser = () => { - this.props.deleteUser(this.props.user); + this.props.deleteUser(this.props.user, this.userDeleted); }; confirmDelete = () => { @@ -90,4 +95,12 @@ const mapStateToProps = (state, ownProps) => { }; }; -export default connect(mapStateToProps)(translate("users")(DeleteUser)); +const mapDispatchToProps = dispatch => { + return { + deleteUser: (user: User, callback?: () => void) => { + dispatch(deleteUser(user, callback)); + } + }; +}; + +export default connect(mapStateToProps, mapDispatchToProps)(withRouter(translate("users")(DeleteUser))); diff --git a/scm-ui/src/users/components/DeleteUser.test.js b/scm-ui/src/users/containers/DeleteUser.test.js similarity index 100% rename from scm-ui/src/users/components/DeleteUser.test.js rename to scm-ui/src/users/containers/DeleteUser.test.js diff --git a/scm-ui/src/users/containers/EditUser.js b/scm-ui/src/users/containers/EditUser.js index 4198c97447..a0263fb9b4 100644 --- a/scm-ui/src/users/containers/EditUser.js +++ b/scm-ui/src/users/containers/EditUser.js @@ -3,16 +3,13 @@ import React from "react"; import { connect } from "react-redux"; import { withRouter } from "react-router-dom"; import UserForm from "../components/UserForm"; -import DeleteUser from "../components/DeleteUser"; +import DeleteUser from "./DeleteUser"; import type { User } from "@scm-manager/ui-types"; import { modifyUser, - deleteUser, isModifyUserPending, getModifyUserFailure, modifyUserReset, - isDeleteUserPending, - getDeleteUserFailure } from "../modules/users"; import type { History } from "history"; import { ErrorNotification } from "@scm-manager/ui-components"; @@ -24,7 +21,6 @@ type Props = { // dispatch functions modifyUser: (user: User, callback?: () => void) => void, modifyUserReset: User => void, - deleteUser: (user: User, callback?: () => void) => void, // context objects user: User, @@ -45,14 +41,6 @@ class EditUser extends React.Component<Props> { this.props.modifyUser(user, this.userModified(user)); }; - userDeleted = () => { - this.props.history.push("/users"); - }; - - deleteUser = (user: User) => { - this.props.deleteUser(user, this.userDeleted); - }; - render() { const { user, loading, error } = this.props; return ( @@ -64,7 +52,7 @@ class EditUser extends React.Component<Props> { loading={loading} /> <hr /> - <DeleteUser user={user} deleteUser={this.deleteUser} /> + <DeleteUser user={user} /> </div> ); } @@ -86,9 +74,6 @@ const mapDispatchToProps = dispatch => { }, modifyUserReset: (user: User) => { dispatch(modifyUserReset(user)); - }, - deleteUser: (user: User, callback?: () => void) => { - dispatch(deleteUser(user, callback)); } }; }; From 6231199862f433bc5d9c88f0701445582cc6d164 Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Wed, 6 Feb 2019 12:10:06 +0100 Subject: [PATCH 095/120] styling --- scm-ui/src/groups/containers/DeleteGroup.js | 11 +++++++---- scm-ui/src/repos/containers/DeleteRepo.js | 6 +++--- scm-ui/src/users/containers/DeleteUser.js | 15 +++++++++++---- scm-ui/src/users/containers/EditUser.js | 2 +- 4 files changed, 22 insertions(+), 12 deletions(-) diff --git a/scm-ui/src/groups/containers/DeleteGroup.js b/scm-ui/src/groups/containers/DeleteGroup.js index b9a453618a..d497e4434d 100644 --- a/scm-ui/src/groups/containers/DeleteGroup.js +++ b/scm-ui/src/groups/containers/DeleteGroup.js @@ -11,11 +11,11 @@ import { import { deleteGroup, getDeleteGroupFailure, - isDeleteGroupPending, + isDeleteGroupPending } from "../modules/groups"; import { connect } from "react-redux"; -import {withRouter} from "react-router-dom"; -import type {History} from "history"; +import { withRouter } from "react-router-dom"; +import type { History } from "history"; type Props = { loading: boolean, @@ -107,4 +107,7 @@ const mapDispatchToProps = dispatch => { }; }; -export default connect(mapStateToProps, mapDispatchToProps)(withRouter(translate("groups")(DeleteGroup))); +export default connect( + mapStateToProps, + mapDispatchToProps +)(withRouter(translate("groups")(DeleteGroup))); diff --git a/scm-ui/src/repos/containers/DeleteRepo.js b/scm-ui/src/repos/containers/DeleteRepo.js index 9875f84aaa..b621a1998b 100644 --- a/scm-ui/src/repos/containers/DeleteRepo.js +++ b/scm-ui/src/repos/containers/DeleteRepo.js @@ -11,11 +11,11 @@ import { import { deleteRepo, getDeleteRepoFailure, - isDeleteRepoPending, + isDeleteRepoPending } from "../modules/repos"; import { connect } from "react-redux"; -import {withRouter} from "react-router-dom"; -import type {History} from "history"; +import { withRouter } from "react-router-dom"; +import type { History } from "history"; type Props = { loading: boolean, diff --git a/scm-ui/src/users/containers/DeleteUser.js b/scm-ui/src/users/containers/DeleteUser.js index 44ad087fe8..b8b42fd9e8 100644 --- a/scm-ui/src/users/containers/DeleteUser.js +++ b/scm-ui/src/users/containers/DeleteUser.js @@ -8,10 +8,14 @@ import { confirmAlert, ErrorNotification } from "@scm-manager/ui-components"; -import {deleteUser, getDeleteUserFailure, isDeleteUserPending} from "../modules/users"; +import { + deleteUser, + getDeleteUserFailure, + isDeleteUserPending +} from "../modules/users"; import { connect } from "react-redux"; -import {withRouter} from "react-router-dom"; -import type {History} from "history"; +import { withRouter } from "react-router-dom"; +import type { History } from "history"; type Props = { loading: boolean, @@ -103,4 +107,7 @@ const mapDispatchToProps = dispatch => { }; }; -export default connect(mapStateToProps, mapDispatchToProps)(withRouter(translate("users")(DeleteUser))); +export default connect( + mapStateToProps, + mapDispatchToProps +)(withRouter(translate("users")(DeleteUser))); diff --git a/scm-ui/src/users/containers/EditUser.js b/scm-ui/src/users/containers/EditUser.js index a0263fb9b4..942d5182e7 100644 --- a/scm-ui/src/users/containers/EditUser.js +++ b/scm-ui/src/users/containers/EditUser.js @@ -9,7 +9,7 @@ import { modifyUser, isModifyUserPending, getModifyUserFailure, - modifyUserReset, + modifyUserReset } from "../modules/users"; import type { History } from "history"; import { ErrorNotification } from "@scm-manager/ui-components"; From 717ddda260ddf12aa696c26ac69991d938091b4f Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Wed, 6 Feb 2019 13:10:30 +0100 Subject: [PATCH 096/120] changed repo overview settings link --- scm-ui/src/repos/components/list/RepositoryEntry.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scm-ui/src/repos/components/list/RepositoryEntry.js b/scm-ui/src/repos/components/list/RepositoryEntry.js index 28a6fe1ad3..b8b03a3523 100644 --- a/scm-ui/src/repos/components/list/RepositoryEntry.js +++ b/scm-ui/src/repos/components/list/RepositoryEntry.js @@ -64,7 +64,7 @@ class RepositoryEntry extends React.Component<Props> { return ( <RepositoryEntryLink iconClass="fa-cog fa-lg" - to={repositoryLink + "/edit"} + to={repositoryLink + "/settings/general"} /> ); } From 8e2a6276366ebbd1047e1f13b778dad291711273 Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Wed, 6 Feb 2019 13:41:48 +0100 Subject: [PATCH 097/120] fixed tests --- .../src/groups/containers/DeleteGroup.test.js | 34 ++++++++++++------- .../src/repos/containers/DeleteRepo.test.js | 7 ++-- .../src/users/containers/DeleteUser.test.js | 34 ++++++++++++------- 3 files changed, 47 insertions(+), 28 deletions(-) diff --git a/scm-ui/src/groups/containers/DeleteGroup.test.js b/scm-ui/src/groups/containers/DeleteGroup.test.js index 043724d5f7..4c38623604 100644 --- a/scm-ui/src/groups/containers/DeleteGroup.test.js +++ b/scm-ui/src/groups/containers/DeleteGroup.test.js @@ -1,30 +1,41 @@ import React from "react"; -import { mount, shallow } from "enzyme"; +import { mount } from "enzyme"; import ReactRouterEnzymeContext from "react-router-enzyme-context"; +import configureStore from "redux-mock-store"; import "../../tests/enzyme"; import "../../tests/i18n"; import DeleteGroup from "./DeleteGroup"; import { confirmAlert } from "@scm-manager/ui-components"; + jest.mock("@scm-manager/ui-components", () => ({ confirmAlert: jest.fn(), Subtitle: require.requireActual("@scm-manager/ui-components").Subtitle, - DeleteButton: require.requireActual("@scm-manager/ui-components").DeleteButton + DeleteButton: require.requireActual("@scm-manager/ui-components").DeleteButton, + ErrorNotification: ({err}) => err ? err.message : null })); const options = new ReactRouterEnzymeContext(); describe("DeleteGroupNavLink", () => { + + let store; + + beforeEach(() => { + store = configureStore()({}); + }); + it("should render nothing, if the delete link is missing", () => { const group = { _links: {} }; - const navLink = shallow( - <DeleteGroup group={group} deleteGroup={() => {}} /> + const navLink = mount( + <DeleteGroup group={group} store={store} />, + options.get() ); - expect(navLink.text()).toBe(""); + expect(navLink.text()).toBeNull(); }); it("should render the navLink", () => { @@ -37,7 +48,7 @@ describe("DeleteGroupNavLink", () => { }; const navLink = mount( - <DeleteGroup group={group} deleteGroup={() => {}} />, + <DeleteGroup group={group} store={store} />, options.get() ); expect(navLink.text()).not.toBe(""); @@ -53,7 +64,7 @@ describe("DeleteGroupNavLink", () => { }; const navLink = mount( - <DeleteGroup group={group} deleteGroup={() => {}} />, + <DeleteGroup group={group} store={store} />, options.get() ); navLink.find("button").simulate("click"); @@ -70,21 +81,18 @@ describe("DeleteGroupNavLink", () => { } }; - let calledUrl = null; - function capture(group) { - calledUrl = group._links.delete.href; - } + store.dispatch = jest.fn(); const navLink = mount( <DeleteGroup group={group} confirmDialog={false} - deleteGroup={capture} + store={store} />, options.get() ); navLink.find("button").simulate("click"); - expect(calledUrl).toBe("/groups"); + expect(store.dispatch.mock.calls.length).toBe(1); }); }); diff --git a/scm-ui/src/repos/containers/DeleteRepo.test.js b/scm-ui/src/repos/containers/DeleteRepo.test.js index 5b37e63763..4f3ebd1049 100644 --- a/scm-ui/src/repos/containers/DeleteRepo.test.js +++ b/scm-ui/src/repos/containers/DeleteRepo.test.js @@ -32,7 +32,8 @@ describe("DeleteRepo", () => { }; const navLink = mount( - <DeleteRepo repository={repository} store={store} /> + <DeleteRepo repository={repository} store={store} />, + options.get() ); expect(navLink.text()).toBeNull(); }); @@ -80,6 +81,8 @@ describe("DeleteRepo", () => { } }; + store.dispatch = jest.fn(); + const navLink = mount( <DeleteRepo repository={repository} @@ -90,6 +93,6 @@ describe("DeleteRepo", () => { ); navLink.find("button").simulate("click"); - expect(calledUrl).toBe("/repos"); + expect(store.dispatch.mock.calls.length).toBe(1); }); }); diff --git a/scm-ui/src/users/containers/DeleteUser.test.js b/scm-ui/src/users/containers/DeleteUser.test.js index 312efe51d9..57995c7394 100644 --- a/scm-ui/src/users/containers/DeleteUser.test.js +++ b/scm-ui/src/users/containers/DeleteUser.test.js @@ -1,30 +1,41 @@ import React from "react"; -import { mount, shallow } from "enzyme"; +import { mount } from "enzyme"; import ReactRouterEnzymeContext from "react-router-enzyme-context"; +import configureStore from "redux-mock-store"; import "../../tests/enzyme"; import "../../tests/i18n"; import DeleteUser from "./DeleteUser"; import { confirmAlert } from "@scm-manager/ui-components"; + jest.mock("@scm-manager/ui-components", () => ({ confirmAlert: jest.fn(), Subtitle: require.requireActual("@scm-manager/ui-components").Subtitle, - DeleteButton: require.requireActual("@scm-manager/ui-components").DeleteButton + DeleteButton: require.requireActual("@scm-manager/ui-components").DeleteButton, + ErrorNotification: ({err}) => err ? err.message : null })); const options = new ReactRouterEnzymeContext(); describe("DeleteUser", () => { + + let store; + + beforeEach(() => { + store = configureStore()({}); + }); + it("should render nothing, if the delete link is missing", () => { const user = { _links: {} }; - const navLink = shallow( - <DeleteUser user={user} deleteUser={() => {}} /> + const navLink = mount( + <DeleteUser user={user} store={store} />, + options.get() ); - expect(navLink.text()).toBe(""); + expect(navLink.text()).toBeNull(); }); it("should render the navLink", () => { @@ -37,7 +48,7 @@ describe("DeleteUser", () => { }; const navLink = mount( - <DeleteUser user={user} deleteUser={() => {}} />, + <DeleteUser user={user} store={store} />, options.get() ); expect(navLink.text()).not.toBe(""); @@ -53,7 +64,7 @@ describe("DeleteUser", () => { }; const navLink = mount( - <DeleteUser user={user} deleteUser={() => {}} />, + <DeleteUser user={user} store={store} />, options.get() ); navLink.find("button").simulate("click"); @@ -70,21 +81,18 @@ describe("DeleteUser", () => { } }; - let calledUrl = null; - function capture(user) { - calledUrl = user._links.delete.href; - } + store.dispatch = jest.fn(); const navLink = mount( <DeleteUser user={user} confirmDialog={false} - deleteUser={capture} + store={store} />, options.get() ); navLink.find("button").simulate("click"); - expect(calledUrl).toBe("/users"); + expect(store.dispatch.mock.calls.length).toBe(1); }); }); From 4e72b3686c704f5c7b4e1c1a9e57860978781390 Mon Sep 17 00:00:00 2001 From: Philipp Czora <philipp.czora@cloudogu.com> Date: Wed, 6 Feb 2019 14:23:25 +0100 Subject: [PATCH 098/120] Added NotAllowedExceptionMapper --- .../scm/api/rest/NotAllowedExceptionMapper.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 scm-webapp/src/main/java/sonia/scm/api/rest/NotAllowedExceptionMapper.java diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/NotAllowedExceptionMapper.java b/scm-webapp/src/main/java/sonia/scm/api/rest/NotAllowedExceptionMapper.java new file mode 100644 index 0000000000..1d268b6855 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/NotAllowedExceptionMapper.java @@ -0,0 +1,12 @@ +package sonia.scm.api.rest; + +import javax.ws.rs.NotAllowedException; +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.Provider; + +@Provider +public class NotAllowedExceptionMapper extends StatusExceptionMapper<NotAllowedException> { + public NotAllowedExceptionMapper() { + super(NotAllowedException.class, Response.Status.METHOD_NOT_ALLOWED); + } +} From 6d7069c914325c2fb64475bef03f5bfc72f60a11 Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Wed, 6 Feb 2019 14:25:03 +0100 Subject: [PATCH 099/120] fixed selector margin --- scm-ui/styles/scm.scss | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scm-ui/styles/scm.scss b/scm-ui/styles/scm.scss index 1feffc401f..83a0e287fc 100644 --- a/scm-ui/styles/scm.scss +++ b/scm-ui/styles/scm.scss @@ -185,8 +185,11 @@ $fa-font-path: "webfonts"; } } -//panels +// panels .panel { + .panel-heading > .field { + margin-bottom: 0; // replace selector margin + } .panel-block { display: block; } From 6b64bc457c10b3613dcf216ed5b9a5ce72fa7336 Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Wed, 6 Feb 2019 14:29:31 +0100 Subject: [PATCH 100/120] deleted tests --- .../src/groups/containers/DeleteGroup.test.js | 98 ------------------- .../src/repos/containers/DeleteRepo.test.js | 98 ------------------- .../src/users/containers/DeleteUser.test.js | 98 ------------------- 3 files changed, 294 deletions(-) delete mode 100644 scm-ui/src/groups/containers/DeleteGroup.test.js delete mode 100644 scm-ui/src/repos/containers/DeleteRepo.test.js delete mode 100644 scm-ui/src/users/containers/DeleteUser.test.js diff --git a/scm-ui/src/groups/containers/DeleteGroup.test.js b/scm-ui/src/groups/containers/DeleteGroup.test.js deleted file mode 100644 index 4c38623604..0000000000 --- a/scm-ui/src/groups/containers/DeleteGroup.test.js +++ /dev/null @@ -1,98 +0,0 @@ -import React from "react"; -import { mount } from "enzyme"; -import ReactRouterEnzymeContext from "react-router-enzyme-context"; -import configureStore from "redux-mock-store"; - -import "../../tests/enzyme"; -import "../../tests/i18n"; -import DeleteGroup from "./DeleteGroup"; - -import { confirmAlert } from "@scm-manager/ui-components"; - -jest.mock("@scm-manager/ui-components", () => ({ - confirmAlert: jest.fn(), - Subtitle: require.requireActual("@scm-manager/ui-components").Subtitle, - DeleteButton: require.requireActual("@scm-manager/ui-components").DeleteButton, - ErrorNotification: ({err}) => err ? err.message : null -})); - -const options = new ReactRouterEnzymeContext(); - -describe("DeleteGroupNavLink", () => { - - let store; - - beforeEach(() => { - store = configureStore()({}); - }); - - it("should render nothing, if the delete link is missing", () => { - const group = { - _links: {} - }; - - const navLink = mount( - <DeleteGroup group={group} store={store} />, - options.get() - ); - expect(navLink.text()).toBeNull(); - }); - - it("should render the navLink", () => { - const group = { - _links: { - delete: { - href: "/groups" - } - } - }; - - const navLink = mount( - <DeleteGroup group={group} store={store} />, - options.get() - ); - expect(navLink.text()).not.toBe(""); - }); - - it("should open the confirm dialog on navLink click", () => { - const group = { - _links: { - delete: { - href: "/groups" - } - } - }; - - const navLink = mount( - <DeleteGroup group={group} store={store} />, - options.get() - ); - navLink.find("button").simulate("click"); - - expect(confirmAlert.mock.calls.length).toBe(1); - }); - - it("should call the delete group function with delete url", () => { - const group = { - _links: { - delete: { - href: "/groups" - } - } - }; - - store.dispatch = jest.fn(); - - const navLink = mount( - <DeleteGroup - group={group} - confirmDialog={false} - store={store} - />, - options.get() - ); - navLink.find("button").simulate("click"); - - expect(store.dispatch.mock.calls.length).toBe(1); - }); -}); diff --git a/scm-ui/src/repos/containers/DeleteRepo.test.js b/scm-ui/src/repos/containers/DeleteRepo.test.js deleted file mode 100644 index 4f3ebd1049..0000000000 --- a/scm-ui/src/repos/containers/DeleteRepo.test.js +++ /dev/null @@ -1,98 +0,0 @@ -import React from "react"; -import { mount } from "enzyme"; -import ReactRouterEnzymeContext from "react-router-enzyme-context"; -import configureStore from "redux-mock-store"; - -import "../../tests/enzyme"; -import "../../tests/i18n"; -import DeleteRepo from "./DeleteRepo"; - -import { confirmAlert } from "@scm-manager/ui-components"; - -jest.mock("@scm-manager/ui-components", () => ({ - confirmAlert: jest.fn(), - Subtitle: require.requireActual("@scm-manager/ui-components").Subtitle, - DeleteButton: require.requireActual("@scm-manager/ui-components").DeleteButton, - ErrorNotification: ({err}) => err ? err.message : null -})); - -const options = new ReactRouterEnzymeContext(); - -describe("DeleteRepo", () => { - - let store; - - beforeEach(() => { - store = configureStore()({}); - }); - - it("should render nothing, if the delete link is missing", () => { - const repository = { - _links: {} - }; - - const navLink = mount( - <DeleteRepo repository={repository} store={store} />, - options.get() - ); - expect(navLink.text()).toBeNull(); - }); - - it("should render the navLink", () => { - const repository = { - _links: { - delete: { - href: "/repositories" - } - } - }; - - const navLink = mount( - <DeleteRepo repository={repository} store={store} />, - options.get() - ); - expect(navLink.text()).not.toBe(""); - }); - - it("should open the confirm dialog on navLink click", () => { - const repository = { - _links: { - delete: { - href: "/repositorys" - } - } - }; - - const navLink = mount( - <DeleteRepo repository={repository} store={store} />, - options.get() - ); - navLink.find("button").simulate("click"); - - expect(confirmAlert.mock.calls.length).toBe(1); - }); - - it("should call the delete repository function with delete url", () => { - const repository = { - _links: { - delete: { - href: "/repos" - } - } - }; - - store.dispatch = jest.fn(); - - const navLink = mount( - <DeleteRepo - repository={repository} - confirmDialog={false} - store={store} - />, - options.get() - ); - navLink.find("button").simulate("click"); - - expect(store.dispatch.mock.calls.length).toBe(1); - }); -}); diff --git a/scm-ui/src/users/containers/DeleteUser.test.js b/scm-ui/src/users/containers/DeleteUser.test.js deleted file mode 100644 index 57995c7394..0000000000 --- a/scm-ui/src/users/containers/DeleteUser.test.js +++ /dev/null @@ -1,98 +0,0 @@ -import React from "react"; -import { mount } from "enzyme"; -import ReactRouterEnzymeContext from "react-router-enzyme-context"; -import configureStore from "redux-mock-store"; - -import "../../tests/enzyme"; -import "../../tests/i18n"; -import DeleteUser from "./DeleteUser"; - -import { confirmAlert } from "@scm-manager/ui-components"; - -jest.mock("@scm-manager/ui-components", () => ({ - confirmAlert: jest.fn(), - Subtitle: require.requireActual("@scm-manager/ui-components").Subtitle, - DeleteButton: require.requireActual("@scm-manager/ui-components").DeleteButton, - ErrorNotification: ({err}) => err ? err.message : null -})); - -const options = new ReactRouterEnzymeContext(); - -describe("DeleteUser", () => { - - let store; - - beforeEach(() => { - store = configureStore()({}); - }); - - it("should render nothing, if the delete link is missing", () => { - const user = { - _links: {} - }; - - const navLink = mount( - <DeleteUser user={user} store={store} />, - options.get() - ); - expect(navLink.text()).toBeNull(); - }); - - it("should render the navLink", () => { - const user = { - _links: { - delete: { - href: "/users" - } - } - }; - - const navLink = mount( - <DeleteUser user={user} store={store} />, - options.get() - ); - expect(navLink.text()).not.toBe(""); - }); - - it("should open the confirm dialog on navLink click", () => { - const user = { - _links: { - delete: { - href: "/users" - } - } - }; - - const navLink = mount( - <DeleteUser user={user} store={store} />, - options.get() - ); - navLink.find("button").simulate("click"); - - expect(confirmAlert.mock.calls.length).toBe(1); - }); - - it("should call the delete user function with delete url", () => { - const user = { - _links: { - delete: { - href: "/users" - } - } - }; - - store.dispatch = jest.fn(); - - const navLink = mount( - <DeleteUser - user={user} - confirmDialog={false} - store={store} - />, - options.get() - ); - navLink.find("button").simulate("click"); - - expect(store.dispatch.mock.calls.length).toBe(1); - }); -}); From eb66b68c2a147579203539ea082cbe7bfd47ca22 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra <sebastian.sdorra@cloudogu.com> Date: Wed, 6 Feb 2019 14:10:37 +0000 Subject: [PATCH 101/120] Close branch feature/improved-navi From 37b6e986bc47cc347e3a706a6e5db26b74b63da3 Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Wed, 6 Feb 2019 14:11:24 +0000 Subject: [PATCH 102/120] Close branch feature/unify_table From 3df6056f00f162e4faa57c3a9d2acef83af7b894 Mon Sep 17 00:00:00 2001 From: Mohamed Karray <mohamed.karray.sr@gmail.com> Date: Thu, 7 Feb 2019 06:01:13 +0100 Subject: [PATCH 103/120] add ui validation --- .../src/forms/AddEntryToTableField.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/scm-ui-components/packages/ui-components/src/forms/AddEntryToTableField.js b/scm-ui-components/packages/ui-components/src/forms/AddEntryToTableField.js index 013014cd98..24b1ced28a 100644 --- a/scm-ui-components/packages/ui-components/src/forms/AddEntryToTableField.js +++ b/scm-ui-components/packages/ui-components/src/forms/AddEntryToTableField.js @@ -10,7 +10,8 @@ type Props = { buttonLabel: string, fieldLabel: string, errorMessage: string, - helpText?: string + helpText?: string, + validateEntry?: string => boolean }; type State = { @@ -25,6 +26,15 @@ class AddEntryToTableField extends React.Component<Props, State> { }; } + isValid = () => { + const {validateEntry} = this.props; + if (!this.state.entryToAdd || this.state.entryToAdd === "" || !validateEntry) { + return true; + } else { + return validateEntry(this.state.entryToAdd); + } + }; + render() { const { disabled, @@ -39,7 +49,7 @@ class AddEntryToTableField extends React.Component<Props, State> { label={fieldLabel} errorMessage={errorMessage} onChange={this.handleAddEntryChange} - validationError={false} + validationError={!this.isValid()} value={this.state.entryToAdd} onReturnPressed={this.appendEntry} disabled={disabled} @@ -48,7 +58,7 @@ class AddEntryToTableField extends React.Component<Props, State> { <AddButton label={buttonLabel} action={this.addButtonClicked} - disabled={disabled || this.state.entryToAdd ===""} + disabled={disabled || this.state.entryToAdd ==="" || !this.isValid()} /> </div> ); From 927ba5df186b9e2ab5a29d545e7a9113399b14be Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra <sebastian.sdorra@cloudogu.com> Date: Thu, 7 Feb 2019 09:28:43 +0100 Subject: [PATCH 104/120] added title with the exact date to DateFromNow --- .../packages/ui-components/src/DateFromNow.js | 35 ++++++++++++------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/scm-ui-components/packages/ui-components/src/DateFromNow.js b/scm-ui-components/packages/ui-components/src/DateFromNow.js index b47de49a3d..be15bcdad2 100644 --- a/scm-ui-components/packages/ui-components/src/DateFromNow.js +++ b/scm-ui-components/packages/ui-components/src/DateFromNow.js @@ -2,31 +2,40 @@ import React from "react"; import moment from "moment"; import { translate } from "react-i18next"; +import injectSheet from "react-jss"; + +const styles = { + date: { + borderBottom: "1px dotted rgba(219, 219, 219)", + cursor: "help" + } +}; type Props = { date?: string, // context props + classes: any, i18n: any }; class DateFromNow extends React.Component<Props> { - static format(locale: string, date?: string) { - let fromNow = ""; - if (date) { - fromNow = moment(date) - .locale(locale) - .fromNow(); - } - return fromNow; - } render() { - const { i18n, date } = this.props; + const { i18n, date, classes } = this.props; - const fromNow = DateFromNow.format(i18n.language, date); - return <span>{fromNow}</span>; + if (date) { + const dateWithLocale = moment(date).locale(i18n.language); + + return ( + <time title={dateWithLocale.format()} className={classes.date}> + {dateWithLocale.fromNow()} + </time> + ); + } + + return null; } } -export default translate()(DateFromNow); +export default injectSheet(styles)(translate()(DateFromNow)); From 9cb1c24567558877c43ff846cf4094c999c11a04 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra <sebastian.sdorra@cloudogu.com> Date: Thu, 7 Feb 2019 09:29:53 +0100 Subject: [PATCH 105/120] use react children for Button and ButtonGroup --- .../ui-components/src/buttons/Button.js | 8 +-- .../ui-components/src/buttons/ButtonGroup.js | 45 ++++++---------- .../components/content/FileButtonGroup.js | 53 ++++++++----------- 3 files changed, 45 insertions(+), 61 deletions(-) diff --git a/scm-ui-components/packages/ui-components/src/buttons/Button.js b/scm-ui-components/packages/ui-components/src/buttons/Button.js index 4ad88f4eac..2102bb540a 100644 --- a/scm-ui-components/packages/ui-components/src/buttons/Button.js +++ b/scm-ui-components/packages/ui-components/src/buttons/Button.js @@ -1,16 +1,17 @@ //@flow -import React from "react"; +import * as React from "react"; import classNames from "classnames"; import { withRouter } from "react-router-dom"; export type ButtonProps = { - label: string, + label?: string, loading?: boolean, disabled?: boolean, action?: (event: Event) => void, link?: string, fullWidth?: boolean, className?: string, + children?: React.Node, classes: any }; @@ -45,6 +46,7 @@ class Button extends React.Component<Props> { type, color, fullWidth, + children, className } = this.props; const loadingClass = loading ? "is-loading" : ""; @@ -62,7 +64,7 @@ class Button extends React.Component<Props> { className )} > - {label} + {label} {children} </button> ); }; diff --git a/scm-ui-components/packages/ui-components/src/buttons/ButtonGroup.js b/scm-ui-components/packages/ui-components/src/buttons/ButtonGroup.js index 56ef66522a..fb48a3ba4f 100644 --- a/scm-ui-components/packages/ui-components/src/buttons/ButtonGroup.js +++ b/scm-ui-components/packages/ui-components/src/buttons/ButtonGroup.js @@ -1,41 +1,30 @@ // @flow -import React from "react"; -import Button from "./Button"; +import * as React from "react"; type Props = { - firstlabel: string, - secondlabel: string, - firstAction?: (event: Event) => void, - secondAction?: (event: Event) => void, - firstIsSelected: boolean + addons?: boolean, + className?: string, + children: React.Node }; class ButtonGroup extends React.Component<Props> { + static defaultProps = { + addons: true + }; + render() { - const { firstlabel, secondlabel, firstAction, secondAction, firstIsSelected } = this.props; - - let showFirstColor = ""; - let showSecondColor = ""; - - if (firstIsSelected) { - showFirstColor += "link is-selected"; - } else { - showSecondColor += "link is-selected"; + const { addons, className, children } = this.props; + let styleClasses = "buttons"; + if (addons) { + styleClasses += " has-addons"; + } + if (className) { + styleClasses += " " + className; } - return ( - <div className="buttons has-addons"> - <Button - label={firstlabel} - color={showFirstColor} - action={firstAction} - /> - <Button - label={secondlabel} - color={showSecondColor} - action={secondAction} - /> + <div className={styleClasses}> + { children } </div> ); } diff --git a/scm-ui/src/repos/sources/components/content/FileButtonGroup.js b/scm-ui/src/repos/sources/components/content/FileButtonGroup.js index c56df2e5a1..6318425711 100644 --- a/scm-ui/src/repos/sources/components/content/FileButtonGroup.js +++ b/scm-ui/src/repos/sources/components/content/FileButtonGroup.js @@ -1,7 +1,7 @@ // @flow import React from "react"; import { translate } from "react-i18next"; -import { ButtonGroup } from "@scm-manager/ui-components"; +import { ButtonGroup, Button } from "@scm-manager/ui-components"; type Props = { t: string => string, @@ -18,39 +18,32 @@ class FileButtonGroup extends React.Component<Props> { this.props.showHistory(false); }; + color = (selected: boolean) => { + return selected ? "link is-selected" : null; + }; + render() { const { t, historyIsSelected } = this.props; - const sourcesLabel = ( - <> - <span className="icon"> - <i className="fas fa-code" /> - </span> - <span className="is-hidden-mobile"> - {t("sources.content.sourcesButton")} - </span> - </> - ); - - const historyLabel = ( - <> - <span className="icon"> - <i className="fas fa-history" /> - </span> - <span className="is-hidden-mobile"> - {t("sources.content.historyButton")} - </span> - </> - ); - return ( - <ButtonGroup - firstlabel={sourcesLabel} - secondlabel={historyLabel} - firstAction={this.showSources} - secondAction={this.showHistory} - firstIsSelected={!historyIsSelected} - /> + <ButtonGroup> + <Button action={this.showSources} color={ this.color(!historyIsSelected) }> + <span className="icon"> + <i className="fas fa-code"/> + </span> + <span className="is-hidden-mobile"> + {t("sources.content.sourcesButton")} + </span> + </Button> + <Button action={this.showHistory} color={ this.color(historyIsSelected) }> + <span className="icon"> + <i className="fas fa-history"/> + </span> + <span className="is-hidden-mobile"> + {t("sources.content.historyButton")} + </span> + </Button> + </ButtonGroup> ); } } From c5511af4480914aafdbea6bcb7b48f529c1a8295 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra <sebastian.sdorra@cloudogu.com> Date: Thu, 7 Feb 2019 09:30:30 +0100 Subject: [PATCH 106/120] use span for tooltip and added an option to change tooltip location --- .../packages/ui-components/src/Tooltip.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/scm-ui-components/packages/ui-components/src/Tooltip.js b/scm-ui-components/packages/ui-components/src/Tooltip.js index d935b323c7..50aecfc80c 100644 --- a/scm-ui-components/packages/ui-components/src/Tooltip.js +++ b/scm-ui-components/packages/ui-components/src/Tooltip.js @@ -5,20 +5,26 @@ import classNames from "classnames"; type Props = { message: string, className: string, + location?: string, children: React.Node }; class Tooltip extends React.Component<Props> { + + static defaultProps = { + location: "right" + }; + render() { - const { className, message, children } = this.props; + const { className, message, location, children } = this.props; const multiline = message.length > 60 ? "is-tooltip-multiline" : ""; return ( - <div - className={classNames("tooltip", "is-tooltip-right", multiline, className)} + <span + className={classNames("tooltip", "is-tooltip-" + location, multiline, className)} data-tooltip={message} > {children} - </div> + </span> ); } } From d14837b03e6c821c25baf872e8ebd12e843b7dfd Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra <sebastian.sdorra@cloudogu.com> Date: Thu, 7 Feb 2019 09:32:24 +0100 Subject: [PATCH 107/120] fix small flow issues --- scm-ui-components/packages/ui-components/src/Tooltip.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scm-ui-components/packages/ui-components/src/Tooltip.js b/scm-ui-components/packages/ui-components/src/Tooltip.js index 50aecfc80c..cecb8d349b 100644 --- a/scm-ui-components/packages/ui-components/src/Tooltip.js +++ b/scm-ui-components/packages/ui-components/src/Tooltip.js @@ -4,8 +4,8 @@ import classNames from "classnames"; type Props = { message: string, - className: string, - location?: string, + className?: string, + location: string, children: React.Node }; From 0c8ed130b9d84cf118833079d531b8d0a337e0c3 Mon Sep 17 00:00:00 2001 From: Mohamed Karray <mohamed.karray.sr@gmail.com> Date: Thu, 7 Feb 2019 08:35:10 +0000 Subject: [PATCH 108/120] Close branch bugfix/method_not_allowed_exception_mapper From a5d9a13bc0d96059288173a518825dda3bab04c2 Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Thu, 7 Feb 2019 09:54:53 +0100 Subject: [PATCH 109/120] added subtitle for git config --- scm-plugins/scm-git-plugin/src/main/js/RepositoryConfig.js | 3 ++- .../scm-git-plugin/src/main/resources/locales/de/plugins.json | 1 + .../scm-git-plugin/src/main/resources/locales/en/plugins.json | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/scm-plugins/scm-git-plugin/src/main/js/RepositoryConfig.js b/scm-plugins/scm-git-plugin/src/main/js/RepositoryConfig.js index 80817664d5..aadb58eed6 100644 --- a/scm-plugins/scm-git-plugin/src/main/js/RepositoryConfig.js +++ b/scm-plugins/scm-git-plugin/src/main/js/RepositoryConfig.js @@ -2,7 +2,7 @@ import React from "react"; -import {apiClient, BranchSelector, ErrorPage, Loading, SubmitButton} from "@scm-manager/ui-components"; +import {apiClient, BranchSelector, ErrorPage, Loading, Subtitle, SubmitButton} from "@scm-manager/ui-components"; import type {Branch, Repository} from "@scm-manager/ui-types"; import {translate} from "react-i18next"; @@ -113,6 +113,7 @@ class RepositoryConfig extends React.Component<Props, State> { if (!(loadingBranches || loadingDefaultBranch)) { return ( <> + <Subtitle subtitle={t("scm-git-plugin.repo-config.title")}/> {this.renderBranchChangedNotification()} <form onSubmit={this.submit}> <BranchSelector diff --git a/scm-plugins/scm-git-plugin/src/main/resources/locales/de/plugins.json b/scm-plugins/scm-git-plugin/src/main/resources/locales/de/plugins.json index 8902a57f32..cd88897e74 100644 --- a/scm-plugins/scm-git-plugin/src/main/resources/locales/de/plugins.json +++ b/scm-plugins/scm-git-plugin/src/main/resources/locales/de/plugins.json @@ -27,6 +27,7 @@ }, "repo-config": { "link": "Konfiguration", + "title": "Git Einstellungen", "default-branch": "Standard Branch", "submit": "Speichern", "error": { diff --git a/scm-plugins/scm-git-plugin/src/main/resources/locales/en/plugins.json b/scm-plugins/scm-git-plugin/src/main/resources/locales/en/plugins.json index 2b579801dd..551573fb72 100644 --- a/scm-plugins/scm-git-plugin/src/main/resources/locales/en/plugins.json +++ b/scm-plugins/scm-git-plugin/src/main/resources/locales/en/plugins.json @@ -27,6 +27,7 @@ }, "repo-config": { "link": "Configuration", + "title": "Git Settings", "default-branch": "Default branch", "submit": "Submit", "error": { From 6ba90fb8dab287fc0cc9b41e99d2e1c388252b67 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra <sebastian.sdorra@cloudogu.com> Date: Thu, 7 Feb 2019 10:23:52 +0100 Subject: [PATCH 110/120] enabled nested rules for jss --- scm-ui/package.json | 1 + scm-ui/src/index.js | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/scm-ui/package.json b/scm-ui/package.json index bf4e272fb0..2144e50e6c 100644 --- a/scm-ui/package.json +++ b/scm-ui/package.json @@ -17,6 +17,7 @@ "i18next": "^11.4.0", "i18next-browser-languagedetector": "^2.2.2", "i18next-fetch-backend": "^0.1.0", + "jss-nested": "^6.0.1", "moment": "^2.22.2", "node-sass": "^4.9.3", "postcss-easy-import": "^3.0.0", diff --git a/scm-ui/src/index.js b/scm-ui/src/index.js index 08e3e8a58c..fd09a0b75f 100644 --- a/scm-ui/src/index.js +++ b/scm-ui/src/index.js @@ -17,6 +17,12 @@ import { ConnectedRouter } from "react-router-redux"; import { urls } from "@scm-manager/ui-components"; +import jss from "jss"; +import jssNested from "jss-nested"; + +// setup jss and install required plugins +jss.setup(jssNested()); + // Create a history of your choosing (we're using a browser history in this case) const history: BrowserHistory = createHistory({ basename: urls.contextPath From 0bf7a6f168a1395bd38db74080b068ad1fb99499 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra <sebastian.sdorra@cloudogu.com> Date: Thu, 7 Feb 2019 10:27:11 +0100 Subject: [PATCH 111/120] redesign changeset list --- .../src/repos/changesets/ChangesetAuthor.js | 56 +++---- .../repos/changesets/ChangesetButtonGroup.js | 45 ++++++ .../src/repos/changesets/ChangesetId.js | 11 +- .../src/repos/changesets/ChangesetRow.js | 140 ++++++++++-------- .../src/repos/changesets/ChangesetTag.js | 25 +--- .../src/repos/changesets/ChangesetTagBase.js | 34 +++++ .../src/repos/changesets/ChangesetTags.js | 31 ++++ .../changesets/ChangesetTagsCollapsed.js | 27 ++++ .../src/repos/changesets/changesets.js | 10 ++ .../src/repos/changesets/index.js | 4 + scm-ui/public/locales/de/repos.json | 8 +- scm-ui/public/locales/en/repos.json | 8 +- 12 files changed, 278 insertions(+), 121 deletions(-) create mode 100644 scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetButtonGroup.js create mode 100644 scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetTagBase.js create mode 100644 scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetTags.js create mode 100644 scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetTagsCollapsed.js diff --git a/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetAuthor.js b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetAuthor.js index bba29a1da2..6ee76e3927 100644 --- a/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetAuthor.js +++ b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetAuthor.js @@ -2,9 +2,13 @@ import React from "react"; import type { Changeset } from "@scm-manager/ui-types"; import { ExtensionPoint } from "@scm-manager/ui-extensions"; +import {translate} from "react-i18next"; type Props = { - changeset: Changeset + changeset: Changeset, + + // context props + t: (string) => string }; class ChangesetAuthor extends React.Component<Props> { @@ -14,39 +18,35 @@ class ChangesetAuthor extends React.Component<Props> { return null; } - const { name } = changeset.author; + const { name, mail } = changeset.author; + if (mail) { + return this.withExtensionPoint(this.renderWithMail(name, mail)); + } + return this.withExtensionPoint(<>{name}</>); + } + + renderWithMail(name: string, mail: string) { + const { t } = this.props; return ( - <> - {name} {this.renderMail()} {this.renderAuthorMetadataExtensionPoint()} - </> + <a href={"mailto: " + mail} title={t("changesets.author.mailto") + " " + mail}> + {name} + </a> ); } - renderAuthorMetadataExtensionPoint = () => { - const { changeset } = this.props; + withExtensionPoint(child: any) { + const { t } = this.props; return ( - <ExtensionPoint - name="changesets.changeset.author.metadata" - props={{ changeset }} - renderAll={true} - > - asas - </ExtensionPoint> + <> + {t("changesets.author.prefix")} {child} + <ExtensionPoint + name="changesets.author.suffix" + props={{ changeset: this.props.changeset }} + renderAll={true} + /> + </> ); - }; - - renderMail() { - const { mail } = this.props.changeset.author; - if (mail) { - return ( - <a className="is-hidden-mobile" href={"mailto:" + mail}> - < - {mail} - > - </a> - ); - } } } -export default ChangesetAuthor; +export default translate("repos")(ChangesetAuthor); diff --git a/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetButtonGroup.js b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetButtonGroup.js new file mode 100644 index 0000000000..292d61b6cc --- /dev/null +++ b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetButtonGroup.js @@ -0,0 +1,45 @@ +//@flow +import React from "react"; +import type { Changeset, Repository } from "@scm-manager/ui-types"; +import ButtonGroup from "../../buttons/ButtonGroup"; +import Button from "../../buttons/Button"; +import { createChangesetLink, createSourcesLink } from "./changesets"; + +type Props = { + repository: Repository, + changeset: Changeset +} + +class ChangesetButtonGroup extends React.Component<Props> { + + render() { + const { repository, changeset } = this.props; + + const changesetLink = createChangesetLink(repository, changeset); + const sourcesLink = createSourcesLink(repository, changeset); + + return ( + <ButtonGroup className="is-pulled-right"> + <Button link={changesetLink}> + <span className="icon"> + <i className="fas fa-code"></i> + </span> + <span className="is-hidden-mobile is-hidden-tablet-only"> + Details + </span> + </Button> + <Button link={sourcesLink}> + <span className="icon"> + <i className="fas fa-history"></i> + </span> + <span className="is-hidden-mobile is-hidden-tablet-only"> + Sources + </span> + </Button> + </ButtonGroup> + ); + } + +} + +export default ChangesetButtonGroup; diff --git a/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetId.js b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetId.js index aec1029427..a3cc3c73b7 100644 --- a/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetId.js +++ b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetId.js @@ -3,6 +3,7 @@ import {Link} from "react-router-dom"; import React from "react"; import type {Changeset, Repository} from "@scm-manager/ui-types"; +import { createChangesetLink } from "./changesets"; type Props = { repository: Repository, @@ -20,13 +21,11 @@ export default class ChangesetId extends React.Component<Props> { }; renderLink = () => { - const { changeset, repository } = this.props; + const { repository, changeset } = this.props; + const link = createChangesetLink(repository, changeset); + return ( - <Link - to={`/repo/${repository.namespace}/${repository.name}/changeset/${ - changeset.id - }`} - > + <Link to={link}> {this.shortId(changeset)} </Link> ); diff --git a/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetRow.js b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetRow.js index 7609bc2171..10287ec71f 100644 --- a/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetRow.js +++ b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetRow.js @@ -8,21 +8,39 @@ import ChangesetId from "./ChangesetId"; import injectSheet from "react-jss"; import { DateFromNow } from "../.."; import ChangesetAuthor from "./ChangesetAuthor"; -import ChangesetTag from "./ChangesetTag"; - import { parseDescription } from "./changesets"; import { AvatarWrapper, AvatarImage } from "../../avatar"; import { ExtensionPoint } from "@scm-manager/ui-extensions"; +import ChangesetTags from "./ChangesetTags"; +import ChangesetButtonGroup from "./ChangesetButtonGroup"; const styles = { - pointer: { - cursor: "pointer" + changeset: { + // & references parent rule + // have a look at https://cssinjs.org/jss-plugin-nested?v=v10.0.0-alpha.9 + "& + &": { + borderTop: "1px solid rgba(219, 219, 219, 0.5)", + marginTop: "1rem", + paddingTop: "1rem" + } }, - changesetGroup: { - marginBottom: "1em" + avatarFigure: { + marginTop: ".25rem", + marginRight: ".5rem", }, - withOverflow: { - overflow: "auto" + avatarImage: { + height: "35px", + width: "35px" + }, + isVcentered: { + marginTop: "auto", + marginBottom: "auto" + }, + metadata: { + marginLeft: 0 + }, + tag: { + marginTop: ".5rem" } }; @@ -34,74 +52,70 @@ type Props = { }; class ChangesetRow extends React.Component<Props> { - createLink = (changeset: Changeset) => { + createChangesetId = (changeset: Changeset) => { const { repository } = this.props; return <ChangesetId changeset={changeset} repository={repository} />; }; - getTags = () => { - const { changeset } = this.props; - return changeset._embedded.tags || []; - }; - render() { - const { changeset, classes } = this.props; - const changesetLink = this.createLink(changeset); - const dateFromNow = <DateFromNow date={changeset.date} />; - const authorLine = <ChangesetAuthor changeset={changeset} />; + const { repository, changeset, classes } = this.props; const description = parseDescription(changeset.description); + const changesetId = this.createChangesetId(changeset); + const dateFromNow = <DateFromNow date={changeset.date} />; return ( - <article className={classNames("media", classes.inner)}> - <AvatarWrapper> - <div> - <figure className="media-left"> - <p className="image is-64x64"> - <AvatarImage person={changeset.author} /> - </p> - </figure> + <div className={classes.changeset}> + <div className="columns"> + <div className="column is-three-fifths"> + + <h4 className="has-text-weight-bold is-ellipsis-overflow"> + <ExtensionPoint + name="changesets.changeset.description" + props={{ changeset, value: description.title }} + renderAll={false} + > + {description.title} + </ExtensionPoint> + </h4> + + <div className="media"> + <AvatarWrapper> + <figure className={classNames(classes.avatarFigure, "media-left")}> + <div className={classNames("image", classes.avatarImage)}> + <AvatarImage person={changeset.author} /> + </div> + </figure> + </AvatarWrapper> + <div className={classNames(classes.metadata, "media-right")}> + <p className="is-hidden-mobile is-hidden-tablet-only"> + <Interpolate + i18nKey="changesets.changeset.summary" + id={changesetId} + time={dateFromNow} + /> + </p> + <p className="is-hidden-desktop"> + <Interpolate + i18nKey="changesets.changeset.short-summary" + id={changesetId} + time={dateFromNow} + /> + </p> + <p className="is-size-7"> + <ChangesetAuthor changeset={changeset} /> + </p> + </div> + </div> + </div> - </AvatarWrapper> - <div className={classNames("media-content", classes.withOverflow)}> - <div className="content"> - <p className="is-ellipsis-overflow"> - <strong> - <ExtensionPoint - name="changesets.changeset.description" - props={{ changeset, value: description.title }} - renderAll={false} - > - {description.title} - </ExtensionPoint> - </strong> - <br /> - <Interpolate - i18nKey="changesets.changeset.summary" - id={changesetLink} - time={dateFromNow} - /> - </p>{" "} - <div className="is-size-7">{authorLine}</div> + <div className={classNames("column", classes.isVcentered)}> + <ChangesetTags changeset={changeset} /> + <ChangesetButtonGroup repository={repository} changeset={changeset} /> </div> </div> - {this.renderTags()} - </article> + </div> ); } - - renderTags = () => { - const tags = this.getTags(); - if (tags.length > 0) { - return ( - <div className="media-right"> - {tags.map((tag: Tag) => { - return <ChangesetTag key={tag.name} tag={tag} />; - })} - </div> - ); - } - return null; - }; } export default injectSheet(styles)(translate("repos")(ChangesetRow)); diff --git a/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetTag.js b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetTag.js index 6a87400d2e..03c73a8e9f 100644 --- a/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetTag.js +++ b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetTag.js @@ -1,32 +1,17 @@ //@flow import React from "react"; import type { Tag } from "@scm-manager/ui-types"; -import injectSheet from "react-jss"; -import classNames from "classnames"; - -const styles = { - spacing: { - marginRight: "4px" - } -}; +import ChangesetTagBase from "./ChangesetTagBase"; type Props = { - tag: Tag, - - // context props - classes: Object + tag: Tag }; class ChangesetTag extends React.Component<Props> { render() { - const { tag, classes } = this.props; - return ( - <span className="tag is-info"> - <span className={classNames("fa", "fa-tag", classes.spacing)} />{" "} - {tag.name} - </span> - ); + const { tag } = this.props; + return <ChangesetTagBase icon={"fa-tag"} label={tag.name} />; } } -export default injectSheet(styles)(ChangesetTag); +export default ChangesetTag; diff --git a/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetTagBase.js b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetTagBase.js new file mode 100644 index 0000000000..7fc8dabcd2 --- /dev/null +++ b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetTagBase.js @@ -0,0 +1,34 @@ +//@flow +import React from "react"; +import injectSheet from "react-jss"; +import classNames from "classnames"; + +const styles = { + tag: { + marginTop: ".5rem" + }, + spacing: { + marginRight: ".25rem" + } +}; + +type Props = { + icon: string, + label: string, + + // context props + classes: Object +}; + +class ChangesetTagBase extends React.Component<Props> { + render() { + const { icon, label, classes } = this.props; + return ( + <span className={classNames(classes.tag, "tag", "is-info")}> + <span className={classNames("fa", icon, classes.spacing)} /> {label} + </span> + ); + } +} + +export default injectSheet(styles)(ChangesetTagBase); diff --git a/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetTags.js b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetTags.js new file mode 100644 index 0000000000..b8bff8ddd2 --- /dev/null +++ b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetTags.js @@ -0,0 +1,31 @@ +//@flow +import React from "react"; +import type { Changeset} from "@scm-manager/ui-types"; +import ChangesetTag from "./ChangesetTag"; +import ChangesetTagsCollapsed from "./ChangesetTagsCollapsed"; + +type Props = { + changeset: Changeset +}; + +class ChangesetTags extends React.Component<Props> { + + getTags = () => { + const { changeset } = this.props; + return changeset._embedded.tags || []; + }; + + render() { + const tags = this.getTags(); + + if (tags.length === 1) { + return <ChangesetTag tag={tags[0]} />; + } else if (tags.length > 1) { + return <ChangesetTagsCollapsed tags={tags} />; + } else { + return null; + } + } +} + +export default ChangesetTags; diff --git a/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetTagsCollapsed.js b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetTagsCollapsed.js new file mode 100644 index 0000000000..50fb36644d --- /dev/null +++ b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetTagsCollapsed.js @@ -0,0 +1,27 @@ +//@flow +import React from "react"; +import type { Tag } from "@scm-manager/ui-types"; +import ChangesetTagBase from "./ChangesetTagBase"; +import { translate } from "react-i18next"; +import Tooltip from "../../Tooltip"; + +type Props = { + tags: Tag[], + + // context props + t: (string) => string +}; + +class ChangesetTagsCollapsed extends React.Component<Props> { + render() { + const { tags, t } = this.props; + const message = tags.map((tag) => tag.name).join(", "); + return ( + <Tooltip location="top" message={message}> + <ChangesetTagBase icon={"fa-tags"} label={ tags.length + " " + t("changesets.tags") } /> + </Tooltip> + ); + } +} + +export default translate("repos")(ChangesetTagsCollapsed); diff --git a/scm-ui-components/packages/ui-components/src/repos/changesets/changesets.js b/scm-ui-components/packages/ui-components/src/repos/changesets/changesets.js index f61a89c74b..69227ad75d 100644 --- a/scm-ui-components/packages/ui-components/src/repos/changesets/changesets.js +++ b/scm-ui-components/packages/ui-components/src/repos/changesets/changesets.js @@ -1,9 +1,19 @@ // @flow +import type { Changeset, Repository } from "@scm-manager/ui-types"; + export type Description = { title: string, message: string }; +export function createChangesetLink(repository: Repository, changeset: Changeset) { + return `/repo/${repository.namespace}/${repository.name}/changeset/${changeset.id}`; +} + +export function createSourcesLink(repository: Repository, changeset: Changeset) { + return `/repo/${repository.namespace}/${repository.name}/sources/${changeset.id}`; +} + export function parseDescription(description?: string): Description { const desc = description ? description : ""; const lineBreak = desc.indexOf("\n"); diff --git a/scm-ui-components/packages/ui-components/src/repos/changesets/index.js b/scm-ui-components/packages/ui-components/src/repos/changesets/index.js index 0e7a5e533d..cede55ceca 100644 --- a/scm-ui-components/packages/ui-components/src/repos/changesets/index.js +++ b/scm-ui-components/packages/ui-components/src/repos/changesets/index.js @@ -3,8 +3,12 @@ import * as changesets from "./changesets"; export { changesets }; export { default as ChangesetAuthor } from "./ChangesetAuthor"; +export { default as ChangesetButtonGroup } from "./ChangesetButtonGroup"; +export { default as ChangesetDiff } from "./ChangesetDiff"; export { default as ChangesetId } from "./ChangesetId"; export { default as ChangesetList } from "./ChangesetList"; export { default as ChangesetRow } from "./ChangesetRow"; export { default as ChangesetTag } from "./ChangesetTag"; +export { default as ChangesetTags } from "./ChangesetTags"; +export { default as ChangesetTagsCollapsed } from "./ChangesetTagsCollapsed"; export { default as ChangesetDiff } from "./ChangesetDiff"; diff --git a/scm-ui/public/locales/de/repos.json b/scm-ui/public/locales/de/repos.json index e82edf7512..2b6fe66bb5 100644 --- a/scm-ui/public/locales/de/repos.json +++ b/scm-ui/public/locales/de/repos.json @@ -76,11 +76,15 @@ "description": "Beschreibung", "contact": "Kontakt", "date": "Datum", - "summary": "Changeset {{id}} wurde committet {{time}}" + "summary": "Changeset {{id}} wurde committet {{time}}", + "short-summary": "Committet {{id}} {{time}}" }, + "tags": "Tags", "author": { "name": "Autor", - "mail": "Mail" + "mail": "Mail", + "prefix": "Verfasst von", + "mailto": "E-Mail senden an" } }, "branch-selector": { diff --git a/scm-ui/public/locales/en/repos.json b/scm-ui/public/locales/en/repos.json index 727fe4f664..c829db54ad 100644 --- a/scm-ui/public/locales/en/repos.json +++ b/scm-ui/public/locales/en/repos.json @@ -76,11 +76,15 @@ "description": "Description", "contact": "Contact", "date": "Date", - "summary": "Changeset {{id}} was committed {{time}}" + "summary": "Changeset {{id}} was committed {{time}}", + "short-summary": "Committed {{id}} {{time}}" }, + "tags": "Tags", "author": { "name": "Author", - "mail": "Mail" + "mail": "Mail", + "prefix": "Authored by", + "mailto": "Send mail to" } }, "branch-selector": { From cbfa751a05e4b5e369b9f20e292953b04c6d94e4 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra <sebastian.sdorra@cloudogu.com> Date: Thu, 7 Feb 2019 10:33:58 +0100 Subject: [PATCH 112/120] fixed wrong icons and added missing translation --- .../repos/changesets/ChangesetButtonGroup.js | 18 +++++++++++------- .../src/repos/changesets/index.js | 1 - scm-ui/public/locales/de/repos.json | 6 +++++- scm-ui/public/locales/en/repos.json | 4 ++++ 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetButtonGroup.js b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetButtonGroup.js index 292d61b6cc..7958dfe804 100644 --- a/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetButtonGroup.js +++ b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetButtonGroup.js @@ -4,16 +4,20 @@ import type { Changeset, Repository } from "@scm-manager/ui-types"; import ButtonGroup from "../../buttons/ButtonGroup"; import Button from "../../buttons/Button"; import { createChangesetLink, createSourcesLink } from "./changesets"; +import { translate } from "react-i18next"; type Props = { repository: Repository, - changeset: Changeset + changeset: Changeset, + + // context props + t: (string) => string } class ChangesetButtonGroup extends React.Component<Props> { render() { - const { repository, changeset } = this.props; + const { repository, changeset, t } = this.props; const changesetLink = createChangesetLink(repository, changeset); const sourcesLink = createSourcesLink(repository, changeset); @@ -22,18 +26,18 @@ class ChangesetButtonGroup extends React.Component<Props> { <ButtonGroup className="is-pulled-right"> <Button link={changesetLink}> <span className="icon"> - <i className="fas fa-code"></i> + <i className="fas fa-code-branch"></i> </span> <span className="is-hidden-mobile is-hidden-tablet-only"> - Details + {t("changesets.buttons.details")} </span> </Button> <Button link={sourcesLink}> <span className="icon"> - <i className="fas fa-history"></i> + <i className="fas fa-code"></i> </span> <span className="is-hidden-mobile is-hidden-tablet-only"> - Sources + {t("changesets.buttons.sources")} </span> </Button> </ButtonGroup> @@ -42,4 +46,4 @@ class ChangesetButtonGroup extends React.Component<Props> { } -export default ChangesetButtonGroup; +export default translate("repos")(ChangesetButtonGroup); diff --git a/scm-ui-components/packages/ui-components/src/repos/changesets/index.js b/scm-ui-components/packages/ui-components/src/repos/changesets/index.js index cede55ceca..7a07ad8f42 100644 --- a/scm-ui-components/packages/ui-components/src/repos/changesets/index.js +++ b/scm-ui-components/packages/ui-components/src/repos/changesets/index.js @@ -11,4 +11,3 @@ export { default as ChangesetRow } from "./ChangesetRow"; export { default as ChangesetTag } from "./ChangesetTag"; export { default as ChangesetTags } from "./ChangesetTags"; export { default as ChangesetTagsCollapsed } from "./ChangesetTagsCollapsed"; -export { default as ChangesetDiff } from "./ChangesetDiff"; diff --git a/scm-ui/public/locales/de/repos.json b/scm-ui/public/locales/de/repos.json index 2b6fe66bb5..13c302aa67 100644 --- a/scm-ui/public/locales/de/repos.json +++ b/scm-ui/public/locales/de/repos.json @@ -84,7 +84,11 @@ "name": "Autor", "mail": "Mail", "prefix": "Verfasst von", - "mailto": "E-Mail senden an" + "mailto": "Mail senden an" + }, + "buttons": { + "details": "Details", + "sources": "Sources" } }, "branch-selector": { diff --git a/scm-ui/public/locales/en/repos.json b/scm-ui/public/locales/en/repos.json index c829db54ad..94e6dd7fdb 100644 --- a/scm-ui/public/locales/en/repos.json +++ b/scm-ui/public/locales/en/repos.json @@ -85,6 +85,10 @@ "mail": "Mail", "prefix": "Authored by", "mailto": "Send mail to" + }, + "buttons": { + "details": "Details", + "sources": "Sources" } }, "branch-selector": { From fbaa5041a71b85e4cb682e674f5f00898eed7969 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra <sebastian.sdorra@cloudogu.com> Date: Thu, 7 Feb 2019 10:43:16 +0100 Subject: [PATCH 113/120] added spacing between tags on changeset detail page --- .../src/repos/components/changesets/ChangesetDetails.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/scm-ui/src/repos/components/changesets/ChangesetDetails.js b/scm-ui/src/repos/components/changesets/ChangesetDetails.js index 217b236122..fab976b797 100644 --- a/scm-ui/src/repos/components/changesets/ChangesetDetails.js +++ b/scm-ui/src/repos/components/changesets/ChangesetDetails.js @@ -22,6 +22,11 @@ import { ExtensionPoint } from "@scm-manager/ui-extensions"; const styles = { spacing: { marginRight: "1em" + }, + tags: { + "& .tag": { + marginLeft: ".25rem" + } } }; @@ -106,10 +111,11 @@ class ChangesetDetails extends React.Component<Props> { }; renderTags = () => { + const { classes } = this.props; const tags = this.getTags(); if (tags.length > 0) { return ( - <div className="level-item"> + <div className={classNames("level-item", classes.tags)}> {tags.map((tag: Tag) => { return <ChangesetTag key={tag.name} tag={tag} />; })} From 8a53730d03272bc4f450ee91f74437526578fc06 Mon Sep 17 00:00:00 2001 From: Mohamed Karray <mohamed.karray.sr@gmail.com> Date: Thu, 7 Feb 2019 11:00:02 +0100 Subject: [PATCH 114/120] enable login button --- scm-ui/src/containers/Login.js | 1 - 1 file changed, 1 deletion(-) diff --git a/scm-ui/src/containers/Login.js b/scm-ui/src/containers/Login.js index e8c5352d58..3ee9b141ef 100644 --- a/scm-ui/src/containers/Login.js +++ b/scm-ui/src/containers/Login.js @@ -143,7 +143,6 @@ class Login extends React.Component<Props, State> { /> <SubmitButton label={t("login.submit")} - disabled={this.isInValid()} fullWidth={true} loading={loading} /> From 19e06cc4396b74f11e2aad74ff6f7094ab55b88b Mon Sep 17 00:00:00 2001 From: Mohamed Karray <mohamed.karray.sr@gmail.com> Date: Thu, 7 Feb 2019 10:02:51 +0000 Subject: [PATCH 115/120] Close branch bugfix/activate_login_button From f0c2cbb915b8d0684aec740fff63cab0ea1a0b04 Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Thu, 7 Feb 2019 11:09:26 +0100 Subject: [PATCH 116/120] added subtitle to edit permissions page --- scm-ui/public/locales/de/repos.json | 1 + scm-ui/public/locales/en/repos.json | 1 + scm-ui/src/repos/permissions/containers/Permissions.js | 2 ++ 3 files changed, 4 insertions(+) diff --git a/scm-ui/public/locales/de/repos.json b/scm-ui/public/locales/de/repos.json index fcb073d151..f10157b4de 100644 --- a/scm-ui/public/locales/de/repos.json +++ b/scm-ui/public/locales/de/repos.json @@ -80,6 +80,7 @@ } }, "permission": { + "title": "Berechtigungen bearbeiten", "user": "Benutzer", "group": "Gruppe", "error-title": "Fehler", diff --git a/scm-ui/public/locales/en/repos.json b/scm-ui/public/locales/en/repos.json index 4aabcf6c72..c30eade1a9 100644 --- a/scm-ui/public/locales/en/repos.json +++ b/scm-ui/public/locales/en/repos.json @@ -80,6 +80,7 @@ } }, "permission": { + "title": "Edit Permissions", "user": "User", "group": "Group", "error-title": "Error", diff --git a/scm-ui/src/repos/permissions/containers/Permissions.js b/scm-ui/src/repos/permissions/containers/Permissions.js index 7c20f503c5..38afea441b 100644 --- a/scm-ui/src/repos/permissions/containers/Permissions.js +++ b/scm-ui/src/repos/permissions/containers/Permissions.js @@ -24,6 +24,7 @@ import { import { Loading, ErrorPage, + Subtitle, LabelWithHelpIcon } from "@scm-manager/ui-components"; import type { @@ -143,6 +144,7 @@ class Permissions extends React.Component<Props> { return ( <div> + <Subtitle subtitle={t("permission.title")} /> <table className="has-background-light table is-hoverable is-fullwidth"> <thead> <tr> From 589926348a7c2a5834d6ab591699d84211ffab2b Mon Sep 17 00:00:00 2001 From: Philipp Czora <philipp.czora@cloudogu.com> Date: Thu, 7 Feb 2019 10:25:03 +0000 Subject: [PATCH 117/120] Close branch feature/add_validation From ecfe0979435691c8310cb43eaf02e0810ff01bd1 Mon Sep 17 00:00:00 2001 From: Philipp Czora <philipp.czora@cloudogu.com> Date: Thu, 7 Feb 2019 11:04:13 +0000 Subject: [PATCH 118/120] Close branch feature/changeset_committer_extension_point From 9fae4c7d46b03c1d62551c62c5278fb6c0ef25b1 Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Thu, 7 Feb 2019 13:53:15 +0100 Subject: [PATCH 119/120] added modal footer --- .../ui-components/src/modals/ConfirmAlert.js | 37 +++++++++++-------- .../ui-components/src/modals/Modal.js | 28 ++++++-------- scm-ui/styles/scm.scss | 7 ++++ 3 files changed, 40 insertions(+), 32 deletions(-) diff --git a/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.js b/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.js index 4192411eb7..6fe08c8ce8 100644 --- a/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.js +++ b/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.js @@ -29,33 +29,40 @@ class ConfirmAlert extends React.Component<Props> { render() { const { title, message, buttons } = this.props; - const body= ( - <> - {message} - <div className="buttons is-right"> - {buttons.map((button, i) => ( - <a className="button is-info is-right" - key={i} - onClick={() => this.handleClickButton(button)} + const body = <>{message}</>; + + const footer = ( + <div className="field is-grouped"> + {buttons.map((button, i) => ( + <p className="control"> + <a + className="button is-info" + key={i} + onClick={() => this.handleClickButton(button)} > {button.label} </a> - ))} - </div> - </> + </p> + ))} + </div> ); - return ( - <Modal title={title} closeFunction={() => this.close()} body={body} active={true}/> + <Modal + title={title} + closeFunction={() => this.close()} + body={body} + active={true} + footer={footer} + /> ); } } export function confirmAlert(properties: Props) { const root = document.getElementById("modalRoot"); - if(root){ - ReactDOM.render(<ConfirmAlert {...properties}/>, root); + if (root) { + ReactDOM.render(<ConfirmAlert {...properties} />, root); } } diff --git a/scm-ui-components/packages/ui-components/src/modals/Modal.js b/scm-ui-components/packages/ui-components/src/modals/Modal.js index febcbcd79d..58da89381d 100644 --- a/scm-ui-components/packages/ui-components/src/modals/Modal.js +++ b/scm-ui-components/packages/ui-components/src/modals/Modal.js @@ -7,6 +7,7 @@ type Props = { title: string, closeFunction: () => void, body: any, + footer?: any, active: boolean, classes: any }; @@ -19,42 +20,35 @@ const styles = { } }; - - class Modal extends React.Component<Props> { - render() { - const { title, closeFunction, body, active, classes } = this.props; + const { title, closeFunction, body, footer, active, classes } = this.props; const isActive = active ? "is-active" : null; + let showFooter = null; + if (footer) { + showFooter = <footer className="modal-card-foot">{footer}</footer>; + } + return ( - <div className={classNames( - "modal", - isActive - )}> + <div className={classNames("modal", isActive)}> <div className="modal-background" /> <div className={classNames("modal-card", classes.resize)}> - <header className="modal-card-head"> - <p className="modal-card-title"> - {title} - </p> + <p className="modal-card-title">{title}</p> <button className="delete" aria-label="close" onClick={closeFunction} /> </header> - <section className="modal-card-body"> - {body} - </section> - + <section className="modal-card-body">{body}</section> + {showFooter} </div> </div> ); } } - export default injectSheet(styles)(Modal); diff --git a/scm-ui/styles/scm.scss b/scm-ui/styles/scm.scss index be708d1bea..ce9e164482 100644 --- a/scm-ui/styles/scm.scss +++ b/scm-ui/styles/scm.scss @@ -314,3 +314,10 @@ $fa-font-path: "webfonts"; border-bottom: 1px solid #eee; } } + +// modal +.modal { + .modal-card-foot { + justify-content: flex-end; // pulled-right + } +} From 5a8e3fcc53e6c041973cecf9b4742d16273d4cbf Mon Sep 17 00:00:00 2001 From: Florian Scholdei <florian.scholdei@cloudogu.com> Date: Thu, 7 Feb 2019 16:17:14 +0000 Subject: [PATCH 120/120] Close branch feature/using_same_modal_classes_for_modals