From 68302fef926e912b528f47b0453271c47200cba3 Mon Sep 17 00:00:00 2001 From: Florian Scholdei Date: Fri, 18 Jan 2019 09:42:47 +0100 Subject: [PATCH 01/90] 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 02/90] 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 03/90] 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 04/90] 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 05/90] 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 06/90] 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 07/90] 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 08/90] 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 09/90] 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 10/90] 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 11/90] 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 12/90] 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 13/90] 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 14/90] 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 15/90] 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 16/90] 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 17/90] 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 18/90] 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 19/90] 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 20/90] 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 21/90] 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 22/90] 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 23/90] 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 24/90] 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 25/90] 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 26/90] 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 27/90] 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 28/90] 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 29/90] 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 30/90] 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 31/90] 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 32/90] 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 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 33/90] 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 34/90] 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 35/90] 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 36/90] =?UTF-8?q?replaced=20self-hacked=E2=84=A2=20solutio?= =?UTF-8?q?n=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 37/90] 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 38/90] 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 39/90] 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 40/90] 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 41/90] 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 42/90] 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 43/90] 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 44/90] 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 45/90] 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 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 46/90] 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 47/90] 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 48/90] 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 49/90] 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 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 50/90] 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 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 51/90] 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 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 52/90] 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 53/90] 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 54/90] 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 55/90] 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 56/90] 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 57/90] 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 58/90] 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 59/90] 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 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 60/90] 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 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 61/90] 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 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 62/90] 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 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 63/90] 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 64/90] 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 65/90] 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 66/90] 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 67/90] 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 68/90] 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 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 69/90] 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 70/90] 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 71/90] 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 72/90] 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 73/90] 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 74/90] 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 75/90] 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 76/90] 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 77/90] 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 78/90] 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 79/90] 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 80/90] 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 81/90] 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 82/90] 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 83/90] 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 84/90] 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 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 85/90] 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 86/90] 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 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 87/90] 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 88/90] 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 89/90] 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 90/90] Close branch feature/add_validation