merge with 2.0.0-m3

This commit is contained in:
Sebastian Sdorra
2019-06-25 09:53:44 +02:00
45 changed files with 246 additions and 159 deletions

View File

@@ -136,7 +136,7 @@ class Configuration extends React.Component<Props, State> {
className="delete"
onClick={() => this.setState({ configChanged: false })}
/>
{this.props.t("config-form.submit-success-notification")}
{this.props.t("config.form.submit-success-notification")}
</div>
);
}
@@ -167,7 +167,7 @@ class Configuration extends React.Component<Props, State> {
{this.props.render(renderProps)}
<hr />
<SubmitButton
label={t("config-form.submit")}
label={t("config.form.submit")}
disabled={!valid || readOnly}
loading={modifying}
/>

View File

@@ -29,20 +29,20 @@ class ConfigurationBinder {
// create NavigationLink with translated label
const ConfigNavLink = translate(this.i18nNamespace)(({t}) => {
return this.navLink("/config" + to, labelI18nKey, t);
return this.navLink("/admin/settings" + to, labelI18nKey, t);
});
// bind navigation link to extension point
binder.bind("config.navigation", ConfigNavLink, configPredicate);
binder.bind("admin.setting", ConfigNavLink, configPredicate);
// route for global configuration, passes the link from the index resource to component
const ConfigRoute = ({ url, links, ...additionalProps }) => {
const link = links[linkName].href;
return this.route(url + to, <ConfigurationComponent link={link} {...additionalProps} />);
return this.route(url + "/settings" + to, <ConfigurationComponent link={link} {...additionalProps} />);
};
// bind config route to extension point
binder.bind("config.route", ConfigRoute, configPredicate);
binder.bind("admin.route", ConfigRoute, configPredicate);
}
bindRepository(to: string, labelI18nKey: string, linkName: string, RepositoryComponent: any) {

View File

@@ -75,7 +75,7 @@ class PrimaryNavigation extends React.Component<Props> {
"primary-navigation.groups",
"groups"
);
append("/config", "/config", "primary-navigation.config", "config");
append("/admin", "/admin", "primary-navigation.admin", "config");
navigationItems.push(
<ExtensionPoint

View File

@@ -0,0 +1,46 @@
{
"admin": {
"menu": {
"navigationLabel": "Administrations Navigation",
"informationNavLink": "Informationen",
"settingsNavLink": "Einstellungen",
"generalNavLink": "Generell"
},
"information": {
"currentAppVersion": "Aktuelle Software-Versionsnummer"
}
},
"repositoryRole": {
"navLink": "Berechtigungsrollen",
"title": "Berechtigungsrollen",
"errorTitle": "Fehler",
"errorSubtitle": "Unbekannter Berechtigungsrollen Fehler",
"createSubtitle": "Berechtigungsrolle erstellen",
"editSubtitle": "Berechtigungsrolle bearbeiten",
"overview": {
"title": "Übersicht aller verfügbaren Berechtigungsrollen",
"noPermissionRoles": "Keine Berechtigungsrollen gefunden.",
"createButton": "Berechtigungsrolle erstellen"
},
"editButton": "Bearbeiten",
"name": "Name",
"type": "Typ",
"verbs": "Berechtigungen",
"system": "System",
"form": {
"name": "Name",
"permissions": "Berechtigungen",
"submit": "Speichern"
},
"delete": {
"button": "Delete",
"subtitle": "Delete Permission Role",
"confirmAlert": {
"title": "Delete Permission Role",
"message": "Do you really want to delete this permission role? All users who own this role will lose their permissions.",
"submit": "Yes",
"cancel": "No"
}
}
}
}

View File

@@ -37,7 +37,7 @@
"users": "Benutzer",
"logout": "Abmelden",
"groups": "Gruppen",
"config": "Einstellungen"
"admin": "Administration"
},
"filterEntries": "Einträge filtern",
"paginator": {

View File

@@ -1,49 +1,15 @@
{
"config": {
"navigationLabel": "Einstellungs Navigation",
"globalConfigurationNavLink": "Globale Einstellungen",
"title": "Einstellungen",
"navigationLabel": "Administrations Navigation",
"title": "Globale Einstellungen",
"errorTitle": "Fehler",
"errorSubtitle": "Unbekannter Einstellungen Fehler"
},
"repositoryRole": {
"navLink": "Berechtigungsrollen",
"title": "Berechtigungsrollen",
"errorTitle": "Fehler",
"errorSubtitle": "Unbekannter Berechtigungsrollen Fehler",
"createSubtitle": "Berechtigungsrolle erstellen",
"editSubtitle": "Berechtigungsrolle bearbeiten",
"overview": {
"title": "Übersicht aller verfügbaren Berechtigungsrollen",
"noPermissionRoles": "Keine Berechtigungsrollen gefunden.",
"createButton": "Berechtigungsrolle erstellen"
},
"editButton": "Bearbeiten",
"name": "Name",
"type": "Typ",
"verbs": "Berechtigungen",
"system": "System",
"errorSubtitle": "Unbekannter Einstellungen Fehler",
"form": {
"name": "Name",
"permissions": "Berechtigungen",
"submit": "Speichern"
}
},
"deleteRole" : {
"button": "Löschen",
"subtitle": "Berechtigungsrolle löschen",
"confirmAlert": {
"title": "Berechtigungsrolle löschen",
"message": "Soll die Berechtigungsrolle wirklich gelöscht werden? Alle Nutzer dieser Rolle verlieren Ihre Berechtigungen.",
"submit": "Ja",
"cancel": "Nein"
}
},
"config-form": {
"submit": "Speichern",
"submit-success-notification": "Einstellungen wurden erfolgreich geändert!",
"no-read-permission-notification": "Hinweis: Es fehlen Berechtigungen zum Lesen der Einstellungen!",
"no-write-permission-notification": "Hinweis: Es fehlen Berechtigungen zum Bearbeiten der Einstellungen!"
}
},
"proxy-settings": {
"name": "Proxy Einstellungen",

View File

@@ -0,0 +1,46 @@
{
"admin": {
"menu": {
"navigationLabel": "Administration Navigation",
"informationNavLink": "Information",
"settingsNavLink": "Settings",
"generalNavLink": "General"
},
"information": {
"currentAppVersion": "Current Application Version"
}
},
"repositoryRole": {
"navLink": "Permission Roles",
"title": "Permission Roles",
"errorTitle": "Error",
"errorSubtitle": "Unknown Permission Role Error",
"createSubtitle": "Create Permission Role",
"editSubtitle": "Edit Permission Role",
"overview": {
"title": "Overview of all permission roles",
"noPermissionRoles": "No permission roles found.",
"createButton": "Create Permission Role"
},
"editButton": "Edit",
"name": "Name",
"type": "Type",
"verbs": "Permissions",
"system": "System",
"form": {
"name": "Name",
"permissions": "Permissions",
"submit": "Save"
},
"delete" : {
"button": "Löschen",
"subtitle": "Berechtigungsrolle löschen",
"confirmAlert": {
"title": "Berechtigungsrolle löschen",
"message": "Soll die Berechtigungsrolle wirklich gelöscht werden? Alle Nutzer dieser Rolle verlieren Ihre Berechtigungen.",
"submit": "Ja",
"cancel": "Nein"
}
}
}
}

View File

@@ -37,7 +37,7 @@
"users": "Users",
"logout": "Logout",
"groups": "Groups",
"config": "Configuration"
"admin": "Administration"
},
"filterEntries": "filter entries",
"paginator": {

View File

@@ -1,49 +1,15 @@
{
"config": {
"navigationLabel": "Configuration Navigation",
"globalConfigurationNavLink": "Global Configuration",
"title": "Configuration",
"navigationLabel": "Administration Navigation",
"title": "Global Configuration",
"errorTitle": "Error",
"errorSubtitle": "Unknown Config Error"
},
"repositoryRole": {
"navLink": "Permission Roles",
"title": "Permission Roles",
"errorTitle": "Error",
"errorSubtitle": "Unknown Permission Role Error",
"createSubtitle": "Create Permission Role",
"editSubtitle": "Edit Permission Role",
"overview": {
"title": "Overview of all permission roles",
"noPermissionRoles": "No permission roles found.",
"createButton": "Create Permission Role"
},
"editButton": "Edit",
"name": "Name",
"type": "Type",
"verbs": "Permissions",
"system": "System",
"errorSubtitle": "Unknown Config Error",
"form": {
"name": "Name",
"permissions": "Permissions",
"submit": "Save"
}
},
"deleteRole": {
"button": "Delete",
"subtitle": "Delete Permission Role",
"confirmAlert": {
"title": "Delete Permission Role",
"message": "Do you really want to delete this permission role? All users who own this role will lose their permissions.",
"submit": "Yes",
"cancel": "No"
}
},
"config-form": {
"submit": "Submit",
"submit-success-notification": "Configuration changed successfully!",
"no-read-permission-notification": "Please note: You do not have the permission to see the config!",
"no-write-permission-notification": "Please note: You do not have the permission to edit the config!"
}
},
"proxy-settings": {
"name": "Proxy Settings",

View File

@@ -99,7 +99,7 @@ class ConfigForm extends React.Component<Props, State> {
return (
<Notification
type={"danger"}
children={t("config-form.no-read-permission-notification")}
children={t("config.form.no-read-permission-notification")}
/>
);
}
@@ -108,7 +108,7 @@ class ConfigForm extends React.Component<Props, State> {
noPermissionNotification = (
<Notification
type={"info"}
children={t("config-form.no-write-permission-notification")}
children={t("config.form.no-write-permission-notification")}
onClose={() => this.onClose()}
/>
);
@@ -167,7 +167,7 @@ class ConfigForm extends React.Component<Props, State> {
<hr />
<SubmitButton
loading={loading}
label={t("config-form.submit")}
label={t("config.form.submit")}
disabled={
!configUpdatePermission || this.hasError() || !this.state.changed
}

View File

@@ -1,14 +1,15 @@
// @flow
import React from "react";
import { translate } from "react-i18next";
import { Route, Switch } from "react-router-dom";
import {Redirect, Route, Switch} from "react-router-dom";
import { ExtensionPoint } from "@scm-manager/ui-extensions";
import type { History } from "history";
import { connect } from "react-redux";
import { compose } from "redux";
import type { Links } from "@scm-manager/ui-types";
import { Page, Navigation, NavLink, Section } from "@scm-manager/ui-components";
import { Page, Navigation, NavLink, Section, SubNavigation } from "@scm-manager/ui-components";
import { getLinks } from "../../modules/indexResource";
import AdminDetails from "./AdminDetails";
import GlobalConfig from "./GlobalConfig";
import RepositoryRoles from "../roles/containers/RepositoryRoles";
import SingleRepositoryRole from "../roles/containers/SingleRepositoryRole";
@@ -23,11 +24,14 @@ type Props = {
history: History
};
class Config extends React.Component<Props> {
class Admin extends React.Component<Props> {
stripEndingSlash = (url: string) => {
if (url.endsWith("/")) {
if(url.includes("role")) {
return url.substring(0, url.length - 2);
}
return url.substring(0, url.length - 1);
}
return url;
};
@@ -55,7 +59,9 @@ class Config extends React.Component<Props> {
<div className="columns">
<div className="column is-three-quarters">
<Switch>
<Route path={url} exact component={GlobalConfig} />
<Redirect exact from={url} to={`${url}/info`} />
<Route path={`${url}/info`} exact component={AdminDetails} />
<Route path={`${url}/settings/general`} exact component={GlobalConfig} />
<Route
path={`${url}/role/:role`}
render={() => (
@@ -86,7 +92,7 @@ class Config extends React.Component<Props> {
)}
/>
<ExtensionPoint
name="config.route"
name="admin.route"
props={extensionProps}
renderAll={true}
/>
@@ -94,22 +100,38 @@ class Config extends React.Component<Props> {
</div>
<div className="column is-one-quarter">
<Navigation>
<Section label={t("config.navigationLabel")}>
<Section label={t("admin.menu.navigationLabel")}>
<NavLink
to={`${url}`}
label={t("config.globalConfigurationNavLink")}
to={`${url}/info`}
icon="fas fa-info-circle"
label={t("admin.menu.informationNavLink")}
/>
<NavLink
to={`${url}/roles/`}
icon="fas fa-user-shield"
label={t("repositoryRole.navLink")}
activeWhenMatch={this.matchesRoles}
activeOnlyWhenExact={false}
/>
<ExtensionPoint
name="config.navigation"
name="admin.navigation"
props={extensionProps}
renderAll={true}
/>
<SubNavigation
to={`${url}/settings/general`}
label={t("admin.menu.settingsNavLink")}
>
<NavLink
to={`${url}/settings/general`}
label={t("admin.menu.generalNavLink")}
/>
<ExtensionPoint
name="admin.setting"
props={extensionProps}
renderAll={true}
/>
</SubNavigation>
</Section>
</Navigation>
</div>
@@ -128,5 +150,5 @@ const mapStateToProps = (state: any) => {
export default compose(
connect(mapStateToProps),
translate("config")
)(Config);
translate("admin")
)(Admin);

View File

@@ -0,0 +1,41 @@
// @flow
import React from "react";
import { translate } from "react-i18next";
import {Loading, Subtitle} from "@scm-manager/ui-components";
import {getAppVersion} from "../../modules/indexResource";
import {connect} from "react-redux";
import Title from "@scm-manager/ui-components/src/layout/Title";
type Props = {
loading: boolean,
error: Error,
version: string,
// context objects
t: string => string
};
class AdminDetails extends React.Component<Props> {
render() {
const { t, loading } = this.props;
if (loading) {
return <Loading />;
}
return <>
<Title title={t("admin.information.currentAppVersion")}/>
<Subtitle subtitle={this.props.version}/>
</>;
}
}
const mapStateToProps = (state: any) => {
const version = getAppVersion(state);
return {
version
};
};
export default connect(mapStateToProps)(translate("admin")(AdminDetails));

View File

@@ -80,7 +80,7 @@ class GlobalConfig extends React.Component<Props, State> {
className="delete"
onClick={() => this.setState({ configChanged: false })}
/>
{this.props.t("config-form.submit-success-notification")}
{this.props.t("config.form.submit-success-notification")}
</div>
);
}

View File

@@ -1,5 +1,4 @@
// @flow
import * as types from "../../modules/types";
import type { Action, NamespaceStrategies } from "@scm-manager/ui-types";
import { apiClient } from "@scm-manager/ui-components";

View File

@@ -47,4 +47,4 @@ class PermissionRoleDetails extends React.Component<Props> {
}
}
export default translate("config")(PermissionRoleDetails);
export default translate("admin")(PermissionRoleDetails);

View File

@@ -35,4 +35,4 @@ class PermissionRoleDetailsTable extends React.Component<Props> {
}
}
export default translate("config")(PermissionRoleDetailsTable);
export default translate("admin")(PermissionRoleDetailsTable);

View File

@@ -34,4 +34,4 @@ class PermissionRoleTable extends React.Component<Props> {
}
}
export default translate("config")(PermissionRoleTable);
export default translate("admin")(PermissionRoleTable);

View File

@@ -34,4 +34,4 @@ class SystemRoleTag extends React.Component<Props> {
}
}
export default injectSheet(styles)(translate("config")(SystemRoleTag));
export default injectSheet(styles)(translate("admin")(SystemRoleTag));

View File

@@ -32,7 +32,7 @@ type Props = {
class CreateRepositoryRole extends React.Component<Props> {
repositoryRoleCreated = (role: RepositoryRole) => {
const { history } = this.props;
history.push("/config/role/" + role.name + "/info");
history.push("/admin/role/" + role.name + "/info");
};
createRepositoryRole = (role: RepositoryRole) => {
@@ -85,4 +85,4 @@ const mapDispatchToProps = dispatch => {
export default connect(
mapStateToProps,
mapDispatchToProps
)(translate("config")(CreateRepositoryRole));
)(translate("admin")(CreateRepositoryRole));

View File

@@ -35,7 +35,7 @@ class DeleteRepositoryRole extends React.Component<Props> {
};
roleDeleted = () => {
this.props.history.push("/config/roles/");
this.props.history.push("/admin/roles/");
};
deleteRole = () => {
@@ -45,15 +45,15 @@ class DeleteRepositoryRole extends React.Component<Props> {
confirmDelete = () => {
const { t } = this.props;
confirmAlert({
title: t("deleteRole.confirmAlert.title"),
message: t("deleteRole.confirmAlert.message"),
title: t("repositoryRole.delete.confirmAlert.title"),
message: t("repositoryRole.delete.confirmAlert.message"),
buttons: [
{
label: t("deleteRole.confirmAlert.submit"),
label: t("repositoryRole.delete.confirmAlert.submit"),
onClick: () => this.deleteRole()
},
{
label: t("deleteRole.confirmAlert.cancel"),
label: t("repositoryRole.delete.confirmAlert.cancel"),
onClick: () => null
}
]
@@ -74,12 +74,12 @@ class DeleteRepositoryRole extends React.Component<Props> {
return (
<>
<Subtitle subtitle={t("deleteRole.subtitle")} />
<Subtitle subtitle={t("repositoryRole.delete.subtitle")} />
<div className="columns">
<div className="column">
<ErrorNotification error={error} />
<DeleteButton
label={t("deleteRole.button")}
label={t("repositoryRole.delete.button")}
action={action}
loading={loading}
/>
@@ -110,4 +110,4 @@ const mapDispatchToProps = dispatch => {
export default connect(
mapStateToProps,
mapDispatchToProps
)(withRouter(translate("config")(DeleteRepositoryRole)));
)(withRouter(translate("admin")(DeleteRepositoryRole)));

View File

@@ -29,7 +29,7 @@ type Props = {
class EditRepositoryRole extends React.Component<Props> {
repositoryRoleUpdated = () => {
this.props.history.push("/config/roles/");
this.props.history.push("/admin/roles/");
};
updateRepositoryRole = (role: RepositoryRole) => {
@@ -78,4 +78,4 @@ const mapDispatchToProps = dispatch => {
export default connect(
mapStateToProps,
mapDispatchToProps
)(translate("config")(EditRepositoryRole));
)(translate("admin")(EditRepositoryRole));

View File

@@ -169,4 +169,4 @@ const mapDispatchToProps = dispatch => {
export default connect(
mapStateToProps,
mapDispatchToProps
)(translate("config")(RepositoryRoleForm));
)(translate("admin")(RepositoryRoleForm));

View File

@@ -151,5 +151,5 @@ export default withRouter(
connect(
mapStateToProps,
mapDispatchToProps
)(translate("config")(RepositoryRoles))
)(translate("admin")(RepositoryRoles))
);

View File

@@ -129,5 +129,5 @@ export default withRouter(
connect(
mapStateToProps,
mapDispatchToProps
)(translate("config")(SingleRepositoryRole))
)(translate("admin")(SingleRepositoryRole))
);

View File

@@ -21,7 +21,8 @@ import Groups from "../groups/containers/Groups";
import SingleGroup from "../groups/containers/SingleGroup";
import CreateGroup from "../groups/containers/CreateGroup";
import Config from "../config/containers/Config";
import Admin from "../admin/containers/Admin";
import Profile from "./Profile";
type Props = {
@@ -112,8 +113,8 @@ class Main extends React.Component<Props> {
authenticated={authenticated}
/>
<ProtectedRoute
path="/config"
component={Config}
path="/admin"
component={Admin}
authenticated={authenticated}
/>
<ProtectedRoute

View File

@@ -14,9 +14,9 @@ import auth from "./modules/auth";
import pending from "./modules/pending";
import failure from "./modules/failure";
import permissions from "./repos/permissions/modules/permissions";
import config from "./config/modules/config";
import roles from "./config/roles/modules/roles";
import namespaceStrategies from "./config/modules/namespaceStrategies";
import config from "./admin/modules/config";
import roles from "./admin/roles/modules/roles";
import namespaceStrategies from "./admin/modules/namespaceStrategies";
import indexResources from "./modules/indexResource";
import type { BrowserHistory } from "history/createBrowserHistory";

View File

@@ -73,6 +73,7 @@ export default function reducer(
case FETCH_INDEXRESOURCES_SUCCESS:
return {
...state,
version: action.payload.version,
links: action.payload._links
};
default:
@@ -107,6 +108,10 @@ export function getLinkCollection(state: Object, name: string): Link[] {
return [];
}
export function getAppVersion(state: Object) {
return state.indexResources.version;
}
export function getUiPluginsLink(state: Object) {
return getLink(state, "uiPlugins");
}

View File

@@ -22,7 +22,7 @@ import { getRepositoriesLink } from "../../modules/indexResource";
import {
fetchNamespaceStrategiesIfNeeded,
getFetchNamespaceStrategiesFailure, getNamespaceStrategies, isFetchNamespaceStrategiesPending
} from "../../config/modules/namespaceStrategies";
} from "../../admin/modules/namespaceStrategies";
type Props = {
repositoryTypes: RepositoryType[],

View File

@@ -30,7 +30,7 @@ final class CronExpression {
boolean shouldRun(ZonedDateTime time) {
ZonedDateTime now = ZonedDateTime.now(clock);
return time.isBefore(now);
return time.isBefore(now) || time.isEqual(now);
}
Optional<ZonedDateTime> calculateNextRun() {

View File

@@ -32,7 +32,7 @@ class CronTask implements Task, Runnable {
@Override
public synchronized void run() {
if (expression.shouldRun(nextRun)) {
if (hasNextRun() && expression.shouldRun(nextRun)) {
LOG.debug("execute task {}, because of matching expression {}", name, expression);
runnable.run();
Optional<ZonedDateTime> next = expression.calculateNextRun();
@@ -40,6 +40,7 @@ class CronTask implements Task, Runnable {
nextRun = next.get();
} else {
LOG.debug("cancel task {}, because expression {} has no next execution", name, expression);
nextRun = null;
cancel();
}
} else {

View File

@@ -53,7 +53,7 @@ class CronTaskTest {
@Test
void shouldCancelWithoutNextRun() {
ZonedDateTime time = ZonedDateTime.now();
when(expression.calculateNextRun()).thenAnswer(new FirstTimeAnswer(Optional.of(time), Optional.empty()));
when(expression.calculateNextRun()).thenReturn(Optional.of(time), Optional.empty());
when(expression.shouldRun(time)).thenReturn(true);
CronTask task = task();
@@ -64,32 +64,26 @@ class CronTaskTest {
verify(future).cancel(false);
}
@Test
void shouldNotRunAfterCancelHasBeenCalledIfRunIsCalledAgain() {
ZonedDateTime time = ZonedDateTime.now();
when(expression.calculateNextRun()).thenReturn(Optional.of(time), Optional.empty());
when(expression.shouldRun(time)).thenReturn(true);
CronTask task = task();
task.setFuture(future);
task.run();
task.run();
verify(future).cancel(false);
verify(runnable).run();
}
@Test
void shouldNotRun() {
task().run();
verify(runnable, never()).run();
}
private static class FirstTimeAnswer implements Answer<Object> {
private boolean first = true;
private final Object answer;
private final Object secondAnswer;
FirstTimeAnswer(Object answer, Object secondAnswer) {
this.answer = answer;
this.secondAnswer = secondAnswer;
}
@Override
public Object answer(InvocationOnMock invocation) {
if (first) {
first = false;
return answer;
}
return secondAnswer;
}
}
}